dnspls-core 0.1.0

Provider-neutral evidence and decision kernel for DNSpls
Documentation
use std::{error::Error, fmt};

use dnspls_domain::DomainName;
use serde::{Deserialize, Serialize};

use crate::{FreshWindow, ObservationId, SourceId};

/// One immutable, normalized observation from an external or local source.
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct Observation {
    id: ObservationId,
    domain: DomainName,
    source: SourceId,
    window: FreshWindow,
    latency_ms: u32,
    fact: EvidenceFact,
}

impl Observation {
    pub const fn new(
        id: ObservationId,
        domain: DomainName,
        source: SourceId,
        window: FreshWindow,
        latency_ms: u32,
        fact: EvidenceFact,
    ) -> Self {
        Self {
            id,
            domain,
            source,
            window,
            latency_ms,
            fact,
        }
    }

    pub const fn id(&self) -> &ObservationId {
        &self.id
    }

    pub const fn domain(&self) -> &DomainName {
        &self.domain
    }

    pub const fn source(&self) -> &SourceId {
        &self.source
    }

    pub const fn window(&self) -> FreshWindow {
        self.window
    }

    pub const fn latency_ms(&self) -> u32 {
        self.latency_ms
    }

    pub const fn fact(&self) -> &EvidenceFact {
        &self.fact
    }
}

/// Facts are closed and typed so adapters cannot combine incompatible fields.
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum EvidenceFact {
    ZoneSnapshot {
        membership: ZoneMembership,
    },
    Dns {
        outcome: DnsOutcome,
        dnssec: DnssecStatus,
    },
    Rdap {
        outcome: RdapOutcome,
    },
    ProviderCheck {
        operation: ProviderOperation,
        authority: ProviderAuthority,
        status: ProviderStatus,
    },
}

#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ZoneMembership {
    Present,
    Absent,
    TldUncovered,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum DnsOutcome {
    Delegated,
    NameError,
    NoData,
    ServerFailure,
    Timeout,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum DnssecStatus {
    Secure,
    Insecure,
    Bogus,
    Indeterminate,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum RdapOutcome {
    ObjectFound,
    ObjectNotFound,
    RateLimited,
    Unsupported,
    Failed,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ProviderOperation {
    Availability,
    Quote,
    Register,
    Aftermarket,
}

/// Contractual truth strength of a provider method—not provider reputation.
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ProviderAuthority {
    Reported,
    RegistrationCapable,
    RegistryEpp,
}

#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ProviderStatus {
    Registrable,
    RegistryPremium,
    AftermarketFixed,
    AftermarketOffer,
    Auction,
    Reserved,
    Registered,
    Unsupported,
    Pending,
    Unknown,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ObservationError {
    DomainMismatch,
}

impl fmt::Display for ObservationError {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter.write_str("observation domain does not match the reduction domain")
    }
}

impl Error for ObservationError {}