extern crate failure;
extern crate lalrpop_util;
#[cfg(test)] #[macro_use] extern crate lazy_static;
#[cfg(test)] #[macro_use] extern crate quickcheck;
#[cfg(test)] extern crate rand;
use lalrpop_util::ParseError;
#[macro_use] mod macros;
#[macro_use] mod trace;
mod strings;
#[macro_use] mod component;
use crate::component::{
Component
};
mod lexer;
use crate::lexer::LexicalError;
#[cfg(test)]
mod grammar;
#[cfg(not(test))]
#[allow(unused_imports, dead_code)]
mod grammar;
#[cfg(test)]
mod roundtrip;
const TRACE : bool = false;
pub type Result<T> = ::std::result::Result<T, failure::Error>;
fn parse_error_downcast<'a>(e: ParseError<usize, lexer::Token<'a>, LexicalError>)
-> ParseError<usize, String, LexicalError>
{
match e {
ParseError::UnrecognizedToken {
token: (start, t, end),
expected,
} => ParseError::UnrecognizedToken {
token: (start, t.into(), end),
expected,
},
ParseError::ExtraToken {
token: (start, t, end),
} => ParseError::ExtraToken {
token: (start, t.into(), end),
},
ParseError::InvalidToken { location }
=> ParseError::InvalidToken { location },
ParseError::User { error }
=> ParseError::User { error },
ParseError::UnrecognizedEOF { location, expected }
=> ParseError::UnrecognizedEOF { location, expected },
}
}
pub struct Name {
}
impl Name {
pub fn escaped<S>(name: S) -> Result<String>
where S: AsRef<str>
{
let name = name.as_ref();
let lexer = lexer::Lexer::new(name);
grammar::EscapedDisplayNameParser::new().parse(name, lexer)
.map_err(|e| parse_error_downcast(e).into())
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct AddrSpec {
components: Vec<Component>,
}
impl AddrSpec {
#[allow(dead_code)]
fn new<S>(address: S)
-> Result<Self>
where S: AsRef<str> + Eq + std::fmt::Debug,
{
let address = address.as_ref();
let a = match Self::parse(address) {
Err(err) => return Err(err.into()),
Ok(a) => a,
};
assert_eq!(a.address(), address);
Ok(a)
}
pub fn parse<S>(input: S) -> Result<Self>
where S: AsRef<str>
{
let input = input.as_ref();
let lexer = lexer::Lexer::new(input);
let components = match grammar::AddrSpecParser::new().parse(input, lexer) {
Ok(components) => components,
Err(err) => return Err(parse_error_downcast(err).into()),
};
Ok(Self {
components,
})
}
pub fn address(&self) -> &str {
for c in self.components.iter() {
if let Component::Address(t) = c {
return &t[..];
}
}
unreachable!();
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct AddrSpecOrOther {
components: Vec<Component>,
}
impl AddrSpecOrOther {
#[allow(dead_code)]
fn new<S>(address: S)
-> Result<Self>
where S: AsRef<str> + Eq + std::fmt::Debug,
{
let address = address.as_ref();
let a = match Self::parse(address) {
Err(err) => return Err(err.into()),
Ok(a) => a,
};
Ok(a)
}
pub fn parse<S>(input: S) -> Result<Self>
where S: AsRef<str>
{
let input = input.as_ref();
let lexer = lexer::Lexer::new(input);
let components = match grammar::AddrSpecOrOtherParser::new().parse(input, lexer) {
Ok(components) => components,
Err(err) => return Err(parse_error_downcast(err).into()),
};
Ok(Self {
components,
})
}
pub fn address(&self) -> Result<&str> {
for c in self.components.iter() {
if let Component::Address(t) = c {
return Ok(&t[..]);
}
if let Component::InvalidAddress(e, _) = c {
return Err(e.clone().into());
}
}
unreachable!();
}
pub fn other(&self) -> Option<&str> {
for c in self.components.iter() {
if let Component::Address(_) = c {
return None;
}
if let Component::InvalidAddress(_, t) = c {
return Some(&t[..]);
}
}
unreachable!();
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct NameAddr {
components: Vec<Component>,
}
impl NameAddr {
#[allow(dead_code)]
fn new<S>(name: Option<S>, comment: Option<S>, address: Option<S>)
-> Result<Self>
where S: AsRef<str> + Eq + std::fmt::Debug,
{
let mut s = if let Some(ref name) = name {
String::from(name.as_ref())
} else {
String::new()
};
if let Some(ref comment) = comment {
if name.is_some() {
s.push(' ');
}
s.push_str(&format!("({})", comment.as_ref())[..]);
}
if let Some(ref address) = address {
if name.is_some() || comment.is_some() {
s.push(' ');
}
s.push_str(&format!("<{}>", address.as_ref())[..]);
}
let na = match Self::parse(s) {
Err(err) => return Err(err.into()),
Ok(na) => na,
};
if let Some(name_reparsed) = na.name() {
assert!(name.is_some());
assert_eq!(name_reparsed, name.unwrap().as_ref());
} else {
assert!(name.is_none());
}
if let Some(comment_reparsed) = na.comment() {
assert!(comment.is_some());
assert_eq!(comment_reparsed, comment.unwrap().as_ref());
} else {
assert!(comment.is_none());
}
if let Some(address_reparsed) = na.address() {
assert!(address.is_some());
assert_eq!(address_reparsed, address.unwrap().as_ref());
} else {
assert!(address.is_none());
}
Ok(na)
}
pub fn parse<S>(input: S) -> Result<Self>
where S: AsRef<str>
{
let input = input.as_ref();
let lexer = lexer::Lexer::new(input);
let components = match grammar::NameAddrParser::new().parse(input, lexer) {
Ok(components) => components,
Err(err) => return Err(parse_error_downcast(err).into()),
};
Ok(Self {
components,
})
}
pub fn name(&self) -> Option<&str> {
for c in self.components.iter() {
if let Component::Text(t) = c {
return Some(&t[..]);
}
}
None
}
pub fn comment(&self) -> Option<&str> {
for c in self.components.iter() {
if let Component::Comment(t) = c {
return Some(&t[..]);
}
}
None
}
pub fn address(&self) -> Option<&str> {
for c in self.components.iter() {
if let Component::Address(t) = c {
return Some(&t[..]);
}
}
None
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct NameAddrOrOther {
components: Vec<Component>,
}
impl NameAddrOrOther {
#[allow(dead_code)]
fn new<S>(name: Option<S>, comment: Option<S>, address: Option<S>)
-> Result<Self>
where S: AsRef<str> + Eq + std::fmt::Debug,
{
let mut s = if let Some(ref name) = name {
String::from(name.as_ref())
} else {
String::new()
};
if let Some(ref comment) = comment {
if name.is_some() {
s.push(' ');
}
s.push_str(&format!("({})", comment.as_ref())[..]);
}
if let Some(ref address) = address {
if name.is_some() || comment.is_some() {
s.push(' ');
}
s.push_str(&format!("<{}>", address.as_ref())[..]);
}
let na = match Self::parse(s) {
Err(err) => return Err(err.into()),
Ok(na) => na,
};
if let Some(name_reparsed) = na.name() {
assert!(name.is_some());
assert_eq!(name_reparsed, name.unwrap().as_ref());
} else {
assert!(name.is_none());
}
if let Some(comment_reparsed) = na.comment() {
assert!(comment.is_some());
assert_eq!(comment_reparsed, comment.unwrap().as_ref());
} else {
assert!(comment.is_none());
}
Ok(na)
}
pub fn parse<S>(input: S) -> Result<Self>
where S: AsRef<str>
{
let input = input.as_ref();
let lexer = lexer::Lexer::new(input);
let components = match grammar::NameAddrOrOtherParser::new().parse(input, lexer) {
Ok(components) => components,
Err(err) => return Err(parse_error_downcast(err).into()),
};
Ok(Self {
components,
})
}
pub fn name(&self) -> Option<&str> {
for c in self.components.iter() {
if let Component::Text(t) = c {
return Some(&t[..]);
}
}
None
}
pub fn comment(&self) -> Option<&str> {
for c in self.components.iter() {
if let Component::Comment(t) = c {
return Some(&t[..]);
}
}
None
}
pub fn address(&self) -> Result<&str> {
for c in self.components.iter() {
if let Component::Address(t) = c {
return Ok(&t[..]);
}
if let Component::InvalidAddress(e, _) = c {
return Err(e.clone().into());
}
}
unreachable!()
}
pub fn other(&self) -> Option<&str> {
for c in self.components.iter() {
if let Component::Address(_) = c {
return None;
}
if let Component::InvalidAddress(_, t) = c {
return Some(&t[..]);
}
}
unreachable!()
}
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! c {
( $parser:expr, $t:ty) => {
fn c<S>(input: S, expected: Option<$t>)
where S: AsRef<str>
{
let input = input.as_ref();
eprintln!("\n\ninput: '{:?}'", input);
let lexer = lexer::Lexer::new(&input[..]);
let result = $parser.parse(input, lexer);
if let Some(expected) = expected {
if let Ok(result) = result {
assert_eq!(result, expected,
"Parsing: '{:?}'\n got: '{:?}'\nexpected: '{:?}'",
input, result, expected);
} else {
panic!("Parsing: '{:?}': {:?}", input, result);
}
} else {
assert!(result.is_err(), "Parsing '{:?}'\n got: '{:?}'\nexpected: '{:?}'",
input, result, expected);
}
}
};
}
#[test]
fn comment_parser() {
c!(grammar::CommentParser::new(), Component);
c("foobar", None);
c("(foobar)",
Some(Component::Comment("foobar".into())));
c("(foo bar)",
Some(Component::Comment("foo bar".into())));
c("((foobar)", None);
c("(\\(foobar)",
Some(Component::Comment("(foobar".into())));
c("((foobar))",
Some(Component::Comment("(foobar)".into())));
c("((fo()ob()ar))",
Some(Component::Comment("(fo()ob()ar)".into())));
c(" \r\n ((abc))", None);
c("((abc)) ", None);
c("( a)",
Some(Component::Comment(" a".into())));
c("(a )",
Some(Component::Comment("a ".into())));
c("( a )",
Some(Component::Comment(" a ".into())));
c("( a b )",
Some(Component::Comment(" a b ".into())));
c("( \r\n a b)",
Some(Component::Comment(" a b".into())));
c("(a \r\n b )",
Some(Component::Comment("a b ".into())));
c("(a b \r\n )",
Some(Component::Comment("a b ".into())));
c("( a b )",
Some(Component::Comment(" a b ".into())));
c("( \r\n a \r\n b \r\n )",
Some(Component::Comment(" a b ".into())));
c("( a \r\n bc \r\n d )",
Some(Component::Comment(" a bc d ".into())));
c("(( a \r\n bc \r\n d ))",
Some(Component::Comment("( a bc d )".into())));
c("(foo\r\n)", None);
c("(foo\r\n )",
Some(Component::Comment("foo ".into())));
c("(( \r\n \r\n a ))", None);
c("(( abcd \r\n \r\n a ))", None);
c("(( abcd \r\n \r\n ))", None);
}
#[test]
fn cfws_parser() {
c!(grammar::CfwsParser::new(), Vec<Component>);
c("foobar", None);
c("(foobar)",
Some(vec![Component::Comment("foobar".into())]));
c("((foobar)", None);
c("((foobar))",
Some(vec![Component::Comment("(foobar)".into())]));
c("((fo()ob()ar))",
Some(vec![Component::Comment("(fo()ob()ar)".into())]));
c(" \r\n ((abc))",
Some(vec![
Component::WS,
Component::Comment("(abc)".into()),
]));
c("((abc)) \r\n ",
Some(vec![
Component::Comment("(abc)".into()),
Component::WS,
]));
c("((a \r\n bc \r\n d))",
Some(vec![Component::Comment("(a bc d)".into())]));
c("((foobar buz)) (bam)\r\n (quuz) \r\n ",
Some(vec![
Component::Comment("(foobar buz)".into()),
Component::WS,
Component::Comment("bam".into()),
Component::WS,
Component::Comment("quuz".into()),
Component::WS,
]));
c("(xy(z)zy) ((foobar)) (bam)",
Some(vec![
Component::Comment("xy(z)zy".into()),
Component::WS,
Component::Comment("(foobar)".into()),
Component::WS,
Component::Comment("bam".into())
]));
c("((foobar buz))(bam)(quuz)",
Some(vec![
Component::Comment("(foobar buz)".into()),
Component::Comment("bam".into()),
Component::Comment("quuz".into()),
]));
}
#[test]
fn atom_parser() {
c!(grammar::AtomParser::new(), Vec<Component>);
c("foobar", Some(vec![Component::Text("foobar".into())]));
for &s in ["a", "1", "ß", "ü", "é", "あ", "fooゔヲ", "ℝ💣東京",
"!", "#", "$", "%", "&", "'", "*", "+", "-",
"/", "=", "?", "^", "_", "`", "{", "|", "}", "~",
".", "@"]
.into_iter()
{
c(s, Some(vec![Component::Text(s.to_string())]))
}
for &s in ["\x02", " ",
"(", ")", "<", ">", "[", "]", ":", ";",
"\\", ",", "\""]
.into_iter()
{
c(s, None)
}
c("foo bar", None);
c("\r\n foobar \r\n ",
Some(vec![
Component::WS,
Component::Text("foobar".into()),
Component::WS,
]));
c(" \r\n foobar ",
Some(vec![
Component::WS,
Component::Text("foobar".into()),
Component::WS,
]));
c("(some comment)foobar",
Some(vec![
Component::Comment("some comment".into()),
Component::Text("foobar".into())
]));
c("(some comment) foobar",
Some(vec![
Component::Comment("some comment".into()),
Component::WS,
Component::Text("foobar".into())
]));
c("(so\r\n m \r\n e co\r\n mme \r\n nt\r\n ) \r\n foobar",
Some(vec![
Component::Comment("so m e co mme nt ".into()),
Component::WS,
Component::Text("foobar".into())
]));
c("(a)(b)(c)foobar(d)(e)",
Some(vec![
Component::Comment("a".into()),
Component::Comment("b".into()),
Component::Comment("c".into()),
Component::Text("foobar".into()),
Component::Comment("d".into()),
Component::Comment("e".into())
]));
c(" \r\n (a)\r\n (b)\r\n (c)\r\n foobar \r\n (d)(e) \r\n ",
Some(vec![
Component::WS,
Component::Comment("a".into()),
Component::WS,
Component::Comment("b".into()),
Component::WS,
Component::Comment("c".into()),
Component::WS,
Component::Text("foobar".into()),
Component::WS,
Component::Comment("d".into()),
Component::Comment("e".into()),
Component::WS,
]));
}
#[test]
fn quoted_string_parser() {
c!(grammar::QuotedStringParser::new(), Vec<Component>);
c("\"foobar\"", Some(vec![Component::Text("foobar".into())]));
c("\" foobar\"", Some(vec![Component::Text(" foobar".into())]));
c("\"foobar \"", Some(vec![Component::Text("foobar ".into())]));
c("\"foo bar bam \"",
Some(vec![Component::Text("foo bar bam ".into())]));
c("\" foo bar bam \"",
Some(vec![Component::Text(" foo bar bam ".into())]));
c("\r\n \"(some comment)\"",
Some(vec![
Component::WS,
Component::Text("(some comment)".into()),
]));
c("\"(some comment)\" \r\n ",
Some(vec![
Component::Text("(some comment)".into()),
Component::WS,
]));
c("\"\\f\\o\\o\\b\\a\\r\"",
Some(vec![Component::Text("foobar".into())]));
c("(not a comment)\"foobar\"",
Some(vec![
Component::Comment("not a comment".into()),
Component::Text("foobar".into())
]));
c("\"(not a comment)foobar\"",
Some(vec![Component::Text("(not a comment)foobar".into())]));
c("\"))((((not a comment)foobar\"",
Some(vec![Component::Text("))((((not a comment)foobar".into())]));
}
#[test]
fn word_parser() {
c!(grammar::WordParser::new(), Vec<Component>);
c("foobar", Some(vec![Component::Text("foobar".into())]));
c("\"foobar\"", Some(vec![Component::Text("foobar".into())]));
c("\"\\f\\o\\o\\b\\a\\r\"", Some(vec![Component::Text("foobar".into())]));
}
#[test]
fn phrase_parser() {
c!(grammar::PhraseParser::new(), Vec<Component>);
c("foobar", Some(vec![Component::Text("foobar".into())]));
c("foobar bam", Some(vec![Component::Text("foobar bam".into())]));
c("foobar bam", Some(vec![Component::Text("foobar bam".into())]));
c(" foobar bam ",
Some(vec![
Component::WS,
Component::Text("foobar bam".into()),
Component::WS,
]));
c("\"foobar\"", Some(vec![Component::Text("foobar".into())]));
c("\"foobar\" \"bam\"", Some(vec![Component::Text("foobar bam".into())]));
c("\"foobar\" \"bam\"", Some(vec![Component::Text("foobar bam".into())]));
c(" \"foobar\" \"bam\" ",
Some(vec![
Component::WS,
Component::Text("foobar bam".into()),
Component::WS,
]));
c("\"foobar\"\"bam\"",
Some(vec![Component::Text("foobarbam".into())]));
c("\"foobar\"quuz\"bam\"",
Some(vec![Component::Text("foobarquuzbam".into())]));
c("\"foobar\"quuz \"bam\"",
Some(vec![Component::Text("foobarquuz bam".into())]));
c("\"foobar\"quuz \"bam\"",
Some(vec![Component::Text("foobarquuz bam".into())]));
c("\"foobar\"", Some(vec![Component::Text("foobar".into())]));
c("(foobar)", None);
c("(foobar) quux",
Some(vec![
Component::Comment("foobar".into()),
Component::WS,
Component::Text("quux".into())
]));
c("xyzzy (foobar) quux",
Some(vec![Component::Text("xyzzy".into()),
Component::WS,
Component::Comment("foobar".into()),
Component::WS,
Component::Text("quux".into())]));
c("foobar (comment) \"quoted string\"",
Some(vec![
Component::Text("foobar".into()),
Component::WS,
Component::Comment("comment".into()),
Component::WS,
Component::Text("quoted string".into())]));
c("foobar (comment) \" quoted string\"",
Some(vec![
Component::Text("foobar".into()),
Component::WS,
Component::Comment("comment".into()),
Component::WS,
Component::Text(" quoted string".into())]));
c("foobar bam quuz",
Some(vec![Component::Text("foobar bam quuz".into())]));
}
#[test]
fn dot_atom_parser() {
c!(grammar::DotAtomParser::new(), Vec<Component>);
c("f",
Some(vec![Component::Text("f".into())]));
c("foo",
Some(vec![Component::Text("foo".into())]));
c("f.o",
Some(vec![Component::Text("f.o".into())]));
c("foo.bar",
Some(vec![Component::Text("foo.bar".into())]));
c("foo.", None);
c("foo.bar.", None);
c("foo..bar", None);
c(".foo.bar", None);
c(".", None);
c("..", None);
c("foo bar", None);
c(" f",
Some(vec![
Component::WS,
Component::Text("f".into()),
]));
c(" f",
Some(vec![
Component::WS,
Component::Text("f".into()),
]));
c("f ",
Some(vec![
Component::Text("f".into()),
Component::WS,
]));
c("f ",
Some(vec![
Component::Text("f".into()),
Component::WS,
]));
c("(comment) f",
Some(vec![
Component::Comment("comment".into()),
Component::WS,
Component::Text("f".into()),
]));
c(" (comment) f",
Some(vec![
Component::WS,
Component::Comment("comment".into()),
Component::WS,
Component::Text("f".into()),
]));
c(" f (comment) ",
Some(vec![
Component::WS,
Component::Text("f".into()),
Component::WS,
Component::Comment("comment".into()),
Component::WS,
]));
c(" f (comment)",
Some(vec![
Component::WS,
Component::Text("f".into()),
Component::WS,
Component::Comment("comment".into()),
]));
}
#[test]
fn domain_literal_parser() {
c!(grammar::DomainLiteralParser::new(), Vec<Component>);
c("[foo]",
Some(vec![Component::Text("[foo]".into())]));
c("[\\[foo\\[]",
Some(vec![Component::Text("[[foo[]".into())]));
c("[foo.bar.com quux:biz]",
Some(vec![Component::Text("[foo.bar.com quux:biz]".into())]));
c(" \r\n [\r\n foo.bar.com \r\n quux:biz\r\n ]\r\n ",
Some(vec![
Component::WS,
Component::Text("[ foo.bar.com quux:biz ]".into()),
Component::WS,
]));
}
#[test]
fn or_other_parsers() {
fn e() -> ParseError<usize, String, LexicalError> {
ParseError::User { error: LexicalError::NoError }
}
struct Test<'a> {
input: &'a str,
output: Option<Vec<Component>>,
};
let tests : &[Test] = &[
Test {
input: "foo@bar.com",
output: Some(vec![Component::Address("foo@bar.com".into())])
},
Test {
input: "foo@bar",
output: Some(vec![Component::Address("foo@bar".into())])
},
Test {
input: "foo.bar@x",
output: Some(vec![Component::Address("foo.bar@x".into())])
},
Test {
input: "foo.bar@ß",
output: Some(vec![Component::Address("foo.bar@ß".into())])
},
Test {
input: "[@x",
output: Some(vec![
Component::InvalidAddress(e(), "[@x".into())
])
},
Test {
input: "[ß@x",
output: Some(vec![
Component::InvalidAddress(e(), "[ß@x".into())
])
},
Test {
input: "[@xß",
output: Some(vec![
Component::InvalidAddress(e(), "[@xß".into())
])
},
Test {
input: "[@xßℝ",
output: Some(vec![
Component::InvalidAddress(e(), "[@xßℝ".into())
])
},
Test {
input: "foo[@x",
output: Some(vec![
Component::InvalidAddress(e(), "foo[@x".into())
])
},
Test {
input: "(c)[@x",
output: Some(vec![
Component::InvalidAddress(e(), "(c)[@x".into())
])
},
Test {
input: "[(c)@x",
output: Some(vec![
Component::InvalidAddress(e(), "[(c)@x".into())
])
},
Test {
input: "[@(c)x",
output: Some(vec![
Component::InvalidAddress(e(), "[@(c)x".into())
])
},
Test {
input: "foo(c)[@x",
output: Some(vec![
Component::InvalidAddress(e(), "foo(c)[@x".into())
])
},
Test {
input: "foo[(c)@x",
output: Some(vec![
Component::InvalidAddress(e(), "foo[(c)@x".into())
])
},
Test {
input: "foo[@(c)x",
output: Some(vec![
Component::InvalidAddress(e(), "foo[@(c)x".into())
])
},
Test {
input: "[@x (c)",
output: Some(vec![
Component::InvalidAddress(e(), "[@x (c)".into())
])
},
Test {
input: "(c) foo[@x",
output: Some(vec![
Component::InvalidAddress(e(), "(c) foo[@x".into())
])
},
Test {
input: "foo[ (c)@x",
output: Some(vec![
Component::InvalidAddress(e(), "foo[ (c)@x".into())
])
},
Test {
input: "foo.bar@@dings",
output: Some(vec![
Component::InvalidAddress(e(), "foo.bar@@dings".into())
])
},
Test {
input: "foo.bar@x@dings",
output: Some(vec![
Component::InvalidAddress(e(), "foo.bar@x@dings".into())
])
},
Test {
input: "foo.bar (1)@@(2)dings",
output: Some(vec![
Component::InvalidAddress(e(), "foo.bar (1)@@(2)dings".into())
])
},
Test {
input: "foo.bar (1) @@ (2)dings",
output: Some(vec![
Component::InvalidAddress(e(), "foo.bar (1) @@ (2)dings".into())
])
},
Test {
input: "foo.bar(1)@x@dings",
output: Some(vec![
Component::InvalidAddress(e(), "foo.bar(1)@x@dings".into())
])
},
Test {
input: "foo.bar@(1)x@dings",
output: Some(vec![
Component::InvalidAddress(e(), "foo.bar@(1)x@dings".into())
])
},
Test {
input: "foo.bar@x(1)@dings",
output: Some(vec![
Component::InvalidAddress(e(), "foo.bar@x(1)@dings".into())
])
},
Test {
input: "foo.bar@x@(1)dings",
output: Some(vec![
Component::InvalidAddress(e(), "foo.bar@x@(1)dings".into())
])
},
Test {
input: "foo.bar@x@ (1) dings",
output: Some(vec![
Component::InvalidAddress(e(), "foo.bar@x@ (1) dings".into())
])
},
Test {
input: "ssh://user:pasword@example.org/resource",
output: Some(vec![
Component::InvalidAddress(
e(), "ssh://user:pasword@example.org/resource".into())
])
},
Test {
input: "(not a comment) ssh://user:pasword@example.org/resource",
output: Some(vec![
Component::InvalidAddress(
e(), "(not a comment) ssh://user:pasword@example.org/resource".into())
])
},
Test {
input: "shark://grrrr/39874293847092837443987492834",
output: Some(vec![
Component::InvalidAddress(
e(), "shark://grrrr/39874293847092837443987492834".into())
])
},
Test {
input: "shark://bait/8uyoi3lu4hl2..dfoif983j4b@%",
output: Some(vec![
Component::InvalidAddress(
e(), "shark://bait/8uyoi3lu4hl2..dfoif983j4b@%".into())
])
},
][..];
for t in tests.iter() {
{
c!(grammar::AddrSpecOrOtherParser::new(), Vec<Component>);
c(t.input.to_string(), t.output.clone())
}
{
c!(grammar::AngleAddrOrOtherParser::new(), Vec<Component>);
c(format!("<{}>", t.input), t.output.clone())
}
{
c!(grammar::NameAddrOrOtherParser::new(), Vec<Component>);
c(format!("Foo Bar <{}>", t.input),
t.output.clone().map(|mut x| {
x.insert(0, Component::WS);
x.insert(0, Component::Text("Foo Bar".into()));
x
}))
}
}
}
#[test]
fn angle_addr_parser() {
c!(grammar::AngleAddrParser::new(), Vec<Component>);
c("[foo]", None);
c("<foo@bar.com>", Some(vec![Component::Address("foo@bar.com".into())]));
c("<foo@bar>", Some(vec![Component::Address("foo@bar".into())]));
c("<foo.bar@x>", Some(vec![Component::Address("foo.bar@x".into())]));
c("<foo@bar>", Some(vec![Component::Address("foo@bar".into())]));
c("<foo@@bar>", None);
c("<f@oo@bar>", None);
c("<\"foo\"@bar.com>",
Some(vec![Component::Address("foo@bar.com".into())]));
c("<\"f\\\"oo\"@bar.com>",
Some(vec![Component::Address("f\"oo@bar.com".into())]));
c("<\"foo\".bar@x>", None);
c("<foo@[bar.com]>",
Some(vec![Component::Address("foo@[bar.com]".into())]));
c("<foo@[bar]>",
Some(vec![Component::Address("foo@[bar]".into())]));
c("<foo.bar@[x]>",
Some(vec![Component::Address("foo.bar@[x]".into())]));
c("<foo.bar@x.>", None);
c("< \r\n foo.bar@x>",
Some(vec![
Component::WS,
Component::Address("foo.bar@x".into()),
]));
c("< \r\n foo.bar \r\n @x>",
Some(vec![
Component::WS,
Component::Address("foo.bar@x".into())
]));
c("< (quuz) \r\n foo.bar@x>",
Some(vec![
Component::WS,
Component::Comment("quuz".into()),
Component::WS,
Component::Address("foo.bar@x".into()),
]));
c("< \r\n foo.bar \r\n @x>",
Some(vec![
Component::WS,
Component::Address("foo.bar@x".into()),
]));
c("<f \r\n oo.bar@x>", None);
c("<foo.bar@x \r\n >",
Some(vec![
Component::Address("foo.bar@x".into()),
Component::WS,
]));
c("<f \r\n oo.bar@x \r\n y>", None);
c(" <foo.bar@x> ",
Some(vec![
Component::WS,
Component::Address("foo.bar@x".into()),
Component::WS,
]));
c("< (Hello!) foo.bar@x \r\n >",
Some(vec![
Component::WS,
Component::Comment("Hello!".into()),
Component::WS,
Component::Address("foo.bar@x".into()),
Component::WS,
]));
c("< (Hello!) foo.bar (bye?) \r\n @x \r\n >",
Some(vec![
Component::WS,
Component::Comment("Hello!".into()),
Component::WS,
Component::Comment("bye?".into()),
Component::WS,
Component::Address("foo.bar@x".into()),
Component::WS,
]));
c("< (Hello!) foo.bar@x \r\n >",
Some(vec![
Component::WS,
Component::Comment("Hello!".into()),
Component::WS,
Component::Address("foo.bar@x".into()),
Component::WS,
]));
c("< x@ (Hello!) foo.bar (bye?) >",
Some(vec![
Component::WS,
Component::Address("x@foo.bar".into()),
Component::WS,
Component::Comment("Hello!".into()),
Component::WS,
Component::Comment("bye?".into()),
Component::WS,
]));
c("< (Hello!) \"f oo.bar\"@x \r\n >",
Some(vec![
Component::WS,
Component::Comment("Hello!".into()),
Component::WS,
Component::Address("f oo.bar@x".into()),
Component::WS,
]));
}
#[test]
fn name_addr_parser() {
c!(grammar::NameAddrParser::new(), Vec<Component>);
c("foo@example.org", None);
c("<\"\"@example.org>",
Some(vec![ Component::Address("@example.org".into()) ]));
c("Willi Wonka <\"\"@無.com>",
Some(vec![ Component::Text("Willi Wonka".into()),
Component::WS,
Component::Address("@無.com".into()) ]));
}
#[test]
fn display_name_parser() {
c!(grammar::DisplayNameParser::new(), Vec<Component>);
c("Willi Wonka",
Some(vec![ Component::Text("Willi Wonka".into()) ]));
c("Willi A. Wonka",
Some(vec![ Component::Text("Willi A. Wonka".into()) ]));
c("foo@example.org",
Some(vec![ Component::Text("foo@example.org".into()) ]));
}
#[test]
fn name_addr_api() {
fn c_(name: Option<&str>, comment: Option<&str>, email: Option<&str>)
{
eprintln!("checking: name: {:?}, comment: {:?}, email: {:?}",
name, comment, email);
let na = NameAddr::new(name, comment, email).unwrap();
assert_eq!(na.name(), name);
assert_eq!(na.comment(), comment);
assert_eq!(na.address(), email);
}
fn c(name: &str, comment: &str, email: &str)
{
c_(Some(name), Some(comment), Some(email));
c_(Some(name), None, Some(email));
c_(None, None, Some(email));
}
c("Harold Hutchins", "(artist)", "harold.hutchins@captain-underpants.com");
c("Mr. Meaner", "(Gym Teacher)", "kenny@jerome-horwitz.k12.us");
c("foo@bar.com", "display name with at", "foo@bar.com");
}
#[test]
fn name_addr_or_other_api() {
fn c_(name: Option<&str>, comment: Option<&str>,
email: Option<&str>, valid: bool)
{
eprintln!("checking: name: {:?}, comment: {:?}, email: {:?}",
name, comment, email);
let na = NameAddrOrOther::new(name, comment, email).unwrap();
assert_eq!(na.name(), name);
assert_eq!(na.comment(), comment);
if let Some(email) = email {
if valid {
assert_eq!(na.address().unwrap(), email);
assert!(na.other().is_none());
} else {
assert!(na.address().is_err());
assert_eq!(na.other().unwrap(), email);
}
}
}
fn c(name: &str, comment: &str, email: &str, valid: bool)
{
c_(Some(name), Some(comment), Some(email), valid);
c_(Some(name), None, Some(email), valid);
c_(None, None, Some(email), valid);
}
c("Harold Hutchins", "(artist)", "harold.hutchins@captain-underpants.com", true);
c("Mr. Meaner", "(Gym Teacher)", "kenny@jerome-horwitz.k12.us", true);
c("Mr. Meaner", "(Gym Teacher)", "ssh://nas.jerome-horwitz.k12.us", false);
}
#[test]
fn addr_spec_api() {
fn c(email: &str, ok: bool)
{
match AddrSpec::new(email) {
Ok(ref a) if ok => assert_eq!(a.address(), email),
Ok(ref a) if !ok =>
panic!("Expected parser to fail for '{:?}': got '{:?}'",
email, a),
Err(ref err) if ok =>
panic!("Expected parser to succeed for '{:?}': {:?}",
email, err),
Err(_) if !ok => (),
_ => unreachable!(),
}
}
c("example@foo.com", true);
c("<example@foo.com>", false);
c("example@@foo.com", false);
}
#[test]
fn addr_spec_or_other_api() {
fn c(email: &str, ok: bool)
{
match AddrSpecOrOther::new(email) {
Ok(ref a) if ok => {
assert_eq!(a.address().unwrap(), email);
assert_eq!(a.other(), None);
}
Ok(ref a) if !ok => {
assert!(a.address().is_err());
assert_eq!(a.other(), Some(email));
}
Err(ref err) if ok =>
panic!("Expected parser to succeed for '{:?}': {:?}",
email, err),
Err(_) if !ok => (),
_ => unreachable!(),
}
}
c("example@foo.com", true);
c("<example@foo.com>", false);
c("example@@foo.com", false);
}
#[test]
fn name_escape_test() {
fn c(raw: &str, escaped_expected: &str) {
eprintln!("\nInput: {:?}", raw);
eprintln!("Expecting escaped version to be: {:?}", escaped_expected);
let escaped_got = Name::escaped(raw).expect("Parse error");
eprintln!(" Escaped version is: {:?}", escaped_got);
assert_eq!(escaped_got, escaped_expected);
let lexer = lexer::Lexer::new(&escaped_got);
let raw_got = grammar::DisplayNameParser::new()
.parse(&escaped_got, lexer)
.expect(&format!("Parse error: {}", escaped_got));
eprintln!("Parsing escaped version, got: {:?}", raw_got);
assert_eq!(raw_got, vec![ Component::Text(raw.to_string()) ]);
}
c("Foo Q. Bar", r#""Foo Q. Bar""#);
c(r#""Foo Q. Bar""#, r#""\"Foo Q. Bar\"""#);
c(r#""Foo Q Bar""#, r#""\"Foo Q Bar\"""#);
c("Foo, the Bar", r#""Foo, the Bar""#);
c(" Foo Bar", r#"" Foo Bar""#);
c("Foo Bar ", r#""Foo Bar ""#);
}
}