diff options
Diffstat (limited to '')
| -rw-r--r-- | src/jid/mod.rs | 182 | ||||
| -rw-r--r-- | src/lib.rs | 16 | 
2 files changed, 198 insertions, 0 deletions
| diff --git a/src/jid/mod.rs b/src/jid/mod.rs new file mode 100644 index 0000000..c6c157f --- /dev/null +++ b/src/jid/mod.rs @@ -0,0 +1,182 @@ +use std::{ +    net::{Ipv4Addr, Ipv6Addr}, +    str::FromStr, +}; + +#[derive(PartialEq, Debug)] +struct JID { +    // TODO: validate localpart (length, char] +    localpart: Option<String>, +    domainpart: Domainpart, +    resourcepart: Option<String>, +} + +#[derive(PartialEq, Debug)] +enum Domainpart { +    IPLiteral(Ipv6Addr), +    IPv4Address(Ipv4Addr), +    // TODO: domain name type, not string +    IFQDN(String), +} + +impl FromStr for Domainpart { +    type Err = DomainpartParseError; + +    fn from_str(s: &str) -> Result<Self, Self::Err> { +        match s.parse::<Ipv6Addr>() { +            Ok(ip) => Ok(Domainpart::IPLiteral(ip)), +            Err(_) => match s.parse::<Ipv4Addr>() { +                Ok(ip) => Ok(Domainpart::IPv4Address(ip)), +                Err(_) => Ok(Domainpart::IFQDN(s.to_owned())), +            }, +        } +    } +} + +impl TryFrom<String> for Domainpart { +    type Error = DomainpartParseError; + +    fn try_from(value: String) -> Result<Self, Self::Error> { +        value.parse() +    } +} + +#[derive(Debug)] +enum DomainpartParseError {} + +#[derive(Debug)] +enum JIDParseError { +    Empty, +    Domainpart(DomainpartParseError), +    Malformed, +} + +impl JID { +    fn new(localpart: Option<String>, domainpart: String, resourcepart: Option<String>) -> Self { +        Self { +            localpart, +            domainpart: domainpart.parse().unwrap(), +            resourcepart, +        } +    } + +    fn validate(&self) -> bool { +        todo!() +    } +} + +impl FromStr for JID { +    type Err = JIDParseError; + +    fn from_str(s: &str) -> Result<Self, Self::Err> { +        let split: Vec<&str> = s.split('@').collect(); +        match split.len() { +            0 => Err(JIDParseError::Empty), +            1 => { +                let split: Vec<&str> = split[0].split('/').collect(); +                match split.len() { +                    1 => Ok(JID::new(None, split[0].to_string(), None)), +                    2 => Ok(JID::new( +                        None, +                        split[0].to_string(), +                        Some(split[1].to_string()), +                    )), +                    _ => Err(JIDParseError::Malformed), +                } +            } +            2 => { +                let split2: Vec<&str> = split[1].split('/').collect(); +                match split2.len() { +                    1 => Ok(JID::new( +                        Some(split[0].to_string()), +                        split2[0].to_string(), +                        None, +                    )), +                    2 => Ok(JID::new( +                        Some(split[0].to_string()), +                        split2[0].to_string(), +                        Some(split2[1].to_string()), +                    )), +                    _ => Err(JIDParseError::Malformed), +                } +            } +            _ => Err(JIDParseError::Malformed), +        } +    } +} + +impl TryFrom<String> for JID { +    type Error = JIDParseError; + +    fn try_from(value: String) -> Result<Self, Self::Error> { +        value.parse() +    } +} + +impl std::fmt::Display for JID { +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +        write!( +            f, +            "{}{}{}", +            self.localpart.clone().map(|l| l + "@").unwrap_or_default(), +            match &self.domainpart { +                Domainpart::IPLiteral(addr) => addr.to_string(), +                Domainpart::IPv4Address(addr) => addr.to_string(), +                Domainpart::IFQDN(domain) => domain.to_owned(), +            }, +            self.resourcepart +                .clone() +                .map(|r| "/".to_owned() + &r) +                .unwrap_or_default() +        ) +    } +} + +#[cfg(test)] +mod tests { +    use super::*; + +    #[test] +    fn jid_to_string() { +        assert_eq!( +            JID::new(Some("cel".into()), "blos.sm".into(), None).to_string(), +            "cel@blos.sm".to_owned() +        ); +    } + +    #[test] +    fn parse_full_jid() { +        assert_eq!( +            "cel@blos.sm/greenhouse".parse::<JID>().unwrap(), +            JID::new( +                Some("cel".into()), +                "blos.sm".into(), +                Some("greenhouse".into()) +            ) +        ) +    } + +    #[test] +    fn parse_bare_jid() { +        assert_eq!( +            "cel@blos.sm".parse::<JID>().unwrap(), +            JID::new(Some("cel".into()), "blos.sm".into(), None) +        ) +    } + +    #[test] +    fn parse_domain_jid() { +        assert_eq!( +            "component.blos.sm".parse::<JID>().unwrap(), +            JID::new(None, "component.blos.sm".into(), None) +        ) +    } + +    #[test] +    fn parse_full_domain_jid() { +        assert_eq!( +            "component.blos.sm/bot".parse::<JID>().unwrap(), +            JID::new(None, "component.blos.sm".into(), Some("bot".into())) +        ) +    } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..b2ce0b2 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,16 @@ +mod jid; + +pub fn add(left: usize, right: usize) -> usize { +    left + right +} + +#[cfg(test)] +mod tests { +    use super::*; + +    #[test] +    fn it_works() { +        let result = add(2, 2); +        assert_eq!(result, 4); +    } +} | 
