use crate::domain_verify::DNS_RECORD_PREFIX;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Host<'a>(&'a str);
impl<'a> Host<'a> {
pub fn new(raw: &'a str) -> Self {
Self(raw)
}
pub fn as_raw(&self) -> &'a str {
self.0
}
pub fn routing_key(&self) -> String {
self.0.trim().trim_end_matches('.').to_ascii_lowercase()
}
pub fn domain_entry(&self) -> String {
match self.0.strip_prefix("*.") {
Some(base) => format!(
"*.{}",
base.trim().trim_end_matches('.').to_ascii_lowercase()
),
None => self.routing_key(),
}
}
pub fn verification(&self) -> String {
let host = self.0.trim().trim_end_matches('.');
host.strip_prefix("*.").unwrap_or(host).to_ascii_lowercase()
}
pub fn is_wildcard(&self) -> bool {
self.0.trim().starts_with("*.")
}
pub fn dns_record_name(&self) -> String {
format!("{DNS_RECORD_PREFIX}.{}", self.verification())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn verification_strips_wildcard_and_normalizes() {
assert_eq!(Host::new("*.Example.COM.").verification(), "example.com");
assert_eq!(
Host::new(" www.example.com ").verification(),
"www.example.com"
);
assert_eq!(Host::new("EXAMPLE.com").verification(), "example.com");
}
#[test]
fn routing_key_preserves_wildcard() {
assert_eq!(Host::new("*.Example.com").routing_key(), "*.example.com");
assert_eq!(Host::new("Example.COM.").routing_key(), "example.com");
assert_eq!(
Host::new(" app.example.com ").routing_key(),
"app.example.com"
);
}
#[test]
fn domain_entry_matches_routing_for_wellformed_hosts() {
for host in ["*.Example.com", "example.com.", " API.example.com "] {
assert_eq!(
Host::new(host).domain_entry(),
Host::new(host).routing_key()
);
}
}
#[test]
fn routing_and_verification_diverge_only_on_the_wildcard() {
let h = Host::new("*.example.com");
assert_eq!(h.routing_key(), "*.example.com");
assert_eq!(h.verification(), "example.com");
assert!(h.is_wildcard());
}
#[test]
fn dns_record_name_uses_the_verification_form() {
assert_eq!(
Host::new("*.Example.com").dns_record_name(),
format!("{DNS_RECORD_PREFIX}.example.com"),
);
}
}