Add Host struct with tests; Add Request struct (unfinished);

This commit is contained in:
Nathan Fisher 2023-05-22 10:12:28 -04:00
parent 62eeb9d627
commit 30f78990b1
4 changed files with 192 additions and 0 deletions

166
src/host.rs Normal file
View file

@ -0,0 +1,166 @@
use std::{fmt, str::FromStr};
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Host {
pub subdomain: Option<String>,
pub domain: String,
pub tld: String,
}
impl fmt::Display for Host {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(ref sub) = self.subdomain {
write!(f, "{sub}.{}.{}", self.domain, self.tld)
} else {
write!(f, "{}.{}", self.domain, self.tld)
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum ParseHostError {
MissingSeparator,
EmptyDomain,
EmptyTld,
EmptySubdomain,
IllegalWhitespace,
}
impl fmt::Display for ParseHostError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{self:?}")
}
}
impl std::error::Error for ParseHostError {}
impl FromStr for Host {
type Err = ParseHostError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.contains(|x: char| x.is_whitespace()) {
return Err(ParseHostError::IllegalWhitespace);
}
if let Some((domain, tld)) = s.rsplit_once('.') {
if domain.is_empty() {
Err(ParseHostError::EmptyDomain)
} else if tld.is_empty() {
Err(ParseHostError::EmptyTld)
} else if let Some((subdomain, domain)) = domain.rsplit_once('.') {
if subdomain.is_empty() {
Err(ParseHostError::EmptySubdomain)
} else if domain.is_empty() {
Err(ParseHostError::EmptyDomain)
} else {
Ok(Host {
subdomain: Some(subdomain.to_string()),
domain: domain.to_string(),
tld: tld.to_string()
})
}
} else {
Ok(Host {
subdomain: None,
domain: domain.to_string(),
tld: tld.to_string(),
})
}
} else {
Err(ParseHostError::MissingSeparator)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_missing_separator() {
assert_eq!(
"exampledotcom".parse::<Host>(),
Err(ParseHostError::MissingSeparator)
);
}
#[test]
fn parse_empty_tld() {
assert_eq!(
"example.".parse::<Host>(),
Err(ParseHostError::EmptyTld)
);
}
#[test]
fn parse_empty_domain() {
assert_eq!(
".com".parse::<Host>(),
Err(ParseHostError::EmptyDomain)
);
assert_eq!(
"example..com".parse::<Host>(),
Err(ParseHostError::EmptyDomain)
);
}
#[test]
fn parse_empty_subdomain() {
assert_eq!(
".example.com".parse::<Host>(),
Err(ParseHostError::EmptySubdomain)
);
}
#[test]
fn parse_illegal_whitespace() {
assert_eq!(
"exam\tple.com".parse::<Host>(),
Err(ParseHostError::IllegalWhitespace)
);
}
#[test]
fn parse_host() {
assert!("example.com".parse::<Host>().is_ok());
}
#[test]
fn parse_host_with_subdomain() {
let host: Host = "mail.example.com".parse().unwrap();
assert_eq!(
host.subdomain,
Some(String::from("mail"))
);
}
#[test]
fn parse_complex_subdomain() {
let host: Host = "mail.sender.example.com".parse().unwrap();
assert_eq!(
host.subdomain,
Some(String::from("mail.sender"))
);
}
#[test]
fn fmt() {
let host = Host {
subdomain: None,
domain: String::from("example"),
tld: String::from("com")
};
let host_str = host.to_string();
assert_eq!(host_str.as_str(), "example.com");
}
#[test]
fn fmt_with_subdomain() {
let host = Host {
subdomain: Some(String::from("mail")),
domain: String::from("example"),
tld: String::from("com")
};
let host_str = host.to_string();
assert_eq!(host_str.as_str(), "mail.example.com");
}
}

View file

@ -1,3 +1,5 @@
pub mod host;
pub mod request;
pub mod response;
pub mod status;

15
src/request.rs Normal file
View file

@ -0,0 +1,15 @@
use crate::host::{Host, ParseHostError};
use std::{fmt, str::FromStr};
#[derive(Clone, Debug, PartialEq)]
pub struct Request {
user: String,
host: Host,
message: String,
}
impl fmt::Display for Request {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "misfin://{}@{} {}\r\n", self.user, self.host, self.message)
}
}

View file

@ -92,5 +92,14 @@ mod tests {
let response = "20 message delivered\n".parse::<Response>();
assert_eq!(response, Err(ParseResponseError::Malformed));
}
#[test]
fn parse_badint() {
let response = "twenty message deliverred\r\n".parse::<Response>();
match response {
Err(ParseResponseError::ParseInt(_)) => {},
_ => panic!(),
}
}
}