1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use url::Url;
6
7#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
9pub struct SourceId(String);
10
11impl SourceId {
12 pub fn new(id: impl Into<String>) -> Self {
14 Self(id.into())
15 }
16
17 pub fn as_str(&self) -> &str {
19 &self.0
20 }
21}
22
23#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
25pub struct MeasurementId(String);
26
27impl MeasurementId {
28 pub fn new(id: impl Into<String>) -> Self {
30 Self(id.into())
31 }
32
33 pub fn as_str(&self) -> &str {
35 &self.0
36 }
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
41pub struct BoundedFraction(f64);
42
43impl BoundedFraction {
44 pub fn new(value: f64) -> Result<Self, String> {
46 if value.is_nan() {
47 return Err("BoundedFraction cannot be NaN".to_string());
48 }
49 if !(0.0..=1.0).contains(&value) {
50 return Err(format!(
51 "BoundedFraction must be in [0.0, 1.0], got {}",
52 value
53 ));
54 }
55 Ok(Self(value))
56 }
57
58 pub fn value(&self) -> f64 {
60 self.0
61 }
62}
63
64#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
66pub struct NonNegativeHours(f64);
67
68impl NonNegativeHours {
69 pub fn new(value: f64) -> Result<Self, String> {
71 if value.is_nan() {
72 return Err("NonNegativeHours cannot be NaN".to_string());
73 }
74 if value < 0.0 {
75 return Err(format!("NonNegativeHours must be >= 0.0, got {}", value));
76 }
77 Ok(Self(value))
78 }
79
80 pub fn value(&self) -> f64 {
82 self.0
83 }
84}
85
86#[derive(Debug, Clone, Serialize, Deserialize)]
88pub enum SourceValue {
89 Fraction(BoundedFraction),
90 Hours(NonNegativeHours),
91}
92
93#[derive(Debug, Clone, Serialize, Deserialize)]
95pub struct Provenance {
96 pub source_url: Url,
97 pub fetch_timestamp: DateTime<Utc>,
98 pub source_version: Option<String>,
99 pub raw_value: String,
100}
101
102#[derive(Debug, Clone, Serialize, Deserialize)]
104pub struct Evidence {
105 pub source: SourceId,
106 pub measurement: MeasurementId,
107 pub value: SourceValue,
108 pub reliability_percentile: u8,
109 pub provenance: Provenance,
110}