use crate::errors::*;
use crate::tokens::{self, Token};
use std::str::FromStr;
#[derive(Debug, PartialEq, Clone)]
pub struct Pattern {
fragments: Vec<Fragment>,
first: bool,
done: bool,
}
impl Pattern {
#[inline]
pub fn next<'a>(&mut self, out: &'a mut String) -> Option<&'a mut String> {
if self.done {
return None;
}
for frag in &mut self.fragments {
match frag {
Fragment::Chunk(chunk) => out.push_str(chunk),
Fragment::Switch(switch) => {
switch.next(out);
}
}
}
self.done = self.bump();
Some(out)
}
#[cfg(test)]
#[inline]
pub fn next_owned(&mut self) -> Option<String> {
let mut out = String::new();
if let Some(_) = self.next(&mut out) {
Some(out)
} else {
None
}
}
#[inline]
pub fn bump(&mut self) -> bool {
for frag in &mut self.fragments {
match frag {
Fragment::Chunk(_) => (),
Fragment::Switch(switch) => {
if !switch.bump() {
return false;
}
}
}
}
true
}
pub fn count(&self) -> usize {
let mut sum = 1;
for frag in &self.fragments {
let m = match frag {
Fragment::Chunk(_) => 1,
Fragment::Switch(switch) => switch.count(),
};
sum *= m;
}
sum
}
}
impl From<Vec<Fragment>> for Pattern {
fn from(fragments: Vec<Fragment>) -> Pattern {
Pattern {
fragments,
first: true,
done: false,
}
}
}
impl FromStr for Pattern {
type Err = Error;
fn from_str(s: &str) -> Result<Pattern> {
let tokens = tokens::parse(s)?;
debug!("parsed into tokens: {:?}", tokens);
let mut switches: Vec<Switch> = Vec::new();
let mut fragments = Vec::new();
for token in tokens {
debug!("adding token: {:?}", token);
match token {
Token::Chunk(chunk) => {
if let Some(tail) = switches.last_mut() {
tail.push(Fragment::Chunk(chunk));
} else {
fragments.push(Fragment::Chunk(chunk));
}
}
Token::SwitchOpen => {
switches.push(Switch::new());
}
Token::SwitchClose => {
let mut switch = switches.pop().unwrap();
switch.reset();
if let Some(tail) = switches.last_mut() {
tail.push(Fragment::Switch(switch));
} else {
fragments.push(Fragment::Switch(switch));
}
}
Token::SwitchNext => {
let tail = switches.last_mut().unwrap();
tail.bump_write_cursor();
}
}
}
Ok(Pattern::from(fragments))
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum Fragment {
Chunk(String),
Switch(Switch),
}
#[derive(Debug, PartialEq, Clone)]
pub struct Switch {
options: Vec<Vec<Fragment>>,
ctr: usize,
write_cursor: usize,
}
impl Switch {
#[inline]
pub fn new() -> Switch {
Switch {
options: Vec::new(),
ctr: 0,
write_cursor: 0,
}
}
#[inline]
pub fn push(&mut self, frag: Fragment) {
if self.write_cursor + 1 > self.options.len() {
self.options.push(Vec::new());
}
let tail = &mut self.options[self.write_cursor];
tail.push(frag);
}
#[inline(always)]
pub fn bump_write_cursor(&mut self) {
self.write_cursor += 1;
}
#[inline(always)]
pub fn reset(&mut self) {
self.write_cursor = 0;
}
#[inline]
pub fn next(&mut self, out: &mut String) {
if let Some(opt) = self.options.get_mut(self.ctr) {
for frag in opt {
match frag {
Fragment::Chunk(chunk) => out.push_str(chunk),
Fragment::Switch(switch) => {
switch.next(out);
}
};
}
}
}
#[inline]
pub fn bump(&mut self) -> bool {
if let Some(opt) = self.options.get_mut(self.ctr) {
for frag in opt {
match frag {
Fragment::Chunk(_) => (),
Fragment::Switch(switch) => {
if !switch.bump() {
return false;
}
}
}
}
}
self.ctr += 1;
if self.ctr >= self.options.len() {
self.ctr = 0;
true
} else {
false
}
}
pub fn count(&self) -> usize {
let mut sum = 0;
for fragments in &self.options {
let mut sum2 = 1;
for frag in fragments {
let m = match frag {
Fragment::Chunk(_) => 1,
Fragment::Switch(switch) => switch.count(),
};
sum2 *= m;
}
sum += sum2;
}
sum
}
}
impl From<Vec<Vec<Fragment>>> for Switch {
fn from(options: Vec<Vec<Fragment>>) -> Switch {
Switch {
options,
ctr: 0,
write_cursor: 0,
}
}
}
#[cfg(test)]
mod test {
use super::*;
fn all(mut p: Pattern) -> Vec<String> {
let mut v = Vec::new();
while let Some(s) = p.next_owned() {
v.push(s);
}
v
}
#[test]
fn simple() {
let p = Pattern::from_str("abc").unwrap();
let p2 = Pattern::from(vec![Fragment::Chunk(String::from("abc"))]);
assert_eq!(p, p2);
assert_eq!(p.count(), all(p).len());
assert_eq!(all(p2), vec![String::from("abc"),]);
}
#[test]
fn empty() {
let p = Pattern::from_str("").unwrap();
let p2 = Pattern::from(vec![]);
assert_eq!(p, p2);
assert_eq!(p.count(), all(p).len());
assert_eq!(all(p2), vec![String::new(),]);
}
#[test]
fn switch() {
let p = Pattern::from_str("abc{x,y,z}").unwrap();
let p2 = Pattern::from(vec![
Fragment::Chunk(String::from("abc")),
Fragment::Switch(Switch::from(vec![
vec![Fragment::Chunk(String::from("x"))],
vec![Fragment::Chunk(String::from("y"))],
vec![Fragment::Chunk(String::from("z"))],
])),
]);
assert_eq!(p, p2);
assert_eq!(p.count(), all(p).len());
assert_eq!(
all(p2),
vec![
String::from("abcx"),
String::from("abcy"),
String::from("abcz"),
]
);
}
#[test]
fn nested() {
let p = Pattern::from_str("abc{x,{y,z}}").unwrap();
let p2 = Pattern::from(vec![
Fragment::Chunk(String::from("abc")),
Fragment::Switch(Switch::from(vec![
vec![Fragment::Chunk(String::from("x"))],
vec![Fragment::Switch(Switch::from(vec![
vec![Fragment::Chunk(String::from("y"))],
vec![Fragment::Chunk(String::from("z"))],
]))],
])),
]);
assert_eq!(p, p2);
assert_eq!(p.count(), all(p).len());
assert_eq!(
all(p2),
vec![
String::from("abcx"),
String::from("abcy"),
String::from("abcz"),
]
);
}
#[test]
fn nested_multiple_times() {
let p = Pattern::from_str("{{{a,b,c},x},y}").unwrap();
let p2 = Pattern::from(vec![Fragment::Switch(Switch::from(vec![
vec![Fragment::Switch(Switch::from(vec![
vec![Fragment::Switch(Switch::from(vec![
vec![Fragment::Chunk(String::from("a"))],
vec![Fragment::Chunk(String::from("b"))],
vec![Fragment::Chunk(String::from("c"))],
]))],
vec![Fragment::Chunk(String::from("x"))],
]))],
vec![Fragment::Chunk(String::from("y"))],
]))]);
assert_eq!(p, p2);
assert_eq!(p.count(), all(p).len());
assert_eq!(
all(p2),
vec![
String::from("a"),
String::from("b"),
String::from("c"),
String::from("x"),
String::from("y"),
]
);
}
#[test]
fn prefix_nested() {
let p = Pattern::from_str("abc{x{y,z}}").unwrap();
let p2 = Pattern::from(vec![
Fragment::Chunk(String::from("abc")),
Fragment::Switch(Switch::from(vec![vec![
Fragment::Chunk(String::from("x")),
Fragment::Switch(Switch::from(vec![
vec![Fragment::Chunk(String::from("y"))],
vec![Fragment::Chunk(String::from("z"))],
])),
]])),
]);
assert_eq!(p, p2);
assert_eq!(p.count(), all(p).len());
assert_eq!(all(p2), vec![String::from("abcxy"), String::from("abcxz"),]);
}
#[test]
fn prefix_nested_middle() {
let p = Pattern::from_str("a{b,c{x,y},d}").unwrap();
let p2 = Pattern::from(vec![
Fragment::Chunk(String::from("a")),
Fragment::Switch(Switch::from(vec![
vec![Fragment::Chunk(String::from("b"))],
vec![
Fragment::Chunk(String::from("c")),
Fragment::Switch(Switch::from(vec![
vec![Fragment::Chunk(String::from("x"))],
vec![Fragment::Chunk(String::from("y"))],
])),
],
vec![Fragment::Chunk(String::from("d"))],
])),
]);
assert_eq!(p, p2);
assert_eq!(p.count(), all(p).len());
assert_eq!(
all(p2),
vec![
String::from("ab"),
String::from("acx"),
String::from("acy"),
String::from("ad"),
]
);
}
#[test]
fn chained() {
let p = Pattern::from_str("{x,y,z}{x,y,z}").unwrap();
let p2 = Pattern::from(vec![
Fragment::Switch(Switch::from(vec![
vec![Fragment::Chunk(String::from("x"))],
vec![Fragment::Chunk(String::from("y"))],
vec![Fragment::Chunk(String::from("z"))],
])),
Fragment::Switch(Switch::from(vec![
vec![Fragment::Chunk(String::from("x"))],
vec![Fragment::Chunk(String::from("y"))],
vec![Fragment::Chunk(String::from("z"))],
])),
]);
assert_eq!(p, p2);
assert_eq!(p.count(), all(p).len());
assert_eq!(
all(p2),
vec![
String::from("xx"),
String::from("yx"),
String::from("zx"),
String::from("xy"),
String::from("yy"),
String::from("zy"),
String::from("xz"),
String::from("yz"),
String::from("zz"),
]
);
}
#[test]
fn chained_single_items_once() {
let p = Pattern::from_str("{a}{a}").unwrap();
let p2 = Pattern::from(vec![
Fragment::Switch(Switch::from(vec![vec![Fragment::Chunk(String::from("a"))]])),
Fragment::Switch(Switch::from(vec![vec![Fragment::Chunk(String::from("a"))]])),
]);
assert_eq!(p, p2);
assert_eq!(p.count(), all(p).len());
assert_eq!(all(p2), vec![String::from("aa"),]);
}
#[test]
fn chained_single_items_twice() {
let p = Pattern::from_str("{a}{a}{a}").unwrap();
let p2 = Pattern::from(vec![
Fragment::Switch(Switch::from(vec![vec![Fragment::Chunk(String::from("a"))]])),
Fragment::Switch(Switch::from(vec![vec![Fragment::Chunk(String::from("a"))]])),
Fragment::Switch(Switch::from(vec![vec![Fragment::Chunk(String::from("a"))]])),
]);
assert_eq!(p, p2);
assert_eq!(p.count(), all(p).len());
assert_eq!(all(p2), vec![String::from("aaa"),]);
}
#[test]
fn empty_switch() {
let p = Pattern::from_str("{}").unwrap();
let p2 = Pattern::from(vec![Fragment::Switch(Switch::from(vec![]))]);
assert_eq!(p, p2);
assert_eq!(all(p2), vec![String::new()]);
}
#[test]
fn optional_prefix() {
let p = Pattern::from_str("{{a..b},}x").unwrap();
let p2 = Pattern::from(vec![
Fragment::Switch(Switch::from(vec![
vec![Fragment::Switch(Switch::from(vec![
vec![Fragment::Chunk(String::from("a"))],
vec![Fragment::Chunk(String::from("b"))],
]))],
vec![Fragment::Chunk(String::from(""))],
])),
Fragment::Chunk(String::from("x")),
]);
assert_eq!(p, p2);
assert_eq!(p.count(), all(p).len());
assert_eq!(
all(p2),
vec![String::from("ax"), String::from("bx"), String::from("x"),]
);
}
#[test]
fn one_empty_chunk_right() {
let p = Pattern::from_str("abc{x,y,}").unwrap();
let p2 = Pattern::from(vec![
Fragment::Chunk(String::from("abc")),
Fragment::Switch(Switch::from(vec![
vec![Fragment::Chunk(String::from("x"))],
vec![Fragment::Chunk(String::from("y"))],
vec![Fragment::Chunk(String::from(""))],
])),
]);
assert_eq!(p, p2);
assert_eq!(p.count(), all(p).len());
assert_eq!(
all(p2),
vec![
String::from("abcx"),
String::from("abcy"),
String::from("abc"),
]
);
}
#[test]
fn one_empty_chunk_left() {
let p = Pattern::from_str("abc{,x,y}").unwrap();
let p2 = Pattern::from(vec![
Fragment::Chunk(String::from("abc")),
Fragment::Switch(Switch::from(vec![
vec![Fragment::Chunk(String::from(""))],
vec![Fragment::Chunk(String::from("x"))],
vec![Fragment::Chunk(String::from("y"))],
])),
]);
assert_eq!(p, p2);
assert_eq!(p.count(), all(p).len());
assert_eq!(
all(p2),
vec![
String::from("abc"),
String::from("abcx"),
String::from("abcy"),
]
);
}
#[test]
fn one_empty_chunk_center() {
let p = Pattern::from_str("abc{x,,y}").unwrap();
let p2 = Pattern::from(vec![
Fragment::Chunk(String::from("abc")),
Fragment::Switch(Switch::from(vec![
vec![Fragment::Chunk(String::from("x"))],
vec![Fragment::Chunk(String::from(""))],
vec![Fragment::Chunk(String::from("y"))],
])),
]);
assert_eq!(p, p2);
assert_eq!(p.count(), all(p).len());
assert_eq!(
all(p2),
vec![
String::from("abcx"),
String::from("abc"),
String::from("abcy"),
]
);
}
#[test]
fn numeric_range() {
let p = Pattern::from_str("{0..9}").unwrap();
let p2 = Pattern::from(vec![Fragment::Switch(Switch::from(vec![
vec![Fragment::Chunk(String::from("0"))],
vec![Fragment::Chunk(String::from("1"))],
vec![Fragment::Chunk(String::from("2"))],
vec![Fragment::Chunk(String::from("3"))],
vec![Fragment::Chunk(String::from("4"))],
vec![Fragment::Chunk(String::from("5"))],
vec![Fragment::Chunk(String::from("6"))],
vec![Fragment::Chunk(String::from("7"))],
vec![Fragment::Chunk(String::from("8"))],
vec![Fragment::Chunk(String::from("9"))],
]))]);
assert_eq!(p, p2);
assert_eq!(p.count(), all(p).len());
assert_eq!(
all(p2),
vec![
String::from("0"),
String::from("1"),
String::from("2"),
String::from("3"),
String::from("4"),
String::from("5"),
String::from("6"),
String::from("7"),
String::from("8"),
String::from("9"),
]
);
}
#[test]
fn numeric_range_errs() {
assert!(Pattern::from_str("{..}").is_err());
assert!(Pattern::from_str("{0..}").is_err());
assert!(Pattern::from_str("{..0}").is_err());
assert!(Pattern::from_str("{00..}").is_err());
assert!(Pattern::from_str("{..00}").is_err());
assert!(Pattern::from_str("{00..00}").is_err());
assert!(Pattern::from_str("{.").is_err());
assert!(Pattern::from_str("{..").is_err());
assert!(Pattern::from_str("{...}").is_err());
assert!(Pattern::from_str("{a...}").is_err());
}
}