Add Host struct with tests; Add Request struct (unfinished);
This commit is contained in:
parent
62eeb9d627
commit
30f78990b1
4 changed files with 192 additions and 0 deletions
166
src/host.rs
Normal file
166
src/host.rs
Normal 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");
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,5 @@
|
||||||
|
pub mod host;
|
||||||
|
pub mod request;
|
||||||
pub mod response;
|
pub mod response;
|
||||||
pub mod status;
|
pub mod status;
|
||||||
|
|
||||||
|
|
15
src/request.rs
Normal file
15
src/request.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -92,5 +92,14 @@ mod tests {
|
||||||
let response = "20 message delivered\n".parse::<Response>();
|
let response = "20 message delivered\n".parse::<Response>();
|
||||||
assert_eq!(response, Err(ParseResponseError::Malformed));
|
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!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue