inet 0.1.1

This library aids in internet processing.
Documentation
use std::convert::TryFrom;

/// Represents a domain name.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub struct Domain {
    name: String,
}

impl Domain {

    /// The maximum length of a domain label.
    pub const MAX_LABEL_LENGTH: usize = 63;

    /// The maximum length of a domain name.
    pub const MAX_NAME_LENGTH: usize = 253;

    /// Checks if the domain label is valid.
    ///
    /// ```
    /// use inet::domain::Domain;
    ///
    /// assert!(!Domain::is_valid_label("".as_bytes()));
    /// assert!(Domain::is_valid_label("a".as_bytes()));
    /// assert!(!Domain::is_valid_label("a.b".as_bytes()));
    /// assert!(!Domain::is_valid_label("a-".as_bytes()));
    /// assert!(!Domain::is_valid_label("-a".as_bytes()));
    /// assert!(Domain::is_valid_label("a-b".as_bytes()));
    /// ```
    pub fn is_valid_label(label: &[u8]) -> bool {
        if (1..Domain::MAX_LABEL_LENGTH).contains(&label.len()) {
            for (i, r) in label.iter().enumerate() {
                let c: u8 = *r;
                if c.is_ascii_alphanumeric() {
                    continue;
                } else if c == b'-' {
                    if i == 0 || i == label.len() - 1 {
                        return false;
                    } else if label[i-1] == b'-' || label[i+1] == b'-' {
                        return false;
                    }
                    continue;
                } else {
                    return false;
                }
            }
            return true;
        }
        return false;
    }

    /// Checks if the domain name is valid.
    ///
    /// ```
    /// use inet::domain::Domain;
    /// assert!(Domain::is_valid_name("a".as_bytes()));
    /// assert!(Domain::is_valid_name("a.b".as_bytes()));
    /// assert!(Domain::is_valid_name("a.b.c".as_bytes()));
    /// assert!(!Domain::is_valid_name("a.".as_bytes()));
    /// assert!(!Domain::is_valid_name(".a".as_bytes()));
    /// assert!(!Domain::is_valid_name("".as_bytes()));
    /// assert!(!Domain::is_valid_name("".as_bytes()));
    /// ```
    pub fn is_valid_name(name: &[u8]) -> bool {
        if (1..Domain::MAX_NAME_LENGTH).contains(&name.len()) {
            let mut rem: &[u8] = name;
            loop {
                match rem.iter().position(|r| *r == b'.') {
                    None => return Domain::is_valid_label(rem),
                    Some(i) => {
                        if !Domain::is_valid_label(&rem[..i]) {
                            return false;
                        }
                        rem = &rem[i+1..];
                    }
                }
            }
        }
        return false;
    }
}

impl Domain {

    /// Gets the name.
    ///
    /// ```
    /// use inet::domain::Domain;
    /// use std::convert::TryFrom;
    ///
    /// let domain: Domain = Domain::try_from("localhost".as_bytes()).ok().unwrap();
    /// assert_eq!(domain.name(), "localhost");
    /// ```
    pub fn name(&self) -> &String {
        &self.name
    }
}

impl TryFrom<&[u8]> for Domain {
    type Error = &'static str;

    /// ```
    /// use inet::domain::Domain;
    /// use std::convert::TryFrom;
    ///
    /// let domain: Domain = Domain::try_from("localhost".as_bytes()).ok().unwrap();
    /// assert_eq!(Domain::try_from("localhost".as_bytes()), Ok(domain.clone()));
    /// assert_eq!(Domain::try_from("LocalHost".as_bytes()), Ok(domain.clone()));
    /// assert_eq!(Domain::try_from("".as_bytes()), Err("Invalid Domain Name"));
    /// ```
    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
        if Domain::is_valid_name(value) {
            let lower: Vec<u8> = value.to_ascii_lowercase();
            let name: String = unsafe { String::from_utf8_unchecked(lower) };
            return Ok(Domain{ name });
        }
        return Err("Invalid Domain Name");
    }
}

impl ToString for Domain {

    /// ```
    /// use inet::domain::Domain;
    /// use std::convert::TryFrom;
    ///
    /// let domain: Domain = Domain::try_from("localhost".as_bytes()).ok().unwrap();
    /// assert_eq!(&domain.to_string(), domain.name());
    /// ```
    fn to_string(&self) -> String {
        self.name.clone()
    }
}