use crate::domain_info::DomainInfo;
use crate::rdap::{ContactInfo, RdapResponse};
use crate::whois::WhoisResponse;
pub(super) const ROLES: [&str; 3] = ["Registrant", "Admin", "Tech"];
#[derive(Clone, Copy)]
pub(super) struct Contact<'a> {
pub name: &'a Option<String>,
pub organization: &'a Option<String>,
pub email: &'a Option<String>,
pub phone: &'a Option<String>,
pub address: &'a Option<String>,
pub country: &'a Option<String>,
}
impl<'a> Contact<'a> {
pub const EMPTY: Contact<'static> = Contact {
name: &None,
organization: &None,
email: &None,
phone: &None,
address: &None,
country: &None,
};
pub fn rdap(contact: Option<&'a ContactInfo>) -> Self {
match contact {
Some(c) if c.has_info() => Contact {
name: &c.name,
organization: &c.organization,
email: &c.email,
phone: &c.phone,
address: &c.address,
country: &c.country,
},
_ => Contact::EMPTY,
}
}
pub fn without_identity(self) -> Self {
Contact {
name: &None,
organization: &None,
..self
}
}
pub fn fields(&self) -> [(&'static str, &'a Option<String>); 6] {
[
("Name", self.name),
("Organization", self.organization),
("Email", self.email),
("Phone", self.phone),
("Address", self.address),
("Country", self.country),
]
}
pub fn is_empty(&self) -> bool {
self.fields().iter().all(|(_, field)| field.is_none())
}
}
pub(super) fn rdap_contacts(data: &RdapResponse) -> [Option<ContactInfo>; 3] {
[
data.get_registrant_contact(),
data.get_admin_contact(),
data.get_tech_contact(),
]
}
pub(super) fn rdap_views(contacts: &[Option<ContactInfo>; 3]) -> [Contact<'_>; 3] {
contacts.each_ref().map(|c| Contact::rdap(c.as_ref()))
}
pub(super) trait FlatContacts {
fn contacts(&self) -> [Contact<'_>; 3];
}
macro_rules! impl_flat_contacts {
($($source:ty),+) => {$(
impl FlatContacts for $source {
fn contacts(&self) -> [Contact<'_>; 3] {
[
Contact {
email: &self.registrant_email,
phone: &self.registrant_phone,
address: &self.registrant_address,
country: &self.registrant_country,
..Contact::EMPTY
},
Contact {
name: &self.admin_name,
organization: &self.admin_organization,
email: &self.admin_email,
phone: &self.admin_phone,
..Contact::EMPTY
},
Contact {
name: &self.tech_name,
organization: &self.tech_organization,
email: &self.tech_email,
phone: &self.tech_phone,
..Contact::EMPTY
},
]
}
}
)+};
}
impl_flat_contacts!(WhoisResponse, DomainInfo);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rdap_view_is_empty_for_redacted_only_contact() {
let redacted = ContactInfo {
name: Some("REDACTED FOR PRIVACY".to_string()),
..Default::default()
};
assert!(Contact::rdap(Some(&redacted)).is_empty());
assert!(Contact::rdap(None).is_empty());
}
#[test]
fn without_identity_keeps_only_contact_details() {
let identity_only = ContactInfo {
name: Some("Jane".to_string()),
organization: Some("Example LLC".to_string()),
..Default::default()
};
let view = Contact::rdap(Some(&identity_only));
assert!(!view.is_empty());
assert!(view.without_identity().is_empty());
}
}