use lalrpop_util::ParseError; use crate::lexer::LexicalError; /// A UserID value typically looks something like: /// /// Text (Comment) /// /// That is, it contains three components: a text string, a comment, /// and an email address. /// /// The actual format allows for lots of interleaved comments and /// multiple texts. Thus, when parsing we build up a vector of /// Components in the order that they were encountered. #[derive(Debug, Clone)] pub enum Component { // A text string. Text(String), // A comment. // // The outermost parens are removed. That is, if the comment is: // "(foo(bar)bam)", then "foo(bar)bam" is stored. Comment(String), // An email address. Address(String), // The text found where an address was expected. InvalidAddress(ParseError, String), // White space. WS, } // When comparing two `Component::InvalidAddress`es, we consider them // equal if the values match; we don't compare the saved errors. This // is because the parser will always generate the same error for the // same input. And, the PartialEq implementation is only used to // support comparing two `Component`s in assertions. impl PartialEq for Component { fn eq(&self, other: &Self) -> bool { match (self, other) { (Component::Text(a), Component::Text(b)) => a == b, (Component::Comment(a), Component::Comment(b)) => a == b, (Component::Address(a), Component::Address(b)) => a == b, (Component::InvalidAddress(_, a), Component::InvalidAddress(_, b)) => a == b, (Component::WS, Component::WS) => true, (_, _) => false, } } } impl Eq for Component { } impl From for Vec { fn from(c: Component) -> Self { vec![c] } } impl From for Option> { fn from(c: Component) -> Self { Some(vec![c]) } } // Collect the `Component`s to the vector `v`. // // The Components can be anything that can be turned into an // Option>. This currently includes `Component`, and // `Vec`. macro_rules! components_concat_into { ( $v:expr, $c:expr ) => {{ let v: &mut Vec = $v; let c : Option> = $c.into(); if let Some(mut c) = c { // If v ends in a WS and c starts with a WS, then collapse // them. if destructures_to!(Some(Component::WS) = v.last()) && destructures_to!(Some(Component::WS) = c.first()) { v.pop(); } v.append(&mut c); } }}; ( $v:expr, $car:expr, $($cdr:expr),* ) => {{ let v: &mut Vec = $v; let car : Option> = $car.into(); if let Some(mut car) = car { if destructures_to!(Some(Component::WS) = v.last()) && destructures_to!(Some(Component::WS) = car.first()) { v.pop(); } v.append(&mut car) } components_concat_into!(v, $($cdr),*); }}; } // Collect the `Component`s into a vector `v`. // // The Components can be anything that can be turned into an // Option>. This currently includes `Component`, and // `Vec`. macro_rules! components_concat { ( $( $args:expr ),*) => {{ let mut v : Vec = Vec::new(); components_concat_into!(&mut v, $($args),*); v }}; } // Merge the components in the vector. pub(crate) fn components_merge(components: Vec) -> Vec { tracer!(crate::TRACE, "components_merge", 0); t!("{:?}", components); let mut iter = components.into_iter(); let mut components = vec![]; let mut left = if let Some(left) = iter.next() { left } else { return components; }; let mut middleo = iter.next(); let mut righto = iter.next(); while let Some(mut middle) = middleo { enum Kill { None, Middle, MiddleRight, }; let mut kill = Kill::None; match (&mut left, &mut middle, righto.as_mut()) { (Component::Text(ref mut l), Component::Text(ref mut m), _) => { t!("Merging '{}' and '{}'", l, m); l.push_str(m); kill = Kill::Middle; }, (Component::Text(ref mut l), Component::WS, Some(Component::Text(ref mut r))) => { t!("Merging '{}', WS and '{}'", l, r); l.push(' '); l.push_str(r); kill = Kill::MiddleRight; }, (Component::WS, Component::WS, _) => { // This can happen when we have a local-part of the // following form: // // (comment) foo (comment) // // The local-part is produced by the dot_atom_left // production, which puts the dot_atom_text (foo) to // the right: // // COMMENT WS WS COMMENT TEXT // // It is also possible to have: // // WS WS COMMENT TEXT // // as CFWS can expand to just a WS. kill = Kill::Middle; }, _ => (), } match kill { Kill::Middle => { middleo = righto; righto = iter.next(); } Kill::MiddleRight => { middleo = iter.next(); righto = iter.next(); } Kill::None => { components.push(left); left = middle; middleo = righto; righto = iter.next(); } } } components.push(left); if let Some(middle) = middleo { components.push(middle); } components }