Something went wrong. Try again.
Nushell plugin for interacting with D-Bus
Something went wrong. Try again.
15 kB · 470 lines
Rust
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471#[derive(Debug, Clone, PartialEq, Eq)]pub struct Pattern { separator: Option<char>, tokens: Vec<PatternToken>,}
#[derive(Debug, Clone, PartialEq, Eq)]enum PatternToken { Exact(String), OneWildcard, ManyWildcard, AnyChar,}
impl Pattern { pub fn new(pattern: &str, separator: Option<char>) -> Pattern { let mut tokens = vec![]; for ch in pattern.chars() { match ch { '*' => { if tokens.last() == Some(&PatternToken::OneWildcard) { *tokens.last_mut().unwrap() = PatternToken::ManyWildcard; } else { tokens.push(PatternToken::OneWildcard); } } '?' => tokens.push(PatternToken::AnyChar), _ => match tokens.last_mut() { Some(PatternToken::Exact(s)) => s.push(ch), _ => tokens.push(PatternToken::Exact(ch.into())), }, } } Pattern { separator, tokens } }
pub fn is_match(&self, string: &str) -> bool { #[derive(Debug)] enum MatchState { Precise, ScanAhead { stop_at_separator: bool }, } let mut state = MatchState::Precise; let mut tokens = &self.tokens[..]; let mut search_str = string; while !tokens.is_empty() { match tokens.first().unwrap() { PatternToken::Exact(s) => { if search_str.starts_with(s) { // Exact match passed. Consume the token and string and continue tokens = &tokens[1..]; search_str = &search_str[s.len()..]; state = MatchState::Precise; } else { match state { MatchState::Precise => { // Can't possibly match return false; } MatchState::ScanAhead { stop_at_separator } => { if search_str.is_empty() { // End of input, can't match return false; } if stop_at_separator && self .separator .is_some_and(|sep| search_str.starts_with(sep)) { // Found the separator. Consume a char and revert to precise // mode search_str = &search_str[1..]; state = MatchState::Precise; } else { // Skip the non-matching char and continue search_str = &search_str[1..]; } } } } } PatternToken::OneWildcard => { // Set the mode to ScanAhead, stopping at separator state = MatchState::ScanAhead { stop_at_separator: true, }; tokens = &tokens[1..]; } PatternToken::ManyWildcard => { // Set the mode to ScanAhead, ignoring separator state = MatchState::ScanAhead { stop_at_separator: false, }; tokens = &tokens[1..]; } PatternToken::AnyChar => { if !search_str.is_empty() { // Take a char from the search str and continue search_str = &search_str[1..]; tokens = &tokens[1..]; } else { // End of input return false; } } } } #[cfg(test)] { println!( "end, state={:?}, search_str={:?}, tokens={:?}", state, search_str, tokens ); } if !search_str.is_empty() { // If the search str is not empty at the end match state { // We didn't end with a wildcard, so this is a fail MatchState::Precise => false, // This could be a match as long as the separator isn't contained in the remainder MatchState::ScanAhead { stop_at_separator: true, } => { if let Some(separator) = self.separator { !search_str.contains(separator) } else { // No separator specified, so this is a success true } } // Always a success, no matter what remains MatchState::ScanAhead { stop_at_separator: false, } => true, } } else { // The match has succeeded - there is nothing more to match true } }}
#[test]fn test_pattern_new() { assert_eq!( Pattern::new("", Some('/')), Pattern { separator: Some('/'), tokens: vec![] } ); assert_eq!( Pattern::new("", None), Pattern { separator: None, tokens: vec![] } ); assert_eq!( Pattern::new("org.freedesktop.DBus", Some('.')), Pattern { separator: Some('.'), tokens: vec![PatternToken::Exact("org.freedesktop.DBus".into()),] } ); assert_eq!( Pattern::new("*", Some('.')), Pattern { separator: Some('.'), tokens: vec![PatternToken::OneWildcard,] } ); assert_eq!( Pattern::new("**", Some('.')), Pattern { separator: Some('.'), tokens: vec![PatternToken::ManyWildcard,] } ); assert_eq!( Pattern::new("?", Some('.')), Pattern { separator: Some('.'), tokens: vec![PatternToken::AnyChar,] } ); assert_eq!( Pattern::new("org.freedesktop.*", Some('.')), Pattern { separator: Some('.'), tokens: vec![ PatternToken::Exact("org.freedesktop.".into()), PatternToken::OneWildcard, ] } ); assert_eq!( Pattern::new("org.freedesktop.**", Some('.')), Pattern { separator: Some('.'), tokens: vec![ PatternToken::Exact("org.freedesktop.".into()), PatternToken::ManyWildcard, ] } ); assert_eq!( Pattern::new("org.*.DBus", Some('.')), Pattern { separator: Some('.'), tokens: vec![ PatternToken::Exact("org.".into()), PatternToken::OneWildcard, PatternToken::Exact(".DBus".into()), ] } ); assert_eq!( Pattern::new("org.**.DBus", Some('.')), Pattern { separator: Some('.'), tokens: vec![ PatternToken::Exact("org.".into()), PatternToken::ManyWildcard, PatternToken::Exact(".DBus".into()), ] } ); assert_eq!( Pattern::new("org.**.?Bus", Some('.')), Pattern { separator: Some('.'), tokens: vec![ PatternToken::Exact("org.".into()), PatternToken::ManyWildcard, PatternToken::Exact(".".into()), PatternToken::AnyChar, PatternToken::Exact("Bus".into()), ] } ); assert_eq!( Pattern::new("org.free*top", Some('.')), Pattern { separator: Some('.'), tokens: vec![ PatternToken::Exact("org.free".into()), PatternToken::OneWildcard, PatternToken::Exact("top".into()), ] } ); assert_eq!( Pattern::new("org.free**top", Some('.')), Pattern { separator: Some('.'), tokens: vec![ PatternToken::Exact("org.free".into()), PatternToken::ManyWildcard, PatternToken::Exact("top".into()), ] } ); assert_eq!( Pattern::new("org.**top", Some('.')), Pattern { separator: Some('.'), tokens: vec![ PatternToken::Exact("org.".into()), PatternToken::ManyWildcard, PatternToken::Exact("top".into()), ] } ); assert_eq!( Pattern::new("**top", Some('.')), Pattern { separator: Some('.'), tokens: vec![ PatternToken::ManyWildcard, PatternToken::Exact("top".into()), ] } ); assert_eq!( Pattern::new("org.free**", Some('.')), Pattern { separator: Some('.'), tokens: vec![ PatternToken::Exact("org.free".into()), PatternToken::ManyWildcard, ] } );}
#[test]fn test_pattern_is_match_empty() { let pat = Pattern { separator: Some('.'), tokens: vec![], }; assert!(pat.is_match("")); assert!(!pat.is_match("anystring")); assert!(!pat.is_match("anystring.anyotherstring"));}
#[test]fn test_pattern_is_match_exact() { let pat = Pattern { separator: Some('.'), tokens: vec![PatternToken::Exact("specific".into())], }; assert!(pat.is_match("specific")); assert!(!pat.is_match("")); assert!(!pat.is_match("specifi")); assert!(!pat.is_match("specifica"));}
#[test]fn test_pattern_is_match_one_wildcard() { let pat = Pattern { separator: Some('.'), tokens: vec![ PatternToken::Exact("foo.".into()), PatternToken::OneWildcard, PatternToken::Exact(".baz".into()), ], }; assert!(pat.is_match("foo.bar.baz")); assert!(pat.is_match("foo.grok.baz")); assert!(pat.is_match("foo..baz")); assert!(!pat.is_match("foo.ono.notmatch.baz")); assert!(!pat.is_match("")); assert!(!pat.is_match("specifi")); assert!(!pat.is_match("specifica.baz")); assert!(!pat.is_match("foo.specifica"));}
#[test]fn test_pattern_is_match_one_wildcard_at_end() { let pat = Pattern { separator: Some('.'), tokens: vec![ PatternToken::Exact("foo.".into()), PatternToken::OneWildcard, ], }; assert!(pat.is_match("foo.bar")); assert!(pat.is_match("foo.grok")); assert!(pat.is_match("foo.")); assert!(!pat.is_match("foo.ono.notmatch.baz")); assert!(!pat.is_match("")); assert!(!pat.is_match("specifi")); assert!(!pat.is_match("specifica.baz"));}
#[test]fn test_pattern_is_match_one_wildcard_at_start() { let pat = Pattern { separator: Some('.'), tokens: vec![ PatternToken::OneWildcard, PatternToken::Exact(".bar".into()), ], }; assert!(pat.is_match("foo.bar")); assert!(pat.is_match("grok.bar")); assert!(pat.is_match(".bar")); assert!(!pat.is_match("foo.ono.notmatch.bar")); assert!(!pat.is_match("")); assert!(!pat.is_match("specifi")); assert!(!pat.is_match("specifica.baz"));}
#[test]fn test_pattern_is_match_one_wildcard_no_separator() { let pat = Pattern { separator: None, tokens: vec![ PatternToken::Exact("foo.".into()), PatternToken::OneWildcard, PatternToken::Exact(".baz".into()), ], }; assert!(pat.is_match("foo.bar.baz")); assert!(pat.is_match("foo.grok.baz")); assert!(pat.is_match("foo..baz")); assert!(pat.is_match("foo.this.shouldmatch.baz")); assert!(pat.is_match("foo.this.should.match.baz")); assert!(!pat.is_match("")); assert!(!pat.is_match("specifi")); assert!(!pat.is_match("specifica.baz")); assert!(!pat.is_match("foo.specifica"));}
#[test]fn test_pattern_is_match_many_wildcard() { let pat = Pattern { separator: Some('.'), tokens: vec![ PatternToken::Exact("foo.".into()), PatternToken::ManyWildcard, PatternToken::Exact(".baz".into()), ], }; assert!(pat.is_match("foo.bar.baz")); assert!(pat.is_match("foo.grok.baz")); assert!(pat.is_match("foo..baz")); assert!(pat.is_match("foo.this.shouldmatch.baz")); assert!(pat.is_match("foo.this.should.match.baz")); assert!(!pat.is_match("")); assert!(!pat.is_match("specifi")); assert!(!pat.is_match("specifica.baz")); assert!(!pat.is_match("foo.specifica"));}
#[test]fn test_pattern_is_match_many_wildcard_at_end() { let pat = Pattern { separator: Some('.'), tokens: vec![ PatternToken::Exact("foo.".into()), PatternToken::ManyWildcard, ], }; assert!(pat.is_match("foo.bar")); assert!(pat.is_match("foo.grok")); assert!(pat.is_match("foo.")); assert!(pat.is_match("foo.this.should.match")); assert!(!pat.is_match("")); assert!(!pat.is_match("specifi")); assert!(!pat.is_match("specifica.baz"));}
#[test]fn test_pattern_is_match_many_wildcard_at_start() { let pat = Pattern { separator: Some('.'), tokens: vec![ PatternToken::ManyWildcard, PatternToken::Exact(".bar".into()), ], }; assert!(pat.is_match("foo.bar")); assert!(pat.is_match("grok.bar")); assert!(pat.is_match("should.match.bar")); assert!(pat.is_match(".bar")); assert!(!pat.is_match("")); assert!(!pat.is_match("specifi")); assert!(!pat.is_match("specifica.baz"));}
#[test]fn test_pattern_is_match_any_char() { let pat = Pattern { separator: Some('.'), tokens: vec![ PatternToken::Exact("fo".into()), PatternToken::AnyChar, PatternToken::Exact(".baz".into()), ], }; assert!(pat.is_match("foo.baz")); assert!(pat.is_match("foe.baz")); assert!(pat.is_match("foi.baz")); assert!(!pat.is_match("")); assert!(!pat.is_match("fooo.baz")); assert!(!pat.is_match("fo.baz"));}