diff --git a/macros/tests.rs b/macros/tests.rs index 7f35c44..2203671 100644 --- a/macros/tests.rs +++ b/macros/tests.rs @@ -33,7 +33,6 @@ fn cargs_expanding() { assert_eq!(a, ["hello/path".to_string(), "a literal".to_string()]); } - #[test] fn cmd_smoketest() { let a = format!("{:?}", cmd!(ls)); diff --git a/src/args.rs b/src/args.rs index fbe915a..a6d06ef 100644 --- a/src/args.rs +++ b/src/args.rs @@ -171,7 +171,7 @@ impl Args { pub fn finish(&mut self) -> Result<()> { let mut x = true; let idx = self.idx; - while let Some(_) = self.peek_str() { + while self.peek_str().is_some() { x = false; self.advance_pos(); } @@ -240,7 +240,7 @@ impl Args { /// assert_eq!(&args.req::("filepath").unwrap(), "fst.txt"); /// assert_eq!(args.peek_str(), Some("24h")); /// ``` - pub fn peek_str<'a>(&'a mut self) -> Option<&'a str> { + pub fn peek_str(&mut self) -> Option<&str> { if self.idx >= self.seen.len() { self.seen.extend(self.incoming.next()); } diff --git a/src/cmd.rs b/src/cmd.rs index 2dd50ba..a462400 100644 --- a/src/cmd.rs +++ b/src/cmd.rs @@ -157,9 +157,9 @@ impl CommandExecute for Command { } /// Methods on [`Command`] which take `self`. -/// +/// /// This is useful with [`cargs!`](crate::prelude::cargs). -/// +/// /// # Example /// ```rust /// # use rust_script_ext::prelude::*; @@ -215,9 +215,10 @@ impl CommandBuilder for Command { } fn with_env(mut self, key: K, val: V) -> Self - where - K: AsRef, - V: AsRef { + where + K: AsRef, + V: AsRef, + { self.env(key, val); self } @@ -246,23 +247,26 @@ pub trait CommandString { impl CommandString for Command { fn cmd_str(&self) -> String { // note that the debug format is unstable and need careful testing/handling - // dbg!(self); - let x = format!("{self:?}"); - let prg = x - .split_once(' ') - .map(|x| x.0) - .unwrap_or(&x) - .trim_matches('"'); - // println!("{prg}"); - // let prg = x - // .split_once("program:") - // .expect("known format") - // .1 - // .split_once(",") - // .expect("known format") - // .0 - // .trim() - // .trim_matches('"'); + let x = format!("{self:#?}"); + // eprintln!("{x}"); + + let prg = if cfg!(windows) { + x.split_once(' ') + .map(|x| x.0) + .unwrap_or(&x) + .trim_matches('"') + } else { + x.split_once("program:") + .expect("known format") + .1 + .split_once(',') + .expect("known format") + .0 + .trim() + .trim_matches('"') + }; + + // eprintln!("{prg}"); self.get_args() .fold(prg.to_string(), |s, a| s + " " + &*a.to_string_lossy()) @@ -296,7 +300,6 @@ mod tests { } #[test] - #[cfg(unix)] fn cmd_execute() { let x = cmd!(ls).execute_str(Quiet).unwrap(); let mut x = x.trim().split('\n').collect::>(); @@ -309,7 +312,6 @@ mod tests { "Cargo.toml", "LICENSE", "README.md", - "local.rs", "macros", "src", "target", @@ -317,10 +319,23 @@ mod tests { ] ); - let x = cmd!(ls "foo").execute_str(Verbose).unwrap_err(); + let x = cmd!(ls: "foo").execute_str(Verbose).unwrap_err(); assert_snapshot!("execute-err", pretty_print_err(x)); - let x = cmd!(watcmd "foo").execute_str(Verbose).unwrap_err(); + let x = cmd!(watcmd: "foo").execute_str(Verbose).unwrap_err(); assert_snapshot!("unknown-cmd", pretty_print_err(x)); } + #[test] + fn cmd_naming_with_env() { + let x = cmd!(ls).with_env("YO", "zog").cmd_str(); + assert_eq!(&x, "ls"); + + let x = cmd!(ls: foo, bar).with_env("YO", "zog").cmd_str(); + assert_eq!(&x, "ls foo bar"); + + let x = cmd!(ls: foo, bar) + .with_envs([("YO", "zog"), ("JO", "bar")]) + .cmd_str(); + assert_eq!(&x, "ls foo bar"); + } } diff --git a/src/fs.rs b/src/fs.rs index 7feec87..60181be 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -18,7 +18,7 @@ impl File { /// Opens a file in write-only mode. /// /// This function will create a file if it does not exist, and will truncate it if it does. - /// + /// /// **If the parent directory does not exist, it will be created.** pub fn create(path: impl Into) -> Result { let path = path.into(); @@ -131,7 +131,7 @@ impl File { fn create_p_dir(path: &Path) { if let Some(p) = path.parent() { if let Err(e) = std::fs::create_dir_all(p) { - eprintln!("failed to create parent directory '{}'", p.display()); + eprintln!("failed to create parent directory '{}': {e}", p.display()); } } } diff --git a/src/lib.rs b/src/lib.rs index ce9dbfc..6c54bff 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,14 +6,14 @@ //! These patterns include file reading, argument parsing, error handling. //! //! # Argument Parsing -//! +//! //! A rudimentary argument parser is provided, simply call [`args`](args::args). //! //! The parsing is meant to be simple, tailored to script usage. For fully featured CLI apps, //! consider importing [`clap`](https://docs.rs/clap/latest/clap/index.html). //! //! # Error Handling -//! +//! //! Error handling uses the [`miette`] crate. //! A `Result` type alias is exposed, and [`IntoDiagnostic`](prelude::IntoDiagnostic) can be used //! to convert errors. @@ -31,24 +31,24 @@ //! //! Running commands is done through `std::process::Command`. //! There are a few helper traits and macros to assist in: -//! +//! //! 1. Building a `Command`, and //! 2. Executing a command. -//! +//! //! Building commands can leverage the [`cmd!`](crate::prelude::cmd) macro. //! This can be used to succintly build a command with arguments. -//! +//! //! ```rust //! # use rust_script_ext::prelude::*; //! let x = 1.0; //! let cmd = cmd!(./my-script.sh: foo/bar, --verbose, {x + 2.14}); //! assert_eq!(&cmd.cmd_str(), "./my-script.sh foo/bar --verbose 3.14"); //! ``` -//! +//! //! The [`CommandExecute`](crate::prelude::CommandExecute) trait provides some methods which //! can execute a command and automatically collect the output, along with providing verbose //! error messages if something fails. -//! +//! //! ```rust,no_run //! # use rust_script_ext::prelude::*; //! // Verbose means also print stdout/stderr to terminal as execution occurs @@ -56,7 +56,7 @@ //! ``` //! //! # Serialisation -//! +//! //! [`Serialize`](::serde::Serialize), [`Deserialize`](::serde::Deserialize), //! and [`DeserializeOwned`](::serde::de::DeserializeOwned) are all exposed. //! Because of some path quirks with re-exported proc-macros, all derived values need to be tagged @@ -74,23 +74,23 @@ //! ``` //! //! # Date and Time -//! +//! //! Date and time is handled by exposing the [`time`](::time) crate. //! For _duration_, [`humantime`](::humantime) is used, exposing its `Duration` directly. This is //! done for duration parsing similar to what is experienced in unix tools. -//! +//! //! # Number formatting -//! +//! //! [`numfmt::Formatter`] is exposed (as [`NumFmt`](prelude::NumFmt)) which can be used //! to format numbers in a nice way. The `numfmt` module documentation describes ways to //! build a formatter, along with the syntax for parsing a format string. -//! +//! //! # Progress reporting -//! +//! //! [`how-u-doin`](::howudoin) can be used to show progress of a longer running script. -//! +//! //! # Tabular printing -//! +//! //! Tables can be printed neatly with [`TablePrinter`](prelude::TablePrinter), which is just //! exposing [`comfy-table`](::comfy_table). #![warn(missing_docs)] @@ -122,7 +122,7 @@ pub mod prelude { pub use super::args::{args, Args}; pub use super::cmd::{ - CommandExecute, CommandString, CommandBuilder, + CommandBuilder, CommandExecute, CommandString, Output::{self, *}, }; @@ -149,23 +149,23 @@ pub mod prelude { // publically document cargs! and cmd! here /// Construct a `[String]` array from a list of arguments. - /// + /// /// This macro is primarily for use with [`cmd!`](cmd), but can also be independently /// used, a great location is [`Command::args`](std::process::Command::args). - /// + /// /// Arguments are delimited by commas, any text between delimiters is stringified and /// passed through. /// Arguments wrapped in braces (`{ ... }`) are treated as expressions to be evaluated. /// This effectively writes `{ ... }.to_string()`. - /// + /// /// ```plaintext /// arg1, arg2/foo, {expr} /// ``` - /// + /// /// # Example /// ```rust /// # use rust_script_ext::prelude::*; - /// + /// /// let x = "hello"; /// let c = cargs!(foo, bar/zog, {x}, {1 + 2}); /// assert_eq!(c, [ @@ -178,47 +178,47 @@ pub mod prelude { pub use ::macros::cargs; /// Helper to construct a [`Command`] with arguments. - /// + /// /// The macro uses the syntax: /// ```plaintext /// cmd: arg1, arg2 /// ``` - /// + /// /// That is, the command path, optionally followed by a colon (`:`) followed by one or /// more _comma delimited_ arguments. - /// + /// /// Note that `cmd!` defers to [`cargs!`](cargs) to parse the arguments. - /// + /// /// The macro is powerful enough to support raw path identifiers: /// ```rust /// # use rust_script_ext::prelude::*; /// let c = cmd!(ls); // no args /// assert_eq!(&c.cmd_str(), "ls"); - /// + /// /// let c = cmd!(ls: foo/bar, zog); /// assert_eq!(&c.cmd_str(), "ls foo/bar zog"); - /// + /// /// let c = cmd!(./local-script.sh: foo/bar, zog); /// assert_eq!(&c.cmd_str(), "./local-script.sh foo/bar zog"); /// ``` - /// + /// /// Literals are supported: /// ```rust /// # use rust_script_ext::prelude::*; /// let c = cmd!(ls: "foo bar", 1.23); /// assert_eq!(&c.cmd_str(), r#"ls "foo bar" 1.23"#); /// ``` - /// + /// /// Arguments wrapped in braces (`{ ... }`) are treated as expressions to be evaluated. /// This effectively writes `{ ... }.to_string()`. - /// + /// /// ```rust /// # use rust_script_ext::prelude::*; /// let h = "hello"; /// let c = cmd!(ls: {h}, {format!("world")}); /// assert_eq!(&c.cmd_str(), "ls hello world"); /// ``` - /// + /// /// [`Command`]: std::process::Command pub use ::macros::cmd; }