mod bootstrap;
mod convention;
mod definition;
mod json;
mod layered;
mod provider;
pub use bootstrap::{BootstrapRegistry, IANA_BOOTSTRAP_URL};
pub use convention::{ConventionRegistry, DEFAULT_TEMPLATE};
pub use definition::{
Endpoint, EndpointKind, IdnForm, RdapEndpoint, Registry, RegistryBuilder, WhoisEndpoint,
WHOIS_PORT,
};
pub use json::JsonRegistry;
pub use layered::{LayerStrategy, LayeredRegistry};
pub use provider::{RegistryProvider, Resolution};
use std::sync::{Arc, OnceLock};
pub fn default_provider() -> Arc<dyn RegistryProvider> {
static DEFAULT: OnceLock<Arc<LayeredRegistry>> = OnceLock::new();
let stack = DEFAULT.get_or_init(|| {
Arc::new(
LayeredRegistry::new(LayerStrategy::Union)
.shared_layer(JsonRegistry::bundled())
.shared_layer(BootstrapRegistry::bundled()),
)
});
Arc::clone(stack) as Arc<dyn RegistryProvider>
}
#[cfg(test)]
mod tests {
use super::*;
use crate::domain::{DomainName, Tld};
#[test]
fn default_stack_offers_both_protocols_for_com() {
let registry = default_provider();
let com = registry.get(&Tld::parse("com").unwrap()).unwrap();
assert!(com.endpoints().iter().any(Endpoint::is_whois));
assert!(com.endpoints().iter().any(Endpoint::is_rdap));
assert!(!com.available_markers().is_empty());
assert!(com.is_thin());
}
#[test]
fn default_stack_covers_more_than_either_layer_alone() {
let curated = JsonRegistry::bundled().tlds().len();
let iana = BootstrapRegistry::bundled().tlds().len();
let combined = default_provider().tlds().len();
assert!(
combined > curated.max(iana),
"{combined} <= max({curated}, {iana})"
);
}
#[test]
fn default_stack_resolves_multi_label_suffixes() {
let registry = default_provider();
for (input, expected_tld, expected_name) in [
("example.co.uk", "co.uk", "example.co.uk"),
("www.example.co.uk", "co.uk", "example.co.uk"),
("example.com", "com", "example.com"),
("example.com.au", "com.au", "example.com.au"),
] {
let name = DomainName::parse(input).unwrap();
let resolved = registry.resolve(&name).unwrap();
assert_eq!(resolved.tld.ascii(), expected_tld, "for {input}");
assert_eq!(
resolved.registrable.as_ascii(),
expected_name,
"for {input}"
);
}
}
#[test]
fn default_stack_is_shared_not_reparsed() {
let first = default_provider();
let second = default_provider();
assert_eq!(first.tlds().len(), second.tlds().len());
}
}