use std::path::PathBuf;
use clap::Parser;
use semver::Version;
use crate::crate_diff_request::CrateDiffRequest;
#[derive(Debug, Parser)]
#[command(version, about, long_about = None)]
pub struct Cli {
#[arg(short, long)]
pub cargo_path: Option<PathBuf>,
#[arg(short, long, default_value = ".")]
pub manifest_path: PathBuf,
#[arg(short = 'a', long)]
pub show_all: bool,
#[arg(short, long)]
pub group: bool,
#[arg(short, long)]
pub verbose: bool,
#[arg(short, long)]
pub diff_rs: bool,
#[arg(value_parser = parse_crate_diff_info)]
pub crates: Vec<CrateDiffRequest>,
}
fn parse_crate_diff_info(s: &str) -> Result<CrateDiffRequest, String> {
let parts: Vec<_> = s.split('@').collect();
if parts.len() == 1 {
return Ok(CrateDiffRequest {
crate_name: s.into(),
from_version: None,
to_version: None,
});
}
if parts.len() == 2 {
let version_parts: Vec<_> = parts[1].split('-').collect();
if version_parts.len() == 1 {
let to_version = if version_parts[0].trim().is_empty() {
None } else {
Some(Version::parse(version_parts[0]).map_err(|err| err.to_string())?)
};
return Ok(CrateDiffRequest {
crate_name: parts[0].into(),
from_version: None,
to_version,
});
}
if version_parts.len() == 2 {
let from_version = if version_parts[0].trim().is_empty() {
None } else {
Some(Version::parse(version_parts[0]).map_err(|err| err.to_string())?)
};
let to_version = if version_parts[1].trim().is_empty() {
None } else {
Some(Version::parse(version_parts[1]).map_err(|err| err.to_string())?)
};
return Ok(CrateDiffRequest {
crate_name: parts[0].into(),
from_version,
to_version,
});
}
}
Err(format!("Wrong crate version format: {s}"))
}
#[cfg(test)]
mod tests {
use semver::Version;
use crate::cli::parse_crate_diff_info;
#[test]
fn test_simple_crate_definition() {
check_simple_crate_def("serde", "serde");
check_simple_crate_def("serde@", "serde");
check_simple_crate_def("serde@-", "serde");
}
#[test]
fn test_crate_with_target_version() {
check_rate_with_target_version("serde@1.0.225", "serde", Version::new(1, 0, 225));
check_rate_with_target_version("serde@-1.0.223", "serde", Version::new(1, 0, 223));
}
#[test]
fn test_crate_with_source_version() {
let info = parse_crate_diff_info("serde@1.0.224-").expect("Wrong crate version definition");
assert_eq!("serde", info.crate_name);
assert_eq!(Some(Version::new(1, 0, 224)), info.from_version);
assert_eq!(None, info.to_version);
}
#[test]
fn test_crate_with_both_versions() {
let info =
parse_crate_diff_info("serde@1.0.224-1.0.228").expect("Wrong crate version definition");
assert_eq!("serde", info.crate_name);
assert_eq!(Some(Version::new(1, 0, 224)), info.from_version);
assert_eq!(Some(Version::new(1, 0, 228)), info.to_version);
}
#[test]
fn test_multiple_at_fail() {
let result = parse_crate_diff_info("serde@1.0.224@1.0.228");
assert!(result.is_err());
}
#[test]
fn test_multiple_dash_fail() {
let result = parse_crate_diff_info("serde@1.0.224-1.0.226-1.0.228");
assert!(result.is_err());
}
#[test]
fn test_wrong_first_version_fail() {
let result = parse_crate_diff_info("serde@1.0.22*");
assert!(result.is_err());
}
#[test]
fn test_wrong_second_version_fail() {
let result = parse_crate_diff_info("serde@-1.0.22*");
assert!(result.is_err());
}
fn check_simple_crate_def(s: &str, expected_name: &str) {
let info = parse_crate_diff_info(s).expect("Wrong crate version definition");
assert_eq!(expected_name, info.crate_name, "{s}");
assert_eq!(None, info.from_version, "{s}");
assert_eq!(None, info.to_version, "{s}");
}
fn check_rate_with_target_version(s: &str, expected_name: &str, expected_version: Version) {
let info = parse_crate_diff_info(s).expect("Wrong crate version definition");
assert_eq!(expected_name, info.crate_name, "{s}");
assert_eq!(None, info.from_version, "{s}");
assert_eq!(Some(expected_version), info.to_version, "{s}");
}
}