diff --git a/rust/Cargo.lock b/rust/Cargo.lock index abc558c1..3cf66328 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1430,9 +1430,9 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lenient_semver" @@ -1982,7 +1982,7 @@ dependencies = [ "regex", "regex-syntax 0.6.29", "reqwest 0.12.5", - "rstest", + "rstest 0.19.0", "serde", "serde_json", "sxd-document", @@ -2032,7 +2032,7 @@ dependencies = [ "quickcheck", "rand", "reqwest 0.12.5", - "rstest", + "rstest 0.19.0", "semver", "serde", "serde_json", @@ -2110,7 +2110,7 @@ dependencies = [ "regex", "regex-syntax 0.6.29", "reqwest 0.11.27", - "rstest", + "rstest 0.19.0", "semver", "serde", "serde_json", @@ -2175,12 +2175,14 @@ dependencies = [ "env_logger 0.11.3", "expectest", "junit-report", + "lazy_static", "log", "maplit", "pact_models", "pact_verifier", "regex", "reqwest 0.12.5", + "rstest 0.21.0", "serde_json", "strip-ansi-escapes", "time", @@ -2792,7 +2794,19 @@ checksum = "9d5316d2a1479eeef1ea21e7f9ddc67c191d497abc8fc3ba2467857abbb68330" dependencies = [ "futures", "futures-timer", - "rstest_macros", + "rstest_macros 0.19.0", + "rustc_version", +] + +[[package]] +name = "rstest" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afd55a67069d6e434a95161415f5beeada95a01c7b815508a82dcb0e1593682" +dependencies = [ + "futures", + "futures-timer", + "rstest_macros 0.21.0", "rustc_version", ] @@ -2813,6 +2827,24 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "rstest_macros" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4165dfae59a39dd41d8dec720d3cbfbc71f69744efb480a3920f5d4e0cc6798d" +dependencies = [ + "cfg-if", + "glob", + "proc-macro-crate", + "proc-macro2 1.0.85", + "quote 1.0.36", + "regex", + "relative-path", + "rustc_version", + "syn 2.0.66", + "unicode-ident", +] + [[package]] name = "rustc-demangle" version = "0.1.24" diff --git a/rust/pact_verifier_cli/Cargo.toml b/rust/pact_verifier_cli/Cargo.toml index 01ee4a25..de398ec4 100644 --- a/rust/pact_verifier_cli/Cargo.toml +++ b/rust/pact_verifier_cli/Cargo.toml @@ -27,6 +27,7 @@ anyhow = "1.0.75" clap = { version = "4.5.4", features = ["cargo", "env"] } env_logger = "0.11.2" junit-report = { version = "0.8.3", optional = true } +lazy_static = "1.5.0" log = "0.4.20" maplit = "1.0.2" pact_models = { version = "~1.2.1", default-features = false } @@ -44,4 +45,5 @@ tracing-subscriber = { version = "0.3.18", features = ["env-filter", "tracing-lo [dev-dependencies] expectest = "0.12.0" +rstest = "0.21.0" trycmd = "0.15.0" diff --git a/rust/pact_verifier_cli/src/args.rs b/rust/pact_verifier_cli/src/args.rs index cb8769df..35b87a68 100644 --- a/rust/pact_verifier_cli/src/args.rs +++ b/rust/pact_verifier_cli/src/args.rs @@ -1,5 +1,6 @@ use clap::{Arg, ArgAction, ArgGroup, Command, command}; use clap::builder::{FalseyValueParser, NonEmptyStringValueParser, PossibleValuesParser}; +use lazy_static::lazy_static; use regex::Regex; fn port_value(v: &str) -> Result { @@ -20,14 +21,26 @@ fn validate_regex(val: &str) -> Result { } } -fn transport_value(v: &str) -> Result<(String, u16), String> { - let (transport, port) = v.split_once(':') - .ok_or_else(|| format!("'{}' is not a valid transport, it must be in the form TRANSPORT:PORT", v))?; - if transport.is_empty() { - return Err(format!("'{}' is not a valid transport, the transport part is empty", v)); +lazy_static! { + static ref TRANSPORT_VALUE_RE: Regex = Regex::new(r#"^(\w+):(\d+)(\/[^\s]*)?$"#).unwrap(); +} + +fn transport_value(v: &str) -> Result<(String, u16, Option), String> { + if let Some(result) = TRANSPORT_VALUE_RE.captures(v) { + let transport = if let Some(transport) = result.get(1) { + transport.as_str().to_string() + } else { + return Err(format!("'{}' is not a valid transport, the transport part is empty", v)); + }; + let port = if let Some(port) = result.get(2) { + port.as_str().parse::().unwrap() // Ok to unwrap, the regex will only allow digits + } else { + return Err(format!("'{}' is not a valid transport, the port part is empty", v)); + }; + Ok((transport, port, result.get(3).map(|v| v.as_str().to_string()))) + } else { + Err(format!("'{}' is not a valid transport, it must be in the form TRANSPORT:PORT[/path]", v)) } - port.parse::().map(|port| (transport.to_string(), port)) - .map_err(|e| format!("'{}' is not a valid port value: {}", port, e) ) } pub(crate) fn setup_app() -> Command { @@ -334,6 +347,7 @@ pub(crate) fn setup_app() -> Command { #[cfg(test)] mod test { use expectest::prelude::*; + use rstest::rstest; use crate::args::setup_app; @@ -354,12 +368,27 @@ mod test { #[test] fn validates_transport_value() { - expect!(transport_value("http:1234")).to(be_ok()); + expect!(transport_value("http:1234")).to(be_ok().value(("http".to_string(), 1234, None))); expect!(transport_value("1234x")).to(be_err()); expect!(transport_value(":1234")).to(be_err()); expect!(transport_value("x:")).to(be_err()); expect!(transport_value("x:x")).to(be_err()); expect!(transport_value("x:1234x")).to(be_err()); + expect!(transport_value("x:1234/x")).to(be_ok()); + expect!(transport_value("x:1234/p a t h")).to(be_err()); + expect!(transport_value("x:1234/p-a%20t%20h")).to(be_ok()); + } + + #[rstest( + value, expected_value, + case("http:1234/", ("http".to_string(), 1234, Some("/".to_string()))), + case("http:1234/p", ("http".to_string(), 1234, Some("/p".to_string()))), + case("http:1234/p/", ("http".to_string(), 1234, Some("/p/".to_string()))), + case("http:1234/path/2", ("http".to_string(), 1234, Some("/path/2".to_string()))), + case("http:1234/path/2/s%20s", ("http".to_string(), 1234, Some("/path/2/s%20s".to_string()))) + )] + fn validates_transport_value_with_path(value: &str, expected_value: (String, u16, Option)) { + expect!(transport_value(value)).to(be_ok().value(expected_value)); } #[test] diff --git a/rust/pact_verifier_cli/src/main.rs b/rust/pact_verifier_cli/src/main.rs index 587c1e2b..85536177 100644 --- a/rust/pact_verifier_cli/src/main.rs +++ b/rust/pact_verifier_cli/src/main.rs @@ -611,14 +611,14 @@ fn setup_pretty_log(level: &str, coloured_output: bool) { #[allow(deprecated)] pub(crate) fn configure_provider(matches: &ArgMatches) -> ProviderInfo { - // It is ok to unwrap values here, as they have all been validated by the CLI - let transports = matches.get_many::<(String, u16)>("transports") + // It is ok to unwrap values here, as they have all been validated by the CLI parser + let transports = matches.get_many::<(String, u16, Option)>("transports") .map(|values| { - values.map(|(transport, port)| { + values.map(|(transport, port, base_path)| { ProviderTransport { transport: transport.to_string(), port: Some(*port), - path: None, + path: base_path.clone(), scheme: None } }).collect()