use std::convert::TryFrom;
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub struct Domain {
name: String,
}
impl Domain {
pub const MAX_LABEL_LENGTH: usize = 63;
pub const MAX_NAME_LENGTH: usize = 253;
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;
}
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 {
pub fn name(&self) -> &String {
&self.name
}
}
impl TryFrom<&[u8]> for Domain {
type Error = &'static str;
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 {
fn to_string(&self) -> String {
self.name.clone()
}
}