use ::MailType;
#[inline(always)]
pub fn is_ftext(ch: char) -> bool {
let bch = ch as u32;
bch > 32 && bch < 127 && ch != ':'
}
#[inline(always)]
pub fn is_ws(ch: char) -> bool {
ch == ' ' || ch == '\t'
}
#[inline(always)]
pub fn is_space(ch: char) -> bool {
ch == ' '
}
#[inline(always)]
pub fn is_ascii(ch: char) -> bool {
(ch as u32) < 128
}
#[inline(always)]
pub fn is_ascii_vchar(ch: char) -> bool {
let u32_ch = ch as u32;
32 < u32_ch && u32_ch <= 126
}
#[inline(always)]
pub fn is_vchar(ch: char, mt: MailType) -> bool {
is_ascii_vchar(ch) || (mt == MailType::Internationalized && !is_ascii(ch))
}
#[inline(always)]
pub fn is_quotable(ch: char, tp: MailType) -> bool {
is_vchar(ch, tp) || is_ws(ch)
}
#[inline(always)]
pub fn is_any_whitespace(ch: char) -> bool {
ch.is_whitespace()
}
pub fn is_ctext(ch: char, mt: MailType) -> bool {
match ch {
'!'...'\'' |
'*'...'[' |
']'...'~' => true,
_ => mt == MailType::Internationalized && !is_ascii( ch )
}
}
pub fn is_special(ch: char) -> bool {
match ch {
'(' | ')' |
'<' | '>' |
'[' | ']' |
':' | ';' |
'@' | '\\'|
',' | '.' |
'"' => true,
_ => false
}
}
pub fn is_tspecial(ch: char) -> bool {
match ch {
'(' | ')' |
'<' | '>' |
'@' | ',' |
';' | ':' |
'\\'| '"' |
'/' | '[' |
']' | '?' |
'=' => true,
_ => false
}
}
#[inline(always)]
pub fn is_atext(ch: char, tp: MailType) -> bool {
is_vchar(ch, tp) && !is_special(ch)
}
#[inline(always)]
pub fn is_dtext(ch: char , mt: MailType) -> bool {
match ch as u32 {
33...90 |
94...126 => true,
_ => mt == MailType::Internationalized && !is_ascii(ch)
}
}
pub fn is_qtext(ch: char, mt: MailType) -> bool {
match ch {
'!' |
'#'...'[' |
']'...'~' => true,
_ => mt == MailType::Internationalized && !is_ascii(ch)
}
}
#[inline(always)]
pub fn is_ctl(ch: char) -> bool {
(ch as u32) < 32
}
#[inline(always)]
pub fn is_token_char(ch: char) -> bool {
is_ascii(ch) && !is_ctl(ch) && !is_tspecial(ch) && ch != ' '
}
#[inline(always)]
pub fn is_especial(ch: char) -> bool {
match ch {
'(' | ')' |
'<' | '>' |
'@' | ',' |
';' | ':' |
'"' | '/'|
'[' | ']' |
'?' | '.' |
'=' => true,
_ => false
}
}
pub fn is_token(s: &str) -> bool {
0 < s.len() && s.chars().all(is_token_char)
}
pub mod encoded_word {
use nom;
use ::MailType;
use ::error::{EncodingError, EncodingErrorKind};
use super::{ is_especial, is_ascii_vchar };
pub const MAX_ECW_LEN: usize = 75;
pub const ECW_SEP_OVERHEAD: usize = 6;
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
pub enum EncodedWordContext {
Phrase,
Text,
Comment
}
impl EncodedWordContext {
fn char_validator( &self ) -> fn(char) -> bool {
use self::EncodedWordContext::*;
match *self {
Phrase => valid_char_in_ec_in_phrase,
Text => is_encoded_word_char,
Comment => valid_char_in_ec_in_comment,
}
}
}
pub fn is_encoded_word(word: &str, ctx: EncodedWordContext, mail_type: MailType) -> bool {
try_parse_encoded_word_parts(word, ctx, mail_type).is_ok()
}
pub fn try_parse_encoded_word_parts(
word: &str,
ctx: EncodedWordContext,
mail_type: MailType
) -> Result<(&str, &str, &str), EncodingError>
{
let char_validator = ctx.char_validator();
let res = do_parse!(
word,
char!( '=' ) >>
char!( '?' ) >>
charset: take_while!( is_ew_token_char ) >>
char!( '?' ) >>
encoding: take_while!( is_ew_token_char ) >>
char!( '?' ) >>
text: take_while!( char_validator ) >>
char!( '?' ) >>
char!( '=' ) >>
eof!() >>
(charset, encoding, text)
);
match res {
nom::IResult::Done( rest, result ) => {
assert_eq!(rest.len(), 0, "[BUG] used nom::eof!() but rest.len() > 0");
Ok( result )
},
nom::IResult::Incomplete( .. ) => {
return Err((EncodingErrorKind::Malformed, mail_type).into());
}
nom::IResult::Error( .. ) => {
return Err((EncodingErrorKind::Malformed, mail_type).into());
}
}
}
fn is_encoded_word_char(ch: char) -> bool {
is_ascii_vchar(ch) && ch != '?'
}
fn valid_char_in_ec_in_comment(ch: char) -> bool {
is_encoded_word_char(ch) && !(ch == '(' || ch == ')' || ch == '"')
}
fn valid_char_in_ec_in_phrase(ch: char) -> bool {
match ch {
'0'...'9' |
'a'...'z' |
'A'...'Z' |
'!' | '*' |
'+' | '-' |
'/' | '=' |
'_' => true,
_ => false
}
}
fn is_ew_token_char(ch: char) -> bool {
is_ascii_vchar(ch) && !is_especial(ch)
}
}
pub fn is_quoted_string(qstr: &str, tp: MailType) -> bool {
let mut iter = qstr.chars();
if let Some('"') = iter.next() {} else { return false }
let mut next = iter.next();
while let Some(ch) = next {
match ch {
'\\' => {
if let Some(next_char) = iter.next() {
if !(is_vchar(next_char, tp) || is_ws(next_char)) {
return false;
}
} else {
return false;
}
},
'"' => {
if iter.next().is_none() {
return true;
} else {
return false;
}
}
ch => {
if !is_qtext(ch, tp) {
return false
}
}
}
next = iter.next()
}
return false;
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn _is_ascii_vchar() {
assert_eq!(false, is_ascii_vchar('\x7f'));
for bad_char in b'\0'..b' ' {
if is_ascii_vchar(bad_char as char) {
panic!("{:?} should not be a VCHAR", bad_char);
}
}
for good_char in b'!'..(b'~'+1) {
if !is_ascii_vchar(good_char as char) {
panic!("{:?} should be a VCHAR", good_char as char);
}
}
}
#[test]
fn htap_is_ctl_space_is_not() {
assert_eq!(true, is_ctl('\t'));
assert_eq!(false, is_ctl(' '));
}
#[test]
fn is_toke_empty() {
assert_eq!(false, is_token(""));
}
}