view src/resolv_addr.rs @ 5:27456533853e

Attempt to setup CI using GitHub Actions This sets up CI to test the crate on Ubuntu, Windows and macOS. It's expected to fail on Windows now and it will be fixed after we know which things need to be configured-away.
author Martin Habovštiak <martin.habovstiak@gmail.com>
date Fri, 27 Nov 2020 15:18:25 +0100
parents 66c0e10c89fc
children cfef4593e207
line wrap: on
line source

use thiserror::Error;
use std::fmt;

#[derive(Debug, PartialEq)]
pub(crate) struct ResolvAddr(String);

impl ResolvAddr {
    pub(crate) fn as_str(&self) -> &str {
        &self.0
    }

    pub(crate) fn try_from_generic<T: std::ops::Deref<Target=str> + Into<String>>(string: T) -> Result<Self, ResolvAddrError> {
        // can't use a combinator due to borrowing
        let colon = match string.rfind(':') {
            Some(colon) => colon,
            None => return Err(ResolvAddrError::MissingPort(string.into())),
        };

        let (hostname, port) = string.split_at(colon);

        if let Err(error) = port[1..].parse::<u16>() {
            return Err(ResolvAddrError::InvalidPort { string: string.into(), error, });
        }

        let len = hostname.len();
        if len > 253 {
            return Err(ResolvAddrError::TooLong { string: string.into(), len, } )
        }

        let mut label_start = 0usize;

        for (i, c) in hostname.chars().enumerate() {
            match c {
                '.' => {
                    if i - label_start == 0 {
                        return Err(ResolvAddrError::EmptyLabel { string: string.into(), label_start, });
                    }

                    label_start = i + 1;
                },
                'a'..='z' | 'A'..='Z' | '0'..='9' | '-' => (),
                _ => return Err(ResolvAddrError::InvalidCharacter { string: string.into(), c, pos: i, }),
            }

            if i - label_start > 63 {
                return Err(ResolvAddrError::LongLabel { string: string.into(), label_start, label_end: i, });
            }
        }

        Ok(ResolvAddr(string.into()))
    }
}

impl fmt::Display for ResolvAddr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&self.0, f)
    }
}


#[derive(Debug, Error)]
pub(crate) enum ResolvAddrError {
    #[error("hostname {string} has {len} character which exceeds the limit of 253")]
    TooLong { string: String, len: usize },
    #[error("invalid character {c} in hostname {string} at position {pos}")]
    InvalidCharacter { string: String, pos: usize, c: char, },
    #[error("hostname {string} contains a label {} at position {label_start} which is {} characters long - more than the limit 63", &string[(*label_start)..(*label_end)], label_end - label_start)]
    LongLabel { string: String, label_start: usize, label_end: usize, },
    #[error("hostname {string} contains an empty label at position {label_start}")]
    EmptyLabel { string: String, label_start: usize, },
    #[error("the address {0} is missing a port")]
    MissingPort(String),
    #[error("failed to parse port numer in the address {string}")]
    InvalidPort { string: String, error: std::num::ParseIntError, },
}