diff --git a/.envrc b/.envrc
deleted file mode 100644
index 3550a30..0000000
--- a/.envrc
+++ /dev/null
@@ -1 +0,0 @@
-use flake
diff --git a/.gitignore b/.gitignore
index c0dc59d..4592497 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,11 @@
+# build
/target
/result
-.direnv
+# environments
.env*
!.env.example
+# direnv
+.envrc
+.direnv
diff --git a/Cargo.lock b/Cargo.lock
index 98951b4..f8a41c1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -197,6 +197,7 @@ name = "vnetod"
version = "0.3.3"
dependencies = [
"clap",
+ "termcolor",
]
[[package]]
diff --git a/Cargo.toml b/Cargo.toml
index fc6b11e..9ea72fe 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,6 +13,7 @@ categories = ["command-line-interface", "config", "development-tools"]
[dependencies]
clap = { version = "3.2.15", default-features = false, features = ["std", "env", "derive"] }
+termcolor = { version = "1.1.3", optional = true }
[features]
-color = ["clap/color"]
+color = ["clap/color", "dep:termcolor"]
diff --git a/flake.nix b/flake.nix
index 4ae4d21..e5847b7 100644
--- a/flake.nix
+++ b/flake.nix
@@ -11,22 +11,28 @@
cargoToml = fromTOML (readFile ./Cargo.toml);
version = "${cargoToml.package.version}+${substring 0 8 self.lastModifiedDate}.${self.shortRev or "dirty"}";
- mkVnetod = { lib, rustPlatform, ... }:
+ mkVnetod = { lib, rustPlatform, vnetodFeatures ? [ ], ... }:
rustPlatform.buildRustPackage {
name = "vnetod-${version}";
src = lib.cleanSource ./.;
cargoLock.lockFile = ./Cargo.lock;
+ buildFeatures = vnetodFeatures;
+
doCheck = true;
};
in
{
- overlays = rec {
- vnetod = final: prev: {
+ overlays = {
+ default = final: prev: {
vnetod = final.callPackage mkVnetod { };
};
- default = vnetod;
+ colored = final: prev: {
+ vnetod = final.callPackage mkVnetod {
+ vnetodFeatures = [ "color" ];
+ };
+ };
};
}
// flake-utils.lib.eachDefaultSystem (system:
@@ -34,6 +40,7 @@
pkgs = import nixpkgs { inherit system; };
vnetod = pkgs.callPackage mkVnetod { };
+ coloredVnetod = pkgs.callPackage mkVnetod { vnetodFeatures = [ "color" ]; };
docker = pkgs.dockerTools.buildLayeredImage {
name = "pleshevskiy/vnetod";
@@ -46,14 +53,21 @@
};
in
{
- apps.default = {
- type = "app";
- program = "${vnetod}/bin/vnetod";
+ apps = {
+ default = {
+ type = "app";
+ program = "${vnetod}/bin/vnetod";
+ };
+ colored = {
+ type = "app";
+ program = "${coloredVnetod}/bin/vnetod";
+ };
};
packages = {
inherit docker vnetod;
default = vnetod;
+ colored = coloredVnetod;
};
devShell = pkgs.mkShell {
diff --git a/src/cli/switch.rs b/src/cli/switch.rs
index d346425..27e8a13 100644
--- a/src/cli/switch.rs
+++ b/src/cli/switch.rs
@@ -14,21 +14,24 @@
//! along with vnetod. If not, see .
//!
+#[cfg(feature = "color")]
+use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
+
use crate::{cli::Args, domain};
use std::fs::File;
-use std::io::{stdout, Write};
+use std::io::Write;
#[derive(Debug)]
pub enum Error {
OpenFile,
- Switch(domain::switch::Error),
+ WriteFile,
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::OpenFile => f.write_str("Cannot open file"),
- Error::Switch(inner) => write!(f, "Cannot switch between states: {}", inner),
+ Error::WriteFile => f.write_str("Cannot write file"),
}
}
}
@@ -38,20 +41,39 @@ impl std::error::Error for Error {}
pub fn execute(args: &Args) -> Result<(), Error> {
let content = std::fs::read_to_string(&args.file).map_err(|_| Error::OpenFile)?;
- let writer: Box = if args.dry_run {
- 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)
+ .then(|| {
+ File::create(args.output.as_ref().unwrap_or(&args.file)).map_err(|_| Error::OpenFile)
+ })
+ .transpose()?;
- domain::switch::execute(domain::switch::Request {
+ let new_content = domain::switch::execute(domain::switch::Request {
content: &content,
- writer,
sections: &args.sections,
- on_line: None,
- })
- .map_err(Error::Switch)
+ on_line: Some(Box::new(print_line)),
+ });
+ 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) {
+ let mut stdout = StandardStream::stdout(ColorChoice::Always);
+ 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();
+}
+
+#[cfg(not(feature = "color"))]
+fn print_line(line: &String) {
+ print!("{}", line)
}
diff --git a/src/domain.rs b/src/domain.rs
index 88f0ab6..960684f 100644
--- a/src/domain.rs
+++ b/src/domain.rs
@@ -44,7 +44,7 @@ impl Section {
}
}
-pub(crate) struct SectionInfo {
+pub struct SectionInfo {
enable_variable: bool,
disable_variable: bool,
}
diff --git a/src/domain/switch.rs b/src/domain/switch.rs
index e499da4..01882e7 100644
--- a/src/domain/switch.rs
+++ b/src/domain/switch.rs
@@ -14,43 +14,17 @@
//! along with vnetod. If not, see .
//!
-use std::io::{BufWriter, Write};
-
use super::{Section, SectionInfo};
-#[derive(Debug)]
-pub enum Error {
- WriteData,
-}
+pub type OnLineFn = Box;
-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 type OnLineFn = Box, &String)>;
-
-pub struct Request<'args, W>
-where
- W: Write,
-{
+pub struct Request<'args> {
pub content: &'args str,
- pub writer: W,
pub sections: &'args [String],
pub on_line: Option,
}
-pub fn execute(req: Request) -> Result<(), Error>
-where
- W: Write,
-{
- let mut writer = BufWriter::new(req.writer);
-
+pub fn execute(req: Request) -> String {
let choose_sections = req
.sections
.iter()
@@ -58,6 +32,7 @@ where
.collect::>();
let mut current_section: Option = None;
+ let mut new_content = String::new();
for line in req.content.split_inclusive('\n') {
let new_line = if is_section_end(line) {
@@ -86,16 +61,13 @@ where
line.to_string()
};
- writer
- .write_all(new_line.as_bytes())
- .map_err(|_| Error::WriteData)?;
-
+ new_content.push_str(&new_line);
if let Some(on_line) = req.on_line.as_ref() {
- on_line(current_section.as_ref(), &new_line);
+ on_line(&new_line);
}
}
- writer.flush().map_err(|_| Error::WriteData)
+ new_content
}
fn is_variable(trimmed_line: &str) -> bool {
@@ -151,28 +123,19 @@ fn should_disable_variable(choose_sections: &[Section], current_sections: &[Sect
#[cfg(test)]
mod tests {
use super::*;
- use std::io::Cursor;
const BASE_ENV: &str = include_str!("../../test_data/base_env");
fn make_test(input: &str, expected_output: &str, sections: Vec<&str>) {
- let mut output_data = vec![];
- let writer = Cursor::new(&mut output_data);
-
- match execute(Request {
+ let output_data = execute(Request {
content: input,
- writer,
sections: §ions.into_iter().map(String::from).collect::>(),
- }) {
- Ok(()) => {
- let output = String::from_utf8(output_data).unwrap();
- assert_eq!(
- output.lines().collect::>(),
- expected_output.lines().collect::>()
- );
- }
- _ => unreachable!(),
- }
+ on_line: None,
+ });
+ assert_eq!(
+ output_data.lines().collect::>(),
+ expected_output.lines().collect::>()
+ );
}
#[test]