use std::fmt;
use soft_ascii_string::SoftAsciiStr;
use internals::grammar::is_ftext;
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct HeaderName {
name: &'static SoftAsciiStr
}
impl HeaderName {
pub fn new( name: &'static SoftAsciiStr ) -> Result<Self, InvalidHeaderName> {
HeaderName::validate_name( name )?;
Ok( HeaderName { name } )
}
pub fn from_ascii_unchecked<B: ?Sized>( name: &'static B ) -> HeaderName
where B: AsRef<str>
{
HeaderName { name: SoftAsciiStr::from_unchecked( name.as_ref() ) }
}
#[inline(always)]
pub fn as_ascii_str( &self ) -> &'static SoftAsciiStr {
self.name
}
#[inline(always)]
pub fn as_str( &self ) -> &'static str {
self.name.as_str()
}
}
impl fmt::Display for HeaderName {
fn fmt(&self, fter: &mut fmt::Formatter) -> fmt::Result {
write!(fter, "{}", self.as_str())
}
}
impl PartialEq<str> for HeaderName {
fn eq(&self, other: &str) -> bool {
self.name.as_str() == other
}
}
impl PartialEq<SoftAsciiStr> for HeaderName {
fn eq(&self, other: &SoftAsciiStr) -> bool {
self.name == other
}
}
impl HeaderName {
fn validate_name(name: &SoftAsciiStr) -> Result<(), InvalidHeaderName> {
let mut begin_of_word = true;
if name.len() < 1 {
return Err(InvalidHeaderName { invalid_name: name.to_owned().into() });
}
for ch in name.as_str().chars() {
if !is_ftext( ch ) {
return Err(InvalidHeaderName { invalid_name: name.to_owned().into() });
}
match ch {
'a'...'z' => {
if begin_of_word {
return Err(InvalidHeaderName { invalid_name: name.to_owned().into() });
}
},
'A'...'Z' => {
if begin_of_word {
begin_of_word = false;
} else {
return Err(InvalidHeaderName { invalid_name: name.to_owned().into() });
}
},
'0'...'9' => {
begin_of_word = false;
},
ch => {
if ch < '!' || ch > '~' || ch == ':' {
return Err(InvalidHeaderName { invalid_name: name.to_owned().into() });
}
begin_of_word = true;
}
}
}
Ok( () )
}
}
#[derive(Clone, Debug, Fail)]
#[fail(display = "given name is not a valid header name: {:?}", invalid_name)]
pub struct InvalidHeaderName {
invalid_name: String
}
pub trait HasHeaderName {
fn get_name(&self) -> HeaderName;
}
impl HasHeaderName for HeaderName {
fn get_name(&self) -> HeaderName {
*self
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn valide_header_names() {
let valid_cases = &[
"Date",
"Some-Header",
"33",
"Some34",
"-33-",
"---",
"<3+Who-Cares&44",
"(3*4=12)^[{~}]"
];
for case in valid_cases.iter() {
assert_ok!(
HeaderName::validate_name( SoftAsciiStr::from_str( case ).unwrap() ) );
}
}
#[test]
fn invalide_header_names() {
let invalid_cases = &[
"ID",
"DaD",
"ans",
"all-lower-calse",
"ALL-UPPER-CASE",
"",
"a:b",
":",
"-:-",
"Message Id",
" Leading-Ws",
"Message\tId",
"Null\0Msg"
];
for case in invalid_cases.iter() {
assert_err!( HeaderName::validate_name( SoftAsciiStr::from_str( case ).unwrap() ), case );
}
}
}