diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-02-23 01:34:55 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-02-23 01:58:28 +0900 |
| commit | ec3d69446cf07409b9c91de3d2e63856f33b26fd (patch) | |
| tree | 06f6c7eb0633809c8f58ab098adb9899cc57af90 /crates/mozart-core/src/wildcard.rs | |
| parent | 1ab3d928a2d350ce407205d9ee6ea9569cd38424 (diff) | |
| download | php-mozart-ec3d69446cf07409b9c91de3d2e63856f33b26fd.tar.gz php-mozart-ec3d69446cf07409b9c91de3d2e63856f33b26fd.tar.zst php-mozart-ec3d69446cf07409b9c91de3d2e63856f33b26fd.zip | |
fix(show,outdated): align outdated classification and wildcard handling with Composer
- Extract matches_wildcard to mozart-core for reuse across commands
- Support wildcard patterns in --package and --ignore arguments
- Use ^<installed_version> for semver-safe classification instead of root constraint
- Replace std::process::exit(1) with bail_silent for proper cleanup
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'crates/mozart-core/src/wildcard.rs')
| -rw-r--r-- | crates/mozart-core/src/wildcard.rs | 86 |
1 files changed, 86 insertions, 0 deletions
diff --git a/crates/mozart-core/src/wildcard.rs b/crates/mozart-core/src/wildcard.rs new file mode 100644 index 0000000..9193549 --- /dev/null +++ b/crates/mozart-core/src/wildcard.rs @@ -0,0 +1,86 @@ +/// Match a package name against a wildcard pattern (case-insensitive). +/// `*` matches any sequence of characters. +pub fn matches_wildcard(name: &str, pattern: &str) -> bool { + let name_lower = name.to_lowercase(); + let pattern_lower = pattern.to_lowercase(); + let parts: Vec<&str> = pattern_lower.split('*').collect(); + + if parts.len() == 1 { + return name_lower == pattern_lower; + } + + let mut pos = 0usize; + for (i, part) in parts.iter().enumerate() { + if part.is_empty() { + continue; + } + match name_lower[pos..].find(*part) { + Some(found) => { + if i == 0 && found != 0 { + return false; // First segment must match at start + } + pos += found + part.len(); + } + None => return false, + } + } + + // If pattern doesn't end with *, name must be fully consumed + if !pattern_lower.ends_with('*') { + return pos == name_lower.len(); + } + + true +} + +// ─── Tests ────────────────────────────────────────────────────────────────── + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_matches_wildcard_exact() { + assert!(matches_wildcard("psr/log", "psr/log")); + } + + #[test] + fn test_matches_wildcard_star_end() { + assert!(matches_wildcard("psr/log", "psr/*")); + } + + #[test] + fn test_matches_wildcard_star_start() { + assert!(matches_wildcard("psr/log", "*/log")); + } + + #[test] + fn test_matches_wildcard_star_middle() { + assert!(matches_wildcard("monolog/monolog", "mono*/mono*")); + } + + #[test] + fn test_matches_wildcard_no_match() { + assert!(!matches_wildcard("psr/log", "symfony/*")); + } + + #[test] + fn test_matches_wildcard_case_insensitive() { + assert!(matches_wildcard("PSR/Log", "psr/*")); + } + + #[test] + fn test_matches_wildcard_star_both_ends() { + assert!(matches_wildcard("monolog/monolog", "*log*")); + } + + #[test] + fn test_matches_wildcard_no_wildcard_mismatch() { + assert!(!matches_wildcard("psr/log", "psr/log2")); + } + + #[test] + fn test_matches_wildcard_trailing_chars_fail() { + assert!(!matches_wildcard("psr/log", "psr/l")); + } +} |
