1use std::{error::Error, fmt};
2
3use dnspls_domain::DomainName;
4use serde::{Deserialize, Serialize};
5
6use crate::{FreshWindow, ObservationId, SourceId};
7
8#[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#[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#[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 {}