Created
May 9, 2020 15:28
-
-
Save AnthonyMikh/7a857ae4d41d9876c2817210686568af to your computer and use it in GitHub Desktop.
Решение задачи №220 от UniLecs (Маска файла)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #[derive(Debug)] | |
| enum MaskPart<'a> { | |
| Wildcard, | |
| String(&'a str), | |
| } | |
| impl<'a> MaskPart<'a> { | |
| fn parse(mut mask: &'a str) -> impl Iterator<Item = MaskPart<'a>> + 'a { | |
| std::iter::from_fn(move || { | |
| if mask.is_empty() { | |
| return None; | |
| } | |
| if let [b'?', ..] = mask.as_bytes() { | |
| mask = &mask[1..]; | |
| Some(Self::Wildcard) | |
| } else { | |
| let pos = mask.find('?').unwrap_or(mask.len()); | |
| let (ret, new_mask) = mask.split_at(pos); | |
| mask = new_mask; | |
| Some(Self::String(ret)) | |
| } | |
| }) | |
| } | |
| fn parse_back(mut mask: &'a str) -> impl Iterator<Item = MaskPart<'a>> + 'a { | |
| std::iter::from_fn(move || { | |
| if mask.is_empty() { | |
| return None; | |
| } | |
| if let [rest @ .., b'?'] = mask.as_bytes() { | |
| mask = &mask[..rest.len()]; | |
| Some(Self::Wildcard) | |
| } else { | |
| let pos = mask.rfind('?').map_or(0, |i| i + 1); | |
| let (new_mask, ret) = mask.split_at(pos); | |
| mask = new_mask; | |
| Some(Self::String(ret)) | |
| } | |
| }) | |
| } | |
| fn apply<'s>(&self, s: &'s str) -> Option<&'s str> { | |
| match *self { | |
| Self::Wildcard => { | |
| let mut chars = s.chars(); | |
| chars.next()?; | |
| Some(chars.as_str()) | |
| } | |
| Self::String(prefix) => strip_prefix(s, prefix), | |
| } | |
| } | |
| fn apply_back<'s>(&self, s: &'s str) -> Option<&'s str> { | |
| match *self { | |
| Self::Wildcard => { | |
| let mut chars = s.chars(); | |
| chars.next_back()?; | |
| Some(chars.as_str()) | |
| } | |
| Self::String(suffix) => strip_suffix(s, suffix), | |
| } | |
| } | |
| } | |
| fn strip_prefix<'s>(s: &'s str, prefix: &str) -> Option<&'s str> { | |
| if s.starts_with(prefix) { | |
| Some(&s[prefix.len()..]) | |
| } else { | |
| None | |
| } | |
| } | |
| fn strip_suffix<'s>(s: &'s str, suffix: &str) -> Option<&'s str> { | |
| if s.ends_with(suffix) { | |
| Some(&s[..s.len() - suffix.len()]) | |
| } else { | |
| None | |
| } | |
| } | |
| fn matches_mask(mask: &str, name: &str) -> bool { | |
| match mask.find('*') { | |
| Some(pos) => { | |
| assert!(!mask[pos + 1..].contains('*'), "mask has more than one '*'"); | |
| let (front, back) = mask.split_at(pos); | |
| let back = &back[1..]; | |
| let leftover = match MaskPart::parse(front).try_fold(name, |s, part| part.apply(s)) { | |
| Some(s) => s, | |
| None => return false, | |
| }; | |
| MaskPart::parse_back(back) | |
| .try_fold(leftover, |s, part| part.apply_back(s)) | |
| .is_some() | |
| } | |
| None => MaskPart::parse(mask) | |
| .try_fold(name, |s, part| part.apply(s)) | |
| .is_some(), | |
| } | |
| } | |
| #[test] | |
| fn some_examples() { | |
| assert!(matches_mask("?or*.d??", "ford.dll")); | |
| assert!(matches_mask("?or*.d??", "fordddddddddddddddddddddddddddd.dll")); | |
| assert!(matches_mask("???????", "PROFIT!")); | |
| assert!(matches_mask("sh*t", "shoot")); | |
| } | |
| #[test] | |
| fn some_counterexamples() { | |
| assert!(!matches_mask("?or*.d??", "orsk.dll")); | |
| assert!(!matches_mask("what?", "where?")); | |
| assert!(!matches_mask("UniLecs", "garbage")); | |
| } | |
| #[test] | |
| fn handles_unicode() { | |
| assert!(matches_mask("Привет, ???!", "Привет, мир!")); | |
| assert!(!matches_mask("?я*я?", "яаяяя")); | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Условия