use super::*;
impl MarkdownFormatter {
pub(super) fn format_rdap(&self, response: &RdapResponse) -> String {
let mut output = Vec::new();
let name = response
.domain_name()
.or(response.name.as_deref())
.unwrap_or("Unknown");
output.push(format!("## RDAP: {}", MdSafe(name)));
output.push(String::new());
if let Some(ref handle) = response.handle {
output.push(format!("- **Handle**: `{}`", MdSafe(handle)));
}
if let Some(registrar) = response.get_registrar() {
output.push(format!("- **Registrar**: {}", MdSafe(®istrar)));
}
if let Some(registrant) = response.get_registrant() {
output.push(format!("- **Registrant**: {}", MdSafe(®istrant)));
}
if let Some(organization) = response.get_registrant_organization() {
output.push(format!("- **Organization**: {}", MdSafe(&organization)));
}
if let Some(contact) = response.get_registrant_contact() {
self.format_rdap_contact(&mut output, "Registrant Contact", &contact);
}
if let Some(contact) = response.get_admin_contact() {
self.format_rdap_contact(&mut output, "Admin Contact", &contact);
}
if let Some(contact) = response.get_tech_contact() {
self.format_rdap_contact(&mut output, "Tech Contact", &contact);
}
if let Some(contact) = response.get_billing_contact() {
self.format_rdap_contact(&mut output, "Billing Contact", &contact);
}
if let Some(created) = response.creation_date() {
output.push(format!("- **Created**: `{}`", created.format("%Y-%m-%d")));
}
if let Some(expires) = response.expiration_date() {
let days_until = (expires - chrono::Utc::now()).num_days();
output.push(format!(
"- **Expires**: `{}` ({} days)",
expires.format("%Y-%m-%d"),
days_until
));
}
if let Some(updated) = response.last_updated() {
output.push(format!("- **Updated**: `{}`", updated.format("%Y-%m-%d")));
}
if !response.status.is_empty() {
output.push(format!(
"- **Status**: {}",
response
.status
.iter()
.map(|s| format!("`{}`", MdSafe(s)))
.collect::<Vec<_>>()
.join(", ")
));
}
let nameservers = response.nameserver_names();
if !nameservers.is_empty() {
output.push(format!(
"- **Nameservers**: {}",
nameservers
.iter()
.map(|ns| format!("`{}`", MdSafe(ns)))
.collect::<Vec<_>>()
.join(", ")
));
}
if response.is_dnssec_signed() {
output.push("- **DNSSEC**: signed".to_string());
}
if let Some(ref start) = response.start_address {
output.push(format!("- **Start Address**: `{}`", MdSafe(start)));
}
if let Some(ref end) = response.end_address {
output.push(format!("- **End Address**: `{}`", MdSafe(end)));
}
if let Some(ref country) = response.country {
output.push(format!("- **Country**: {}", MdSafe(country)));
}
if let Some(start) = response.start_autnum {
output.push(format!(
"- **AS Number**: `AS{}` - `AS{}`",
start,
response.end_autnum.unwrap_or(start)
));
}
output.join("\n")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_markdown_rdap_entity_name_backtick_neutralized() {
use crate::rdap::RdapResponse;
let json = serde_json::json!({
"ldhName": "example.com",
"entities": [
{
"objectClassName": "entity",
"handle": "Evil`Registrar`Co",
"roles": ["registrar"],
}
]
});
let response: RdapResponse = serde_json::from_value(json).unwrap();
let output = MarkdownFormatter::new().format_rdap(&response);
assert!(
output.contains("- **Registrar**: Evil'Registrar'Co"),
"expected backticks neutralized in RDAP output:\n{}",
output
);
assert!(
!output.contains("Evil`Registrar`Co"),
"raw backticks survived into output:\n{}",
output
);
}
}