Skip to main content

ic_query/nns/governance/model/
mod.rs

1//! Module: nns::governance::model
2//!
3//! Responsibility: expose shared NNS Governance report and collection models.
4//! Does not own: live transport, CLI parsing, caching, or text rendering.
5//! Boundary: preserves one explicit facade across native Governance report families.
6
7mod classification;
8mod economics;
9mod events;
10mod metrics;
11
12use serde::{Deserialize as SerdeDeserialize, Serialize};
13use std::fmt;
14
15pub use classification::NnsGovernanceVote;
16pub(in crate::nns) use classification::{
17    deserialize_code_label, native_code_classification, native_optional_code_classification,
18};
19
20///
21/// NnsGovernanceCollectionStatus
22///
23/// Lifecycle of a caller-owned resumable NNS Governance collection.
24/// Each collection owns its cursor and API exhaustion rules.
25///
26
27#[derive(Clone, Copy, Debug, Eq, PartialEq, SerdeDeserialize, Serialize)]
28#[serde(rename_all = "snake_case")]
29pub enum NnsGovernanceCollectionStatus {
30    /// No source page has been admitted yet.
31    Ready,
32    /// Another bounded page may be requested.
33    Collecting,
34    /// Governance API exhaustion was observed.
35    Complete,
36    /// Another cursor exists, but the configured page ceiling was consumed.
37    PageLimitReached,
38}
39
40impl NnsGovernanceCollectionStatus {
41    /// Return the stable JSON and display label.
42    #[must_use]
43    pub const fn as_str(self) -> &'static str {
44        match self {
45            Self::Ready => "ready",
46            Self::Collecting => "collecting",
47            Self::Complete => "complete",
48            Self::PageLimitReached => "page_limit_reached",
49        }
50    }
51}
52
53impl fmt::Display for NnsGovernanceCollectionStatus {
54    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
55        formatter.write_str(self.as_str())
56    }
57}
58
59///
60/// NnsGovernanceExecutionAssurance
61///
62/// Execution semantics derived from direct Governance source provenance.
63///
64
65#[derive(Clone, Copy, Debug, Eq, PartialEq, SerdeDeserialize, Serialize)]
66#[serde(rename_all = "snake_case")]
67pub enum NnsGovernanceExecutionAssurance {
68    /// An ordinary replica query without replicated execution semantics.
69    UnreplicatedQuery,
70    /// A replicated inter-canister call executed by the IC.
71    ReplicatedExecution,
72}
73
74impl NnsGovernanceExecutionAssurance {
75    /// Return the stable display label for this assurance.
76    #[must_use]
77    pub const fn as_str(self) -> &'static str {
78        match self {
79            Self::UnreplicatedQuery => "unreplicated_query",
80            Self::ReplicatedExecution => "replicated_execution",
81        }
82    }
83}
84
85///
86/// NnsGovernanceSourceProvenance
87///
88/// Transport and collector evidence for one direct NNS Governance report.
89///
90
91#[derive(Clone, Debug, Eq, PartialEq, SerdeDeserialize, Serialize)]
92#[serde(tag = "source_transport", rename_all = "snake_case")]
93pub enum NnsGovernanceSourceProvenance {
94    /// An ordinary unreplicated query submitted through a replica endpoint.
95    ReplicaQuery {
96        /// Replica endpoint used for the query.
97        endpoint: String,
98        /// Collector identity supplied by the caller.
99        fetched_by: String,
100    },
101    /// A replicated call made by one collector canister.
102    ReplicatedInterCanisterCall {
103        /// Principal of the canister that executed the call.
104        collector_canister_id: String,
105    },
106}
107
108impl NnsGovernanceSourceProvenance {
109    /// Return the execution assurance implied by this transport.
110    #[must_use]
111    pub const fn execution_assurance(&self) -> NnsGovernanceExecutionAssurance {
112        match self {
113            Self::ReplicaQuery { .. } => NnsGovernanceExecutionAssurance::UnreplicatedQuery,
114            Self::ReplicatedInterCanisterCall { .. } => {
115                NnsGovernanceExecutionAssurance::ReplicatedExecution
116            }
117        }
118    }
119}
120
121///
122/// NnsGovernanceReportContext
123///
124/// Shared context embedded in every direct NNS Governance report.
125///
126
127#[derive(Clone, Debug, Eq, PartialEq, SerdeDeserialize, Serialize)]
128pub struct NnsGovernanceReportContext {
129    /// Report schema version.
130    pub schema_version: u32,
131    /// Queried network identity.
132    pub network: String,
133    /// NNS Governance canister principal.
134    pub governance_canister_id: String,
135    /// UTC collection timestamp.
136    pub fetched_at: String,
137    /// Transport-specific source provenance.
138    pub source: NnsGovernanceSourceProvenance,
139}
140
141pub use economics::{
142    NnsGovernanceDecimal, NnsGovernanceEconomics, NnsGovernanceEconomicsReport,
143    NnsGovernancePercentage, NnsNeuronsFundEconomics,
144    NnsNeuronsFundMatchedFundingCurveCoefficients, NnsVotingPowerEconomics,
145};
146pub use events::{
147    NnsGovernanceMaturityModulation, NnsGovernanceMaturityModulationReport,
148    NnsGovernanceProposalId, NnsGovernanceRewardEvent, NnsGovernanceRewardEventReport,
149};
150pub use metrics::{
151    NnsGovernanceMetricBucket, NnsGovernanceMetrics, NnsGovernanceMetricsReport,
152    NnsGovernanceNeuronSubsetMetrics,
153};
154
155#[cfg(test)]
156mod tests {
157    use super::NnsGovernanceCollectionStatus;
158
159    #[test]
160    fn collection_status_json_and_display_use_the_supported_labels() {
161        for (status, label) in [
162            (NnsGovernanceCollectionStatus::Ready, "ready"),
163            (NnsGovernanceCollectionStatus::Collecting, "collecting"),
164            (NnsGovernanceCollectionStatus::Complete, "complete"),
165            (
166                NnsGovernanceCollectionStatus::PageLimitReached,
167                "page_limit_reached",
168            ),
169        ] {
170            let value = serde_json::json!(label);
171            assert_eq!(
172                serde_json::to_value(status).expect("serialize status"),
173                value
174            );
175            assert_eq!(
176                serde_json::from_value::<NnsGovernanceCollectionStatus>(value)
177                    .expect("deserialize status"),
178                status
179            );
180            assert_eq!(status.as_str(), label);
181            assert_eq!(status.to_string(), label);
182        }
183    }
184}