xbp 10.30.1

XBP is a zero-config build pack that can also interact with proxies, kafka, sockets, synthetic monitors.
Documentation
use crate::provider_support::{
    CloudflareDnsRecord, CloudflareDnsSettings, CloudflareDnssec, CloudflareZone,
    DomainAvailability, RegisteredDomain,
};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct DnsInventoryCache {
    #[serde(default)]
    pub cloudflare: Option<CloudflareDnsInventoryCache>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct CloudflareDnsInventoryCache {
    #[serde(default)]
    pub account_id: Option<String>,
    #[serde(default)]
    pub last_updated_at: Option<DateTime<Utc>>,
    #[serde(default)]
    pub zones: Vec<CloudflareZone>,
    #[serde(default)]
    pub records_by_zone: BTreeMap<String, Vec<CloudflareDnsRecord>>,
    #[serde(default)]
    pub dnssec_by_zone: BTreeMap<String, CloudflareDnssec>,
    #[serde(default)]
    pub settings_by_zone: BTreeMap<String, CloudflareDnsSettings>,
    #[serde(default)]
    pub domain_availability_by_name: BTreeMap<String, DomainAvailability>,
    #[serde(default)]
    pub registered_domains: Vec<RegisteredDomain>,
}

impl DnsInventoryCache {
    pub fn record_cloudflare_zones(
        &mut self,
        account_id: Option<String>,
        zones: &[CloudflareZone],
    ) {
        let cache = self.cloudflare_mut();
        cache.update_context(account_id);
        for zone in zones {
            upsert_zone(&mut cache.zones, zone.clone());
        }
        sort_zones(&mut cache.zones);
    }

    pub fn record_cloudflare_zone(&mut self, account_id: Option<String>, zone: &CloudflareZone) {
        let cache = self.cloudflare_mut();
        cache.update_context(account_id);
        upsert_zone(&mut cache.zones, zone.clone());
        sort_zones(&mut cache.zones);
    }

    pub fn remove_cloudflare_zone(&mut self, zone_id: &str) {
        let cache = self.cloudflare_mut();
        cache.touch();
        cache.zones.retain(|zone| zone.id != zone_id);
        cache.records_by_zone.remove(zone_id);
        cache.dnssec_by_zone.remove(zone_id);
        cache.settings_by_zone.remove(zone_id);
    }

    pub fn record_cloudflare_records(
        &mut self,
        account_id: Option<String>,
        zone_id: &str,
        records: &[CloudflareDnsRecord],
    ) {
        let cache = self.cloudflare_mut();
        cache.update_context(account_id);
        let mut next = records.to_vec();
        sort_records(&mut next);
        cache.records_by_zone.insert(zone_id.to_string(), next);
    }

    pub fn record_cloudflare_record(
        &mut self,
        account_id: Option<String>,
        record: &CloudflareDnsRecord,
    ) {
        let cache = self.cloudflare_mut();
        cache.update_context(account_id);
        let records = cache
            .records_by_zone
            .entry(record.zone_id.clone())
            .or_default();
        upsert_record(records, record.clone());
        sort_records(records);
    }

    pub fn remove_cloudflare_record(&mut self, zone_id: &str, record_id: &str) {
        let cache = self.cloudflare_mut();
        cache.touch();
        if let Some(records) = cache.records_by_zone.get_mut(zone_id) {
            records.retain(|record| record.id != record_id);
        }
    }

    pub fn record_cloudflare_dnssec(
        &mut self,
        account_id: Option<String>,
        zone_id: &str,
        dnssec: &CloudflareDnssec,
    ) {
        let cache = self.cloudflare_mut();
        cache.update_context(account_id);
        cache
            .dnssec_by_zone
            .insert(zone_id.to_string(), dnssec.clone());
    }

    pub fn record_cloudflare_settings(
        &mut self,
        account_id: Option<String>,
        zone_id: &str,
        settings: &CloudflareDnsSettings,
    ) {
        let cache = self.cloudflare_mut();
        cache.update_context(account_id);
        cache
            .settings_by_zone
            .insert(zone_id.to_string(), settings.clone());
    }

    pub fn record_cloudflare_domain_availability(
        &mut self,
        account_id: Option<String>,
        domains: &[DomainAvailability],
    ) {
        let cache = self.cloudflare_mut();
        cache.update_context(account_id);
        for domain in domains {
            cache
                .domain_availability_by_name
                .insert(domain.name.clone(), domain.clone());
        }
    }

    pub fn record_cloudflare_registered_domains(
        &mut self,
        account_id: Option<String>,
        domains: &[RegisteredDomain],
    ) {
        let cache = self.cloudflare_mut();
        cache.update_context(account_id);
        for domain in domains {
            upsert_registered_domain(&mut cache.registered_domains, domain.clone());
        }
        sort_registered_domains(&mut cache.registered_domains);
    }

    fn cloudflare_mut(&mut self) -> &mut CloudflareDnsInventoryCache {
        self.cloudflare
            .get_or_insert_with(CloudflareDnsInventoryCache::default)
    }
}

impl CloudflareDnsInventoryCache {
    fn touch(&mut self) {
        self.last_updated_at = Some(Utc::now());
    }

    fn update_context(&mut self, account_id: Option<String>) {
        if let Some(account_id) = account_id.filter(|value| !value.trim().is_empty()) {
            self.account_id = Some(account_id);
        }
        self.touch();
    }
}

fn upsert_zone(zones: &mut Vec<CloudflareZone>, zone: CloudflareZone) {
    if let Some(existing) = zones.iter_mut().find(|existing| existing.id == zone.id) {
        *existing = zone;
    } else {
        zones.push(zone);
    }
}

fn upsert_record(records: &mut Vec<CloudflareDnsRecord>, record: CloudflareDnsRecord) {
    if let Some(existing) = records.iter_mut().find(|existing| existing.id == record.id) {
        *existing = record;
    } else {
        records.push(record);
    }
}

fn upsert_registered_domain(domains: &mut Vec<RegisteredDomain>, domain: RegisteredDomain) {
    if let Some(existing) = domains
        .iter_mut()
        .find(|existing| existing.name == domain.name)
    {
        *existing = domain;
    } else {
        domains.push(domain);
    }
}

fn sort_zones(zones: &mut [CloudflareZone]) {
    zones.sort_by(|left, right| {
        left.name
            .cmp(&right.name)
            .then_with(|| left.id.cmp(&right.id))
    });
}

fn sort_records(records: &mut [CloudflareDnsRecord]) {
    records.sort_by(|left, right| {
        left.name
            .cmp(&right.name)
            .then_with(|| left.record_type.cmp(&right.record_type))
            .then_with(|| left.id.cmp(&right.id))
    });
}

fn sort_registered_domains(domains: &mut [RegisteredDomain]) {
    domains.sort_by(|left, right| left.name.cmp(&right.name));
}

#[cfg(test)]
mod tests {
    use super::DnsInventoryCache;
    use crate::provider_support::{CloudflareDnsRecord, CloudflareZone, RegisteredDomain};

    #[test]
    fn record_upserts_zone_and_record_entries() {
        let mut cache = DnsInventoryCache::default();
        let zone = CloudflareZone {
            id: "zone_1".to_string(),
            name: "example.com".to_string(),
            status: None,
            r#type: None,
            paused: None,
            account: None,
            owner: None,
            tenant: None,
            tenant_unit: None,
            plan: None,
            name_servers: Vec::new(),
            vanity_name_servers: None,
            original_dnshost: None,
            original_name_servers: None,
            original_registrar: None,
            activated_on: None,
            created_on: "2026-01-01T00:00:00Z".to_string(),
            modified_on: "2026-01-01T00:00:00Z".to_string(),
            development_mode: None,
            cname_suffix: None,
            verification_key: None,
            permissions: None,
            meta: Default::default(),
        };
        let record = CloudflareDnsRecord {
            id: "rec_1".to_string(),
            zone_id: "zone_1".to_string(),
            zone_name: Some("example.com".to_string()),
            name: "api.example.com".to_string(),
            record_type: "A".to_string(),
            content: Some("127.0.0.1".to_string()),
            ttl: Some(1),
            proxied: Some(false),
            priority: None,
            comment: None,
            tags: None,
            data: None,
            settings: None,
            meta: None,
            proxiable: None,
            created_on: None,
            modified_on: None,
        };

        cache.record_cloudflare_zone(Some("acc_1".to_string()), &zone);
        cache.record_cloudflare_record(Some("acc_1".to_string()), &record);
        cache.record_cloudflare_record(Some("acc_1".to_string()), &record);

        let cloudflare = cache.cloudflare.expect("cloudflare cache");
        assert_eq!(cloudflare.zones.len(), 1);
        assert_eq!(cloudflare.records_by_zone["zone_1"].len(), 1);
        assert_eq!(cloudflare.account_id.as_deref(), Some("acc_1"));
    }

    #[test]
    fn registered_domains_upsert_by_name() {
        let mut cache = DnsInventoryCache::default();
        let domain = RegisteredDomain {
            name: "example.com".to_string(),
            account_id: Some("acc_1".to_string()),
            auto_renew: Some(true),
            expires_at: None,
            registered_at: None,
            registration: None,
            workflow: None,
            extra: Default::default(),
        };

        cache.record_cloudflare_registered_domains(None, &[domain.clone(), domain]);

        let cloudflare = cache.cloudflare.expect("cloudflare cache");
        assert_eq!(cloudflare.registered_domains.len(), 1);
    }
}