aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/mozart-core
diff options
context:
space:
mode:
Diffstat (limited to 'crates/mozart-core')
-rw-r--r--crates/mozart-core/src/lib.rs2
-rw-r--r--crates/mozart-core/src/wildcard.rs86
2 files changed, 88 insertions, 0 deletions
diff --git a/crates/mozart-core/src/lib.rs b/crates/mozart-core/src/lib.rs
index 510257b..5383d9a 100644
--- a/crates/mozart-core/src/lib.rs
+++ b/crates/mozart-core/src/lib.rs
@@ -6,5 +6,7 @@ pub mod platform;
pub mod suggest;
pub mod validation;
pub mod version_bumper;
+pub mod wildcard;
pub use mozart_console_macros::console_format;
+pub use wildcard::matches_wildcard;
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"));
+ }
+}