Skip to main content

lxmf_reference/
parity.rs

1use serde::{Deserialize, Serialize};
2
3#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
4#[serde(rename_all = "snake_case")]
5#[non_exhaustive]
6pub enum ParityLevel {
7    Complete,
8    Partial,
9    #[serde(other)]
10    Unknown,
11}
12
13#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
14#[non_exhaustive]
15pub struct ParityRatio {
16    pub numerator: u64,
17    pub denominator: u64,
18}
19
20#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
21#[non_exhaustive]
22pub struct ParityInventory {
23    pub total: u64,
24    pub complete: u64,
25    pub partial: u64,
26    pub not_applicable: u64,
27}
28
29#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
30#[non_exhaustive]
31pub struct ParityCheckpoint {
32    pub level: ParityLevel,
33    pub complete_ratio: ParityRatio,
34    pub inventory: ParityInventory,
35}
36
37#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
38#[non_exhaustive]
39pub struct ReferenceRevision {
40    pub version: String,
41    pub revision: String,
42}
43
44#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
45#[non_exhaustive]
46pub struct SoftwareParityReferences {
47    pub reticulum: ReferenceRevision,
48    pub lxmf: ReferenceRevision,
49}
50
51#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
52#[non_exhaustive]
53pub struct SoftwareParityOrientation {
54    pub advisory: bool,
55    pub references: SoftwareParityReferences,
56    pub overall: ParityCheckpoint,
57    pub reticulum: ParityCheckpoint,
58    pub lxmf: ParityCheckpoint,
59}
60
61impl ParityCheckpoint {
62    fn from_inventory(inventory: ParityInventory) -> Self {
63        let denominator = inventory.complete + inventory.partial;
64        let level = if denominator == 0 {
65            ParityLevel::Unknown
66        } else if inventory.partial == 0 {
67            ParityLevel::Complete
68        } else {
69            ParityLevel::Partial
70        };
71        Self {
72            level,
73            complete_ratio: ParityRatio { numerator: inventory.complete, denominator },
74            inventory,
75        }
76    }
77}
78
79fn inventory(
80    total: usize,
81    complete: usize,
82    partial: usize,
83    not_applicable: usize,
84) -> ParityInventory {
85    ParityInventory {
86        total: u64::try_from(total).expect("parity inventory total fits u64"),
87        complete: u64::try_from(complete).expect("parity complete count fits u64"),
88        partial: u64::try_from(partial).expect("parity partial count fits u64"),
89        not_applicable: u64::try_from(not_applicable)
90            .expect("parity not-applicable count fits u64"),
91    }
92}
93
94pub fn current_software_parity_orientation() -> SoftwareParityOrientation {
95    SoftwareParityOrientation {
96        advisory: true,
97        references: SoftwareParityReferences {
98            reticulum: ReferenceRevision {
99                version: crate::PYTHON_RETICULUM_REFERENCE_VERSION.to_owned(),
100                revision: crate::PYTHON_RETICULUM_REFERENCE_REF.to_owned(),
101            },
102            lxmf: ReferenceRevision {
103                version: crate::PYTHON_LXMF_REFERENCE_VERSION.to_owned(),
104                revision: crate::PYTHON_LXMF_REFERENCE_REF.to_owned(),
105            },
106        },
107        overall: ParityCheckpoint::from_inventory(inventory(
108            crate::PYTHON_SOFTWARE_PARITY_TOTAL,
109            crate::PYTHON_SOFTWARE_PARITY_COMPLETE,
110            crate::PYTHON_SOFTWARE_PARITY_PARTIAL,
111            crate::PYTHON_SOFTWARE_PARITY_NOT_APPLICABLE,
112        )),
113        reticulum: ParityCheckpoint::from_inventory(inventory(
114            crate::PYTHON_RETICULUM_PARITY_TOTAL,
115            crate::PYTHON_RETICULUM_PARITY_COMPLETE,
116            crate::PYTHON_RETICULUM_PARITY_PARTIAL,
117            crate::PYTHON_RETICULUM_PARITY_NOT_APPLICABLE,
118        )),
119        lxmf: ParityCheckpoint::from_inventory(inventory(
120            crate::PYTHON_LXMF_PARITY_TOTAL,
121            crate::PYTHON_LXMF_PARITY_COMPLETE,
122            crate::PYTHON_LXMF_PARITY_PARTIAL,
123            crate::PYTHON_LXMF_PARITY_NOT_APPLICABLE,
124        )),
125    }
126}
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131
132    #[test]
133    fn current_orientation_reports_grouped_complete_over_applicable_ratios() {
134        let orientation = current_software_parity_orientation();
135
136        assert!(orientation.advisory);
137        assert_eq!(orientation.overall.level, ParityLevel::Partial);
138        assert_eq!(
139            orientation.overall.complete_ratio,
140            ParityRatio { numerator: 1_695, denominator: 1_810 }
141        );
142        assert_eq!(
143            orientation.overall.inventory,
144            ParityInventory { total: 1_811, complete: 1_695, partial: 115, not_applicable: 1 }
145        );
146        assert_eq!(orientation.reticulum.level, ParityLevel::Partial);
147        assert_eq!(
148            orientation.reticulum.complete_ratio,
149            ParityRatio { numerator: 1_493, denominator: 1_608 }
150        );
151        assert_eq!(
152            orientation.reticulum.inventory,
153            ParityInventory { total: 1_608, complete: 1_493, partial: 115, not_applicable: 0 }
154        );
155        assert_eq!(orientation.lxmf.level, ParityLevel::Complete);
156        assert_eq!(
157            orientation.lxmf.complete_ratio,
158            ParityRatio { numerator: 202, denominator: 202 }
159        );
160        assert_eq!(
161            orientation.lxmf.inventory,
162            ParityInventory { total: 202, complete: 202, partial: 0, not_applicable: 0 }
163        );
164    }
165
166    #[test]
167    fn empty_applicable_inventory_has_unknown_level() {
168        let checkpoint = ParityCheckpoint::from_inventory(ParityInventory {
169            total: 1,
170            complete: 0,
171            partial: 0,
172            not_applicable: 1,
173        });
174
175        assert_eq!(checkpoint.level, ParityLevel::Unknown);
176        assert_eq!(checkpoint.complete_ratio, ParityRatio { numerator: 0, denominator: 0 });
177    }
178}