ircv3_parse 4.0.0

Zero-copy parser for IRCv3 messages
Documentation
use crate::compat::{Display, FmtResult, Formatter};

use crate::{error::HostnameError, HYPEN};

pub struct RFC1123 {
    max_len: usize,
    max_segment_len: usize,
    max_depth: usize,
}

impl Default for RFC1123 {
    fn default() -> Self {
        Self {
            max_len: 253,
            max_segment_len: 63,
            max_depth: 10,
        }
    }
}

impl RFC1123 {
    #[inline]
    pub fn new() -> Self {
        Self::default()
    }

    pub fn validate(&self, input: &str) -> Result<(), HostnameError> {
        if input.is_empty() {
            return Err(HostnameError::Empty);
        }

        if input.len() > self.max_len {
            return Err(HostnameError::LabelTooLong {
                max: self.max_len,
                actual: input.len(),
            });
        }

        for (i, segment) in input.split('.').enumerate() {
            if self.max_depth < i {
                return Err(HostnameError::TooManyLabels {
                    max: self.max_depth,
                    actual: i,
                });
            }
            self.validate_segment(segment)?;
        }

        Ok(())
    }

    fn validate_segment(&self, segment: &str) -> Result<(), HostnameError> {
        if segment.is_empty() {
            return Err(HostnameError::Empty);
        }

        if segment.len() > self.max_segment_len {
            return Err(HostnameError::LabelTooLong {
                max: self.max_segment_len,
                actual: segment.len(),
            });
        }

        let bytes = segment.as_bytes();

        if !self.start_with(bytes[0]) {
            return Err(HostnameError::InvalidFirstChar {
                char: bytes[0] as char,
            });
        }

        if bytes.len() > 1 && self.end_with(bytes[bytes.len() - 1]) {
            return Err(HostnameError::InvalidLastChar {
                char: bytes[bytes.len() - 1] as char,
            });
        }

        for &c in bytes {
            if !self.validate_char(c) {
                return Err(HostnameError::InvalidChar { char: c as char });
            }
        }

        Ok(())
    }

    #[inline]
    fn start_with(&self, c: u8) -> bool {
        c.is_ascii_alphanumeric()
    }

    #[inline]
    fn end_with(&self, c: u8) -> bool {
        c == HYPEN
    }

    #[inline]
    fn validate_char(&self, c: u8) -> bool {
        c.is_ascii_alphanumeric() || c == HYPEN
    }
}

impl Display for RFC1123 {
    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
        write!(f, "RFC1123")
    }
}