use crate::pattern::*;
use std::collections::BTreeSet;
type Dependencies = BTreeSet<Special>;
impl Pattern<Parsed> {
pub fn optimize(self) -> (Pattern<Optimized>, Dependencies) {
let mut dependencies = Dependencies::new();
let output = Optimize::optimize(self, &mut dependencies);
(output, dependencies)
}
}
pub trait Optimize {
type Output;
fn optimize(self, dependencies: &mut Dependencies) -> Self::Output;
}
impl Optimize for Literal<Parsed> {
type Output = Literal<Optimized>;
fn optimize(self, dependencies: &mut Dependencies) -> Self::Output {
Self::Output {
value: self.value.to_string(),
}
}
}
impl Optimize for Special<Parsed> {
type Output = Special<Optimized>;
fn optimize(self, dependencies: &mut Dependencies) -> Self::Output {
dependencies.insert(self.clone());
match self {
Self::Wordlist(name) => Self::Output::Wordlist(name),
Self::Markov(name) => Self::Output::Markov(name),
Self::Preset(name) => Self::Output::Preset(name),
}
}
}
impl Optimize for Set<Parsed> {
type Output = Set<Optimized>;
fn optimize(self, dependencies: &mut Dependencies) -> Self::Output {
self.into()
}
}
impl Optimize for Item<Parsed> {
type Output = Item<Optimized>;
fn optimize(self, dependencies: &mut Dependencies) -> Self::Output {
Self::Output {
pattern: Optimize::optimize(self.pattern, dependencies),
repeat: self.repeat,
optional: self.optional,
}
}
}
impl Segment<Optimized> {
fn is_empty(&self) -> bool {
self.items.iter().any(|i| !i.is_empty())
}
}
impl Group<Optimized> {
fn is_empty(&self) -> bool {
self.segments.iter().any(|s| !s.is_empty())
}
}
impl Item<Optimized> {
fn is_empty(&self) -> bool {
if self.repeat == (0..=0) {
return true;
}
match &self.pattern {
Pattern::Group(group) => group.is_empty(),
Pattern::Set(_) => false,
Pattern::Special(_) => false,
Pattern::Literal(literal) => literal.value.is_empty(),
}
}
fn is_modified(&self) -> bool {
self.optional || self.repeat != (1..=1)
}
fn try_fuse(&mut self, item: Self) -> Result<(), Self> {
if self.is_modified() || item.is_modified() {
return Err(item);
}
match (&mut self.pattern, &item.pattern) {
(Pattern::Literal(left), Pattern::Literal(right)) => {
left.value.push_str(&right.value);
Ok(())
}
_ => Err(item),
}
}
fn flatten(self) -> impl Iterator<Item = Self> {
if self.is_modified() {
}
match &self.pattern {
Pattern::Group(group) if group.segments.len() == 1 => {}
_ => {}
}
std::iter::once(self)
}
fn simplify(self) -> Self {
if self.is_modified() {
return self;
}
match &self.pattern {
Pattern::Set(set) => {
self
}
_ => self,
}
}
}
impl Optimize for Segment<Parsed> {
type Output = Segment<Optimized>;
fn optimize(self, dependencies: &mut Dependencies) -> Self::Output {
Self::Output {
items: self
.items
.into_iter()
.map(|item| item.optimize(dependencies))
.map(Item::simplify)
.filter(|i| !i.is_empty())
.flat_map(Item::flatten)
.fold(Vec::new(), |mut items, item| {
if let Some(last) = items.last_mut() {
if let Err(item) = last.try_fuse(item) {
items.push(item);
}
} else {
items.push(item);
}
items
}),
}
}
}
impl Optimize for Group<Parsed> {
type Output = Group<Optimized>;
fn optimize(self, dependencies: &mut Dependencies) -> Self::Output {
Self::Output {
segments: self
.segments
.into_iter()
.map(|segment| segment.optimize(dependencies))
.collect(),
}
}
}
impl Optimize for Pattern<Parsed> {
type Output = Pattern<Optimized>;
fn optimize(self, dependencies: &mut Dependencies) -> Self::Output {
match self {
Self::Group(group) => Self::Output::Group(group.optimize(dependencies)),
Self::Set(set) => Self::Output::Set(set.optimize(dependencies)),
Self::Literal(literal) => Self::Output::Literal(literal.optimize(dependencies)),
Self::Special(special) => Self::Output::Special(special.optimize(dependencies)),
}
}
}