const DEBUG: bool = false;
pub fn readf(format: &str, mut s: &str) -> Option<Vec<String>> {
let mut patterns = Vec::new();
{
let mut format = format;
while let Some(idx) = format.find("{}") {
patterns.push(&format[..idx]);
format = &format[(idx + 2)..];
}
patterns.push(format);
}
let count = patterns.len() - 1;
if DEBUG {
println!("Patterns ({count}): {patterns:?}");
}
let mut result = Vec::with_capacity(count);
if count == 0 {
if s == format {
return Some(result);
} else {
return None;
}
}
{
let pat = patterns[0];
if s.get(..pat.len()).is_none_or(|x| x != pat) {
return None;
}
if DEBUG {
println!("Shaving off pattern {pat:?} from {s:?}.");
}
s = &s[pat.len()..];
}
for i in 0..count {
let next_pat = patterns[i + 1];
if DEBUG {
println!("Consuming until next pat: {next_pat:?}");
}
let placeholder_end = if i + 1 == count {
let pat = s.len().wrapping_sub(next_pat.len());
if s.get(pat..).is_none_or(|x| x != next_pat) {
None
} else {
Some(pat)
}
} else {
s.find(next_pat)
};
let Some(placeholder_end) = placeholder_end else {
if DEBUG {
println!("! Unable to find next pat.");
}
return None;
};
result.push(s[..placeholder_end].to_owned());
if DEBUG {
println!("Consumed {:?}.", &s[..placeholder_end]);
}
s = &s[placeholder_end..];
if DEBUG {
println!("Shaving off pattern {next_pat:?} from {s:?}.");
}
s = &s[next_pat.len()..];
}
if !s.is_empty() {
unreachable!("String not fully consumed!");
}
Some(result)
}
pub fn readf1(format: &str, s: &str) -> Option<String> {
let r = readf(format, s);
r.map(|x| {
if x.is_empty() {
"".into()
} else {
x[0].clone()
}
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn t_readf1() {
assert_eq!(readf1("hello, {}", "hello, person"), Some("person".into()));
assert_eq!(
readf1("hello, {}!", "hello, person!"),
Some("person".into())
);
assert_eq!(readf1("Hello, {}", "hello, person"), None);
assert_eq!(readf1("Hello, {}!", "hello, person"), None);
assert_eq!(readf1("hello!", "hello!"), Some("".into()));
assert_eq!(readf1("Hello!", "hello!"), None);
assert_eq!(readf1("{}", "person"), Some("person".into()));
}
#[test]
fn t_readf() {
assert_eq!(
readf("hello, {} and {}", "hello, person 1 and person 2"),
Some(vec!["person 1".into(), "person 2".into()])
);
assert_eq!(
readf("hello, {} and {}!", "hello, person 1 and person 2!"),
Some(vec!["person 1".into(), "person 2".into()])
);
assert_eq!(readf("hello, {} and {}", "hello, person"), None);
assert_eq!(readf("hello, {} and {}!", "hello, person!"), None);
assert_eq!(
readf("Hello, {} and {}", "hello, person 1 and person 2"),
None
);
assert_eq!(
readf("Hello, {} and {}!", "hello, person 1 and person 2!"),
None
);
assert_eq!(readf("hello!", "hello!"), Some(vec![]));
assert_eq!(readf("Hello!", "hello!"), None);
assert_eq!(
readf("{}, {}", "person 1, person 2"),
Some(vec!["person 1".into(), "person 2".into()])
);
assert_eq!(
readf("{}, {}, {}", "person 1, person 2, person 3"),
Some(vec![
"person 1".into(),
"person 2".into(),
"person 3".into()
])
);
assert_eq!(
readf("{}, {}, {}, ", "person 1, person 2, person 3, "),
Some(vec![
"person 1".into(),
"person 2".into(),
"person 3".into()
])
);
assert_eq!(readf("{}, {}, {}, ", "person 1, person 2, person 3"), None);
}
}