Compare commits
No commits in common. "main" and "v0.3.0" have entirely different histories.
15 changed files with 204 additions and 269 deletions
4
.dockerignore
Normal file
4
.dockerignore
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
/target
|
||||||
|
|
||||||
|
.env*
|
||||||
|
!.envrc
|
1
.envrc
Normal file
1
.envrc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
use flake
|
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -1,11 +1,4 @@
|
||||||
# build
|
|
||||||
/target
|
/target
|
||||||
/result
|
|
||||||
|
|
||||||
# environments
|
|
||||||
.env*
|
.env*
|
||||||
!.env.example
|
!.env.example
|
||||||
|
|
||||||
# direnv
|
|
||||||
.envrc
|
|
||||||
.direnv
|
|
||||||
|
|
11
Cargo.lock
generated
11
Cargo.lock
generated
|
@ -37,6 +37,7 @@ dependencies = [
|
||||||
"clap_lex",
|
"clap_lex",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"strsim",
|
||||||
"termcolor",
|
"termcolor",
|
||||||
"textwrap",
|
"textwrap",
|
||||||
]
|
]
|
||||||
|
@ -154,6 +155,12 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.98"
|
version = "1.0.98"
|
||||||
|
@ -194,11 +201,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vnetod"
|
name = "vnetod"
|
||||||
version = "0.4.0"
|
version = "0.2.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atty",
|
|
||||||
"clap",
|
"clap",
|
||||||
"termcolor",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
10
Cargo.toml
10
Cargo.toml
|
@ -1,10 +1,9 @@
|
||||||
[package]
|
[package]
|
||||||
name = "vnetod"
|
name = "vnetod"
|
||||||
description = "Are you still switching sections in your dotenv file manually? Try this dotenv section switcher"
|
description = "Dotenv state switcher"
|
||||||
version = "0.4.0"
|
version = "0.3.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "GPL-3.0+"
|
license = "GPL-3.0+"
|
||||||
homepage = "https://github.com/pleshevskiy/vnetod/discussions"
|
|
||||||
repository = "https://git.pleshevski.ru/pleshevskiy/vnetod.git"
|
repository = "https://git.pleshevski.ru/pleshevskiy/vnetod.git"
|
||||||
keywords = ["env", "dotenv", "switcher", "change"]
|
keywords = ["env", "dotenv", "switcher", "change"]
|
||||||
categories = ["command-line-interface", "config", "development-tools"]
|
categories = ["command-line-interface", "config", "development-tools"]
|
||||||
|
@ -12,9 +11,6 @@ categories = ["command-line-interface", "config", "development-tools"]
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "3.2.15", default-features = false, features = ["std", "env", "derive"] }
|
clap = { version = "3.2.15", features = ["derive"] }
|
||||||
atty = { version = "0.2.14", optional = true }
|
|
||||||
termcolor = { version = "1.1.3", optional = true }
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
color = ["clap/color", "dep:atty", "dep:termcolor"]
|
|
||||||
|
|
22
Dockerfile
Normal file
22
Dockerfile
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
FROM rust:1.62.0-slim-buster
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN cargo init .
|
||||||
|
|
||||||
|
COPY Cargo.* ./
|
||||||
|
|
||||||
|
RUN cargo build --release \
|
||||||
|
&& rm -rf src
|
||||||
|
|
||||||
|
COPY ./src ./src
|
||||||
|
|
||||||
|
RUN cargo install --bin vnetod --path . \
|
||||||
|
&& rm -rf ./src Cargo.*
|
||||||
|
|
||||||
|
VOLUME ["/data"]
|
||||||
|
|
||||||
|
WORKDIR /data
|
||||||
|
|
||||||
|
ENTRYPOINT ["/usr/local/cargo/bin/vnetod"]
|
||||||
|
|
26
README.md
26
README.md
|
@ -1,20 +1,17 @@
|
||||||
# vnetod*
|
# vnetod
|
||||||
|
|
||||||
<small><strong>*</strong> inverted word "dotenv"</small>
|
Dotenv state switcher
|
||||||
|
|
||||||
Are you still switching sections in your dotenv file manually? Try this dotenv
|
You can create many states in your `.env` and switch between them.
|
||||||
section switcher!
|
|
||||||
|
|
||||||
You can create many sections in your `.env` and switch between them.
|
|
||||||
|
|
||||||
Rules:
|
Rules:
|
||||||
|
|
||||||
- Section name starts on a new line with `###` symbols (Ex. `### local`)
|
- State name starts on a new line with `###` symbols (Ex. `### local`)
|
||||||
- Section can contain multiple comma-separated names (Ex.
|
- State name can contain multiple comma-separated sections (Ex.
|
||||||
`### local,staging`)
|
`### local,staging`)
|
||||||
- Each section name may specify a namespace (Ex. `### debug:on,dev:on`). If a
|
- Each section may specify a namespace (Ex. `### debug:on,dev:on`). If a section
|
||||||
section doesn't contain a namespace, it's a global namespace.
|
doesn't contain a namespace, it's a global namespace.
|
||||||
- Section ends if line is empty or contains a new section name.
|
- State ends if line is empty or contains a new state name.
|
||||||
|
|
||||||
You can see the [full example].
|
You can see the [full example].
|
||||||
|
|
||||||
|
@ -85,13 +82,6 @@ docker run --rm -it -v $PWD:/data pleshevskiy/vnetod --help
|
||||||
nix run git+https://git.pleshevski.ru/pleshevskiy/vnetod -- --help
|
nix run git+https://git.pleshevski.ru/pleshevskiy/vnetod -- --help
|
||||||
```
|
```
|
||||||
|
|
||||||
# Contact me
|
|
||||||
|
|
||||||
- [send feedback](https://github.com/pleshevskiy/vnetod/discussions)
|
|
||||||
- [make an issue](https://github.com/pleshevskiy/vnetod/issues)
|
|
||||||
- matrix: @pleshevskiy:matrix.org
|
|
||||||
- email: dmitriy@pleshevski.ru
|
|
||||||
|
|
||||||
# License
|
# License
|
||||||
|
|
||||||
GNU General Public License v3.0 or later
|
GNU General Public License v3.0 or later
|
||||||
|
|
|
@ -1,25 +1,11 @@
|
||||||
# Supported tags
|
# Supported tags
|
||||||
|
|
||||||
- latest
|
- latest
|
||||||
- 0.4
|
- 0.3
|
||||||
|
|
||||||
# vnetod*
|
# vnetod
|
||||||
|
|
||||||
<small><strong>*</strong> inverted word "dotenv"</small>
|
Dotenv state switcher
|
||||||
|
|
||||||
Are you still switching sections in your dotenv file manually? Try this dotenv
|
|
||||||
section switcher!
|
|
||||||
|
|
||||||
You can create many sections in your `.env` and switch between them.
|
|
||||||
|
|
||||||
Rules:
|
|
||||||
|
|
||||||
- Section name starts on a new line with `###` symbols (Ex. `### local`)
|
|
||||||
- Section can contain multiple comma-separated names (Ex.
|
|
||||||
`### local,staging`)
|
|
||||||
- Each section name may specify a namespace (Ex. `### debug:on,dev:on`). If a
|
|
||||||
section doesn't contain a namespace, it's a global namespace.
|
|
||||||
- Section ends if line is empty or contains a new section name.
|
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
|
|
||||||
|
|
54
flake.lock
54
flake.lock
|
@ -1,21 +1,39 @@
|
||||||
{
|
{
|
||||||
"nodes": {
|
"nodes": {
|
||||||
"flake-utils": {
|
"naersk": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1667395993,
|
"lastModified": 1655042882,
|
||||||
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
|
"narHash": "sha256-9BX8Fuez5YJlN7cdPO63InoyBy7dm3VlJkkmTt6fS1A=",
|
||||||
"owner": "numtide",
|
"owner": "nix-community",
|
||||||
"repo": "flake-utils",
|
"repo": "naersk",
|
||||||
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
|
"rev": "cddffb5aa211f50c4b8750adbec0bbbdfb26bb9f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "numtide",
|
"owner": "nix-community",
|
||||||
"repo": "flake-utils",
|
"ref": "master",
|
||||||
|
"repo": "naersk",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1659190188,
|
||||||
|
"narHash": "sha256-LudYrDFPFaQMW0l68TYkPWRPKmqpxIFU1nWfylIp9AQ=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "a3fddd46a7f3418d7e3940ded94701aba569161d",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"id": "nixpkgs",
|
||||||
|
"type": "indirect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1659190188,
|
"lastModified": 1659190188,
|
||||||
"narHash": "sha256-LudYrDFPFaQMW0l68TYkPWRPKmqpxIFU1nWfylIp9AQ=",
|
"narHash": "sha256-LudYrDFPFaQMW0l68TYkPWRPKmqpxIFU1nWfylIp9AQ=",
|
||||||
|
@ -33,8 +51,24 @@
|
||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-utils": "flake-utils",
|
"naersk": "naersk",
|
||||||
"nixpkgs": "nixpkgs"
|
"nixpkgs": "nixpkgs_2",
|
||||||
|
"utils": "utils"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"utils": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1656928814,
|
||||||
|
"narHash": "sha256-RIFfgBuKz6Hp89yRr7+NR5tzIAbn52h8vT6vXkYjZoM=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "7e2a3b3dfd9af950a856d66b0a7d01e3c18aa249",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
95
flake.nix
95
flake.nix
|
@ -1,77 +1,46 @@
|
||||||
{
|
{
|
||||||
inputs = {
|
inputs = {
|
||||||
|
naersk.url = "github:nix-community/naersk/master";
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
utils.url = "github:numtide/flake-utils";
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = { self, nixpkgs, flake-utils }:
|
outputs = { self, nixpkgs, utils, naersk }:
|
||||||
let
|
utils.lib.eachDefaultSystem (system:
|
||||||
inherit (builtins) fromTOML readFile substring;
|
|
||||||
|
|
||||||
cargoToml = fromTOML (readFile ./Cargo.toml);
|
|
||||||
version = "${cargoToml.package.version}+${substring 0 8 self.lastModifiedDate}.${self.shortRev or "dirty"}";
|
|
||||||
|
|
||||||
mkVnetod = { lib, rustPlatform, vnetodFeatures ? [ ], ... }:
|
|
||||||
rustPlatform.buildRustPackage {
|
|
||||||
name = "vnetod-${version}";
|
|
||||||
|
|
||||||
src = lib.cleanSource ./.;
|
|
||||||
cargoLock.lockFile = ./Cargo.lock;
|
|
||||||
|
|
||||||
buildFeatures = vnetodFeatures;
|
|
||||||
|
|
||||||
doCheck = true;
|
|
||||||
};
|
|
||||||
in
|
|
||||||
{
|
|
||||||
overlays = {
|
|
||||||
minimal = final: prev: {
|
|
||||||
vnetod = final.callPackage mkVnetod { };
|
|
||||||
};
|
|
||||||
default = final: prev: {
|
|
||||||
vnetod = final.callPackage mkVnetod {
|
|
||||||
vnetodFeatures = [ "color" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// flake-utils.lib.eachDefaultSystem (system:
|
|
||||||
let
|
let
|
||||||
|
name = "vnetod";
|
||||||
pkgs = import nixpkgs { inherit system; };
|
pkgs = import nixpkgs { inherit system; };
|
||||||
|
naersk-lib = pkgs.callPackage naersk { };
|
||||||
vnetod = pkgs.callPackage mkVnetod { vnetodFeatures = [ "color" ]; };
|
|
||||||
minimalVnetod = pkgs.callPackage mkVnetod { };
|
|
||||||
|
|
||||||
docker = pkgs.dockerTools.buildLayeredImage {
|
|
||||||
name = "pleshevskiy/vnetod";
|
|
||||||
tag = cargoToml.package.version;
|
|
||||||
config = {
|
|
||||||
Volumes."/data" = { };
|
|
||||||
WorkingDir = "/data";
|
|
||||||
Entrypoint = [ "${vnetod}/bin/vnetod" ];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
mkApp = prog: {
|
|
||||||
type = "app";
|
|
||||||
program = "${vnetod}/bin/vnetod";
|
|
||||||
};
|
|
||||||
in
|
in
|
||||||
{
|
rec {
|
||||||
apps = {
|
# Executes by `nix build .#<name>`
|
||||||
default = mkApp vnetod;
|
|
||||||
minimal = mkApp minimalVnetod;
|
|
||||||
};
|
|
||||||
|
|
||||||
packages = {
|
packages = {
|
||||||
inherit docker vnetod;
|
${name} = naersk.lib.${system}.buildPackage {
|
||||||
default = vnetod;
|
pname = name;
|
||||||
minimal = minimalVnetod;
|
root = ./.;
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
# Executes by `nix build .`
|
||||||
|
packages.default = packages.${name};
|
||||||
|
# the same but deprecated in Nix 2.7
|
||||||
|
defaultPackage = packages.default;
|
||||||
|
|
||||||
devShell = pkgs.mkShell {
|
# Executes by `nix run .#<name> -- <args?>`
|
||||||
packages = with pkgs; [ cargo rustc rustfmt clippy rust-analyzer ];
|
apps = {
|
||||||
RUST_SRC_PATH = pkgs.rustPlatform.rustLibSrc;
|
${name} = utils.lib.mkApp {
|
||||||
|
inherit name;
|
||||||
|
drv = packages.${name};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
# Executes by `nix run . -- <args?>`
|
||||||
|
apps.default = apps.${name};
|
||||||
|
# the same but deprecated in Nix 2.7
|
||||||
|
defaultApp = apps.default;
|
||||||
|
|
||||||
|
# Used by `nix develop`
|
||||||
|
devShell = with pkgs; mkShell {
|
||||||
|
buildInputs = [ cargo rustc rustfmt pre-commit rustPackages.clippy ];
|
||||||
|
RUST_SRC_PATH = rustPlatform.rustLibSrc;
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
72
src/cli.rs
72
src/cli.rs
|
@ -1,4 +1,4 @@
|
||||||
//! Copyright (C) 2022, Dmitriy Pleshevskiy <dmitriy@pleshevski.ru>
|
//! Copyright (C) 2022, Dmitriy Pleshevskiy <dmitriy@ideascup.me>
|
||||||
//!
|
//!
|
||||||
//! vnetod is free software: you can redistribute it and/or modify
|
//! vnetod is free software: you can redistribute it and/or modify
|
||||||
//! it under the terms of the GNU General Public License as published by
|
//! it under the terms of the GNU General Public License as published by
|
||||||
|
@ -20,14 +20,14 @@ use std::path::PathBuf;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser)]
|
||||||
#[clap(
|
#[clap(
|
||||||
author,
|
author,
|
||||||
version,
|
version,
|
||||||
about = "\
|
about = "\
|
||||||
Dotenv state switcher
|
Dotenv state switcher
|
||||||
---------------------------------------------------------------------
|
---------------------------------------------------------------------
|
||||||
vnetod Copyright (C) 2022 Dmitriy Pleshevskiy <dmitriy@pleshevski.ru>
|
vnetod Copyright (C) 2022 Dmitriy Pleshevskiy <dmitriy@ideascup.me>
|
||||||
This program comes with ABSOLUTELY NO WARRANTY;
|
This program comes with ABSOLUTELY NO WARRANTY;
|
||||||
This is free software, and you are welcome to redistribute it
|
This is free software, and you are welcome to redistribute it
|
||||||
under certain conditions;
|
under certain conditions;
|
||||||
|
@ -39,15 +39,14 @@ pub struct Args {
|
||||||
short = 'f',
|
short = 'f',
|
||||||
long,
|
long,
|
||||||
default_value = ".env",
|
default_value = ".env",
|
||||||
env = "VNETOD_FILE",
|
help = "Change source file with environment variables"
|
||||||
help = "Change source file with environment variables."
|
|
||||||
)]
|
)]
|
||||||
pub file: PathBuf,
|
pub file: PathBuf,
|
||||||
|
|
||||||
#[clap(
|
#[clap(
|
||||||
short = 'o',
|
short = 'o',
|
||||||
long,
|
long,
|
||||||
help = "Change output file with modified environment variables. It uses `file` argument by default if the output is not specified."
|
help = "Change output file with modified environment variables. It uses `file` argument by default if the output is not specified"
|
||||||
)]
|
)]
|
||||||
pub output: Option<PathBuf>,
|
pub output: Option<PathBuf>,
|
||||||
|
|
||||||
|
@ -59,66 +58,7 @@ pub struct Args {
|
||||||
|
|
||||||
#[clap(
|
#[clap(
|
||||||
value_parser,
|
value_parser,
|
||||||
help = "Environment varible sections that will be enabled."
|
help = "Environment varible sections that will be enabled"
|
||||||
)]
|
)]
|
||||||
pub sections: Vec<String>,
|
pub sections: Vec<String>,
|
||||||
|
|
||||||
#[cfg(feature = "color")]
|
|
||||||
#[clap(
|
|
||||||
long,
|
|
||||||
value_enum,
|
|
||||||
default_value = "auto",
|
|
||||||
help = "This flag controls when to use colors.",
|
|
||||||
long_help = "
|
|
||||||
This flag controls when to use colors. The default setting is 'auto', which
|
|
||||||
means vnetod will try to guess when to use colors. For example, if vnetod is
|
|
||||||
printing to a terminal, then it will use colors, but if it is redirected to a
|
|
||||||
file or a pipe, then it will suppress color output. vnetod will suppress color
|
|
||||||
output in some other circumstances as well. For example, if the TERM
|
|
||||||
environment variable is not set or set to 'dumb', then vnetod will not use
|
|
||||||
colors.
|
|
||||||
|
|
||||||
The possible values for this flag are:
|
|
||||||
|
|
||||||
never Colors will never be used.
|
|
||||||
auto The default. vnetod tries to be smart.
|
|
||||||
always Colors will always be used regardless of where output is sent.
|
|
||||||
ansi Like 'always', but emits ANSI escapes (even in a Windows console).
|
|
||||||
"
|
|
||||||
)]
|
|
||||||
pub color: ColorVariant,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "color")]
|
|
||||||
#[derive(clap::ValueEnum, Clone, Debug)]
|
|
||||||
pub enum ColorVariant {
|
|
||||||
Auto,
|
|
||||||
Always,
|
|
||||||
Ansi,
|
|
||||||
Never,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "color")]
|
|
||||||
impl From<ColorVariant> for termcolor::ColorChoice {
|
|
||||||
fn from(col: ColorVariant) -> Self {
|
|
||||||
match col {
|
|
||||||
ColorVariant::Never => Self::Never,
|
|
||||||
ColorVariant::Always => Self::Always,
|
|
||||||
ColorVariant::Ansi => Self::AlwaysAnsi,
|
|
||||||
ColorVariant::Auto => {
|
|
||||||
if atty::is(atty::Stream::Stdout) {
|
|
||||||
// Otherwise let's `termcolor` decide by inspecting the environment. From the [doc]:
|
|
||||||
// - If `NO_COLOR` is set to any value, then colors will be suppressed.
|
|
||||||
// - If `TERM` is set to dumb, then colors will be suppressed.
|
|
||||||
// - In non-Windows environments, if `TERM` is not set, then colors will be suppressed.
|
|
||||||
//
|
|
||||||
// [doc]: https://github.com/BurntSushi/termcolor#automatic-color-selection
|
|
||||||
Self::Auto
|
|
||||||
} else {
|
|
||||||
// Colors should be deactivated if the terminal is not a tty.
|
|
||||||
Self::Never
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
//! Copyright (C) 2022, Dmitriy Pleshevskiy <dmitriy@pleshevski.ru>
|
//! Copyright (C) 2022, Dmitriy Pleshevskiy <dmitriy@ideascup.me>
|
||||||
//!
|
//!
|
||||||
//! vnetod is free software: you can redistribute it and/or modify
|
//! vnetod is free software: you can redistribute it and/or modify
|
||||||
//! it under the terms of the GNU General Public License as published by
|
//! it under the terms of the GNU General Public License as published by
|
||||||
|
@ -14,24 +14,21 @@
|
||||||
//! along with vnetod. If not, see <https://www.gnu.org/licenses/>.
|
//! along with vnetod. If not, see <https://www.gnu.org/licenses/>.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
#[cfg(feature = "color")]
|
|
||||||
use termcolor::{Color, ColorSpec, StandardStream, WriteColor};
|
|
||||||
|
|
||||||
use crate::{cli::Args, domain};
|
use crate::{cli::Args, domain};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::{stdout, Write};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
OpenFile,
|
OpenFile,
|
||||||
WriteFile,
|
Switch(domain::switch::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for Error {
|
impl std::fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Error::OpenFile => f.write_str("Cannot open file"),
|
Error::OpenFile => f.write_str("Cannot open file"),
|
||||||
Error::WriteFile => f.write_str("Cannot write file"),
|
Error::Switch(inner) => write!(f, "Cannot switch between states: {}", inner),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,51 +38,19 @@ impl std::error::Error for Error {}
|
||||||
pub fn execute(args: &Args) -> Result<(), Error> {
|
pub fn execute(args: &Args) -> Result<(), Error> {
|
||||||
let content = std::fs::read_to_string(&args.file).map_err(|_| Error::OpenFile)?;
|
let content = std::fs::read_to_string(&args.file).map_err(|_| Error::OpenFile)?;
|
||||||
|
|
||||||
if args.dry_run {
|
let writer: Box<dyn Write> = if args.dry_run {
|
||||||
println!("Your file will be changed to the following")
|
Box::new(stdout())
|
||||||
}
|
} else {
|
||||||
|
Box::new(
|
||||||
|
File::create(args.output.as_ref().unwrap_or(&args.file))
|
||||||
|
.map_err(|_| Error::OpenFile)?,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
let fs_writer = (!args.dry_run)
|
domain::switch::execute(domain::switch::Request {
|
||||||
.then(|| {
|
content: &content,
|
||||||
File::create(args.output.as_ref().unwrap_or(&args.file)).map_err(|_| Error::OpenFile)
|
writer,
|
||||||
})
|
|
||||||
.transpose()?;
|
|
||||||
|
|
||||||
#[cfg(feature = "color")]
|
|
||||||
let color = args.color.clone();
|
|
||||||
|
|
||||||
println!();
|
|
||||||
|
|
||||||
let new_content = domain::switch::execute(domain::switch::Request {
|
|
||||||
content: &content.trim(),
|
|
||||||
sections: &args.sections,
|
sections: &args.sections,
|
||||||
on_line: Some(Box::new(move |line| {
|
})
|
||||||
#[cfg(feature = "color")]
|
.map_err(Error::Switch)
|
||||||
print_line(line, color.clone());
|
|
||||||
#[cfg(not(feature = "color"))]
|
|
||||||
print!("{}", line)
|
|
||||||
})),
|
|
||||||
});
|
|
||||||
|
|
||||||
println!();
|
|
||||||
|
|
||||||
if let Some(mut fs_writer) = fs_writer {
|
|
||||||
fs_writer
|
|
||||||
.write_all(new_content.as_bytes())
|
|
||||||
.map_err(|_| Error::WriteFile)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "color")]
|
|
||||||
fn print_line(line: &String, color: crate::cli::ColorVariant) {
|
|
||||||
let mut stdout = StandardStream::stdout(color.into());
|
|
||||||
let color = line
|
|
||||||
.starts_with("###")
|
|
||||||
.then_some(Color::Yellow)
|
|
||||||
.or_else(|| (!line.starts_with("#")).then_some(Color::Green));
|
|
||||||
stdout.set_color(ColorSpec::new().set_fg(color)).ok();
|
|
||||||
write!(&mut stdout, "{}", line).unwrap();
|
|
||||||
stdout.reset().ok();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
//! Copyright (C) 2022, Dmitriy Pleshevskiy <dmitriy@pleshevski.ru>
|
//! Copyright (C) 2022, Dmitriy Pleshevskiy <dmitriy@ideascup.me>
|
||||||
//!
|
//!
|
||||||
//! vnetod is free software: you can redistribute it and/or modify
|
//! vnetod is free software: you can redistribute it and/or modify
|
||||||
//! it under the terms of the GNU General Public License as published by
|
//! it under the terms of the GNU General Public License as published by
|
||||||
|
@ -44,7 +44,7 @@ impl Section {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SectionInfo {
|
struct SectionInfo {
|
||||||
enable_variable: bool,
|
enable_variable: bool,
|
||||||
disable_variable: bool,
|
disable_variable: bool,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
//! Copyright (C) 2022, Dmitriy Pleshevskiy <dmitriy@pleshevski.ru>
|
//! Copyright (C) 2022, Dmitriy Pleshevskiy <dmitriy@ideascup.me>
|
||||||
//!
|
//!
|
||||||
//! vnetod is free software: you can redistribute it and/or modify
|
//! vnetod is free software: you can redistribute it and/or modify
|
||||||
//! it under the terms of the GNU General Public License as published by
|
//! it under the terms of the GNU General Public License as published by
|
||||||
|
@ -14,32 +14,54 @@
|
||||||
//! along with vnetod. If not, see <https://www.gnu.org/licenses/>.
|
//! along with vnetod. If not, see <https://www.gnu.org/licenses/>.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
|
use std::io::{BufWriter, Write};
|
||||||
|
|
||||||
use super::{Section, SectionInfo};
|
use super::{Section, SectionInfo};
|
||||||
|
|
||||||
pub type OnLineFn = Box<dyn Fn(&String)>;
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
pub struct Request<'args> {
|
WriteData,
|
||||||
pub content: &'args str,
|
|
||||||
pub sections: &'args [String],
|
|
||||||
pub on_line: Option<OnLineFn>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute(req: Request) -> String {
|
impl std::fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Error::WriteData => f.write_str("Cannot write data"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for Error {}
|
||||||
|
|
||||||
|
pub struct Request<'args, W>
|
||||||
|
where
|
||||||
|
W: Write,
|
||||||
|
{
|
||||||
|
pub content: &'args str,
|
||||||
|
pub writer: W,
|
||||||
|
pub sections: &'args [String],
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn execute<W>(req: Request<W>) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
W: Write,
|
||||||
|
{
|
||||||
|
let mut writer = BufWriter::new(req.writer);
|
||||||
|
|
||||||
let choose_sections = req
|
let choose_sections = req
|
||||||
.sections
|
.sections
|
||||||
.iter()
|
.iter()
|
||||||
.map(|s| Section::parse(s.as_str()))
|
.map(|s| Section::parse(s.as_str()))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let mut current_section: Option<SectionInfo> = None;
|
let mut current_sections: Option<SectionInfo> = None;
|
||||||
let mut new_content = String::new();
|
|
||||||
|
|
||||||
for line in req.content.split_inclusive('\n') {
|
for line in req.content.split_inclusive('\n') {
|
||||||
let new_line = if is_section_end(line) {
|
let new_line = if is_section_end(line) {
|
||||||
current_section = None;
|
current_sections = None;
|
||||||
line.to_string()
|
line.to_string()
|
||||||
} else if let Some(section_info) = line.strip_prefix("### ") {
|
} else if let Some(section_info) = line.strip_prefix("### ") {
|
||||||
current_section = section_info.split_whitespace().next().map(|r| {
|
current_sections = section_info.split_whitespace().next().map(|r| {
|
||||||
let current_sections = r.split(',').map(Section::parse).collect::<Vec<_>>();
|
let current_sections = r.split(',').map(Section::parse).collect::<Vec<_>>();
|
||||||
SectionInfo {
|
SectionInfo {
|
||||||
enable_variable: should_enable_variable(&choose_sections, ¤t_sections),
|
enable_variable: should_enable_variable(&choose_sections, ¤t_sections),
|
||||||
|
@ -47,7 +69,7 @@ pub fn execute(req: Request) -> String {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
line.to_string()
|
line.to_string()
|
||||||
} else if let Some(section_info) = current_section.as_ref() {
|
} else if let Some(section_info) = current_sections.as_ref() {
|
||||||
let trimmed_line = line.trim_start_matches(['#', ' ']);
|
let trimmed_line = line.trim_start_matches(['#', ' ']);
|
||||||
let is_var = is_variable(trimmed_line);
|
let is_var = is_variable(trimmed_line);
|
||||||
if is_var && section_info.enable_variable {
|
if is_var && section_info.enable_variable {
|
||||||
|
@ -61,13 +83,12 @@ pub fn execute(req: Request) -> String {
|
||||||
line.to_string()
|
line.to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
new_content.push_str(&new_line);
|
writer
|
||||||
if let Some(on_line) = req.on_line.as_ref() {
|
.write_all(new_line.as_bytes())
|
||||||
on_line(&new_line);
|
.map_err(|_| Error::WriteData)?;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
new_content
|
writer.flush().map_err(|_| Error::WriteData)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_variable(trimmed_line: &str) -> bool {
|
fn is_variable(trimmed_line: &str) -> bool {
|
||||||
|
@ -123,20 +144,29 @@ fn should_disable_variable(choose_sections: &[Section], current_sections: &[Sect
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
const BASE_ENV: &str = include_str!("../../test_data/base_env");
|
const BASE_ENV: &str = include_str!("../../test_data/base_env");
|
||||||
|
|
||||||
fn make_test(input: &str, expected_output: &str, sections: Vec<&str>) {
|
fn make_test(input: &str, expected_output: &str, sections: Vec<&str>) {
|
||||||
let output_data = execute(Request {
|
let mut output_data = vec![];
|
||||||
|
let writer = Cursor::new(&mut output_data);
|
||||||
|
|
||||||
|
match execute(Request {
|
||||||
content: input,
|
content: input,
|
||||||
|
writer,
|
||||||
sections: §ions.into_iter().map(String::from).collect::<Vec<_>>(),
|
sections: §ions.into_iter().map(String::from).collect::<Vec<_>>(),
|
||||||
on_line: None,
|
}) {
|
||||||
});
|
Ok(()) => {
|
||||||
|
let output = String::from_utf8(output_data).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
output_data.lines().collect::<Vec<_>>(),
|
output.lines().collect::<Vec<_>>(),
|
||||||
expected_output.lines().collect::<Vec<_>>()
|
expected_output.lines().collect::<Vec<_>>()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_disable_all_sections() {
|
fn should_disable_all_sections() {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
//! Copyright (C) 2022, Dmitriy Pleshevskiy <dmitriy@pleshevski.ru>
|
//! Copyright (C) 2022, Dmitriy Pleshevskiy <dmitriy@ideascup.me>
|
||||||
//!
|
//!
|
||||||
//! vnetod is free software: you can redistribute it and/or modify
|
//! vnetod is free software: you can redistribute it and/or modify
|
||||||
//! it under the terms of the GNU General Public License as published by
|
//! it under the terms of the GNU General Public License as published by
|
||||||
|
|
Loading…
Reference in a new issue