Skip to content

Instantly share code, notes, and snippets.

@AnthonyMikh
Created May 9, 2020 15:28
Show Gist options
  • Select an option

  • Save AnthonyMikh/7a857ae4d41d9876c2817210686568af to your computer and use it in GitHub Desktop.

Select an option

Save AnthonyMikh/7a857ae4d41d9876c2817210686568af to your computer and use it in GitHub Desktop.
Решение задачи №220 от UniLecs (Маска файла)
#[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("?я*я?", "яаяяя"));
}
@AnthonyMikh
Copy link
Copy Markdown
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment