Skip to main content

dnspls_core/
evidence.rs

1use std::{error::Error, fmt};
2
3use dnspls_domain::DomainName;
4use serde::{Deserialize, Serialize};
5
6use crate::{FreshWindow, ObservationId, SourceId};
7
8/// One immutable, normalized observation from an external or local source.
9#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
10pub struct Observation {
11    id: ObservationId,
12    domain: DomainName,
13    source: SourceId,
14    window: FreshWindow,
15    latency_ms: u32,
16    fact: EvidenceFact,
17}
18
19impl Observation {
20    pub const fn new(
21        id: ObservationId,
22        domain: DomainName,
23        source: SourceId,
24        window: FreshWindow,
25        latency_ms: u32,
26        fact: EvidenceFact,
27    ) -> Self {
28        Self {
29            id,
30            domain,
31            source,
32            window,
33            latency_ms,
34            fact,
35        }
36    }
37
38    pub const fn id(&self) -> &ObservationId {
39        &self.id
40    }
41
42    pub const fn domain(&self) -> &DomainName {
43        &self.domain
44    }
45
46    pub const fn source(&self) -> &SourceId {
47        &self.source
48    }
49
50    pub const fn window(&self) -> FreshWindow {
51        self.window
52    }
53
54    pub const fn latency_ms(&self) -> u32 {
55        self.latency_ms
56    }
57
58    pub const fn fact(&self) -> &EvidenceFact {
59        &self.fact
60    }
61}
62
63/// Facts are closed and typed so adapters cannot combine incompatible fields.
64#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
65#[serde(tag = "kind", rename_all = "snake_case")]
66pub enum EvidenceFact {
67    ZoneSnapshot {
68        membership: ZoneMembership,
69    },
70    Dns {
71        outcome: DnsOutcome,
72        dnssec: DnssecStatus,
73    },
74    Rdap {
75        outcome: RdapOutcome,
76    },
77    ProviderCheck {
78        operation: ProviderOperation,
79        authority: ProviderAuthority,
80        status: ProviderStatus,
81    },
82}
83
84#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
85#[serde(rename_all = "snake_case")]
86pub enum ZoneMembership {
87    Present,
88    Absent,
89    TldUncovered,
90}
91
92#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
93#[serde(rename_all = "snake_case")]
94pub enum DnsOutcome {
95    Delegated,
96    NameError,
97    NoData,
98    ServerFailure,
99    Timeout,
100}
101
102#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
103#[serde(rename_all = "snake_case")]
104pub enum DnssecStatus {
105    Secure,
106    Insecure,
107    Bogus,
108    Indeterminate,
109}
110
111#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
112#[serde(rename_all = "snake_case")]
113pub enum RdapOutcome {
114    ObjectFound,
115    ObjectNotFound,
116    RateLimited,
117    Unsupported,
118    Failed,
119}
120
121#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
122#[serde(rename_all = "snake_case")]
123pub enum ProviderOperation {
124    Availability,
125    Quote,
126    Register,
127    Aftermarket,
128}
129
130/// Contractual truth strength of a provider method—not provider reputation.
131#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
132#[serde(rename_all = "snake_case")]
133pub enum ProviderAuthority {
134    Reported,
135    RegistrationCapable,
136    RegistryEpp,
137}
138
139#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
140#[serde(rename_all = "snake_case")]
141pub enum ProviderStatus {
142    Registrable,
143    RegistryPremium,
144    AftermarketFixed,
145    AftermarketOffer,
146    Auction,
147    Reserved,
148    Registered,
149    Unsupported,
150    Pending,
151    Unknown,
152}
153
154#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
155#[serde(rename_all = "snake_case")]
156pub enum ObservationError {
157    DomainMismatch,
158}
159
160impl fmt::Display for ObservationError {
161    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
162        formatter.write_str("observation domain does not match the reduction domain")
163    }
164}
165
166impl Error for ObservationError {}