diff --git a/cli/src/utils.rs b/cli/src/utils.rs index 82f202a92b..f96d24a7ef 100644 --- a/cli/src/utils.rs +++ b/cli/src/utils.rs @@ -21,7 +21,7 @@ pub struct FileOrUrl { pub url: Option, /// The path to the encoded metadata file. #[clap(long, value_parser)] - pub file: Option, + pub file: Option, /// Specify the metadata version. /// /// - "latest": Use the latest stable version available. @@ -33,6 +33,54 @@ pub struct FileOrUrl { pub version: Option, } +impl FromStr for FileOrUrl { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + if let Ok(path) = PathOrStdIn::from_str(s) { + Ok(FileOrUrl { + url: None, + file: Some(path), + version: None, + }) + } else { + Url::parse(s) + .map_err(|_| "Parsing Path or Uri failed.") + .map(|uri| FileOrUrl { + url: Some(uri), + file: None, + version: None, + }) + } + } +} + +/// If `--path -` is provided, read bytes for metadata from stdin +const STDIN_PATH_NAME: &str = "-"; +#[derive(Debug, Clone)] +pub enum PathOrStdIn { + Path(PathBuf), + StdIn, +} + +impl FromStr for PathOrStdIn { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + let s = s.trim(); + if s == STDIN_PATH_NAME { + Ok(PathOrStdIn::StdIn) + } else { + let path = std::path::Path::new(s); + if path.exists() { + Ok(PathOrStdIn::Path(PathBuf::from(path))) + } else { + Err("Path does not exist.") + } + } + } +} + impl FileOrUrl { /// Fetch the metadata bytes. pub async fn fetch(&self) -> color_eyre::Result> { @@ -42,12 +90,20 @@ impl FileOrUrl { eyre::bail!("specify one of `--url` or `--file` but not both") } // Load from --file path - (Some(path), None, None) => { + (Some(PathOrStdIn::Path(path)), None, None) => { let mut file = fs::File::open(path)?; let mut bytes = Vec::new(); file.read_to_end(&mut bytes)?; Ok(bytes) } + (Some(PathOrStdIn::StdIn), None, None) => { + let res = std::io::stdin().bytes().collect::, _>>(); + + match res { + Ok(bytes) => Ok(bytes), + Err(err) => eyre::bail!("reading bytes from stdin (`--file -`) failed: {err}"), + } + } // Cannot load the metadata from the file and specify a version to fetch. (Some(_), None, Some(_)) => { // Note: we could provide the ability to convert between metadata versions @@ -88,25 +144,49 @@ pub fn with_indent(s: String, indent: usize) -> String { .join("\n") } -impl FromStr for FileOrUrl { - type Err = &'static str; +#[cfg(test)] +mod tests { + use crate::utils::{FileOrUrl, PathOrStdIn}; + use std::str::FromStr; - fn from_str(s: &str) -> Result { - let path = std::path::Path::new(s); - if path.exists() { + #[test] + fn parsing() { + assert!(matches!( + FileOrUrl::from_str("-"), Ok(FileOrUrl { url: None, - file: Some(PathBuf::from(s)), - version: None, + file: Some(PathOrStdIn::StdIn), + version: None }) - } else { - Url::parse(s) - .map_err(|_| "no path or uri could be crated") - .map(|uri| FileOrUrl { - url: Some(uri), - file: None, - version: None, - }) - } + ),); + + assert!(matches!( + FileOrUrl::from_str(" - "), + Ok(FileOrUrl { + url: None, + file: Some(PathOrStdIn::StdIn), + version: None + }) + ),); + + assert!(matches!( + FileOrUrl::from_str("./src/main.rs"), + Ok(FileOrUrl { + url: None, + file: Some(PathOrStdIn::Path(_)), + version: None + }) + ),); + + assert!(FileOrUrl::from_str("./src/i_dont_exist.rs").is_err()); + + assert!(matches!( + FileOrUrl::from_str("https://github.com/paritytech/subxt"), + Ok(FileOrUrl { + url: Some(_), + file: None, + version: None + }) + )); } }