Skip to main content

ic_query/sns/report/model/reports/
canisters.rs

1//! Module: sns::report::model::reports::canisters
2//!
3//! Responsibility: SNS Root canister inventory and health report DTOs.
4//! Does not own: Root transport, SNS lookup, report assembly, or rendering.
5//! Boundary: preserves native canister roles, status, module hashes, and typed gaps.
6
7use serde::Serialize;
8
9///
10/// SnsCanisterRole
11///
12/// Native role assigned to a canister by the SNS Root interface.
13///
14
15#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize)]
16#[serde(rename_all = "snake_case")]
17pub enum SnsCanisterRole {
18    /// SNS Root canister.
19    Root,
20    /// SNS Governance canister.
21    Governance,
22    /// SNS ledger canister.
23    Ledger,
24    /// SNS decentralization swap canister.
25    Swap,
26    /// SNS ledger index canister.
27    Index,
28    /// SNS ledger archive canister.
29    Archive,
30    /// Dapp canister registered with SNS Root.
31    Dapp,
32    /// SNS extension canister registered with SNS Root.
33    Extension,
34}
35
36impl SnsCanisterRole {
37    /// Return the native lowercase role label used in text reports.
38    #[must_use]
39    pub const fn as_str(self) -> &'static str {
40        match self {
41            Self::Root => "root",
42            Self::Governance => "governance",
43            Self::Ledger => "ledger",
44            Self::Swap => "swap",
45            Self::Index => "index",
46            Self::Archive => "archive",
47            Self::Dapp => "dapp",
48            Self::Extension => "extension",
49        }
50    }
51}
52
53///
54/// SnsCanisterStatus
55///
56/// Native running state returned by SNS Root for one canister.
57///
58
59#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
60#[serde(rename_all = "snake_case")]
61pub enum SnsCanisterStatus {
62    /// The canister is running.
63    Running,
64    /// The canister is stopping.
65    Stopping,
66    /// The canister is stopped.
67    Stopped,
68}
69
70impl SnsCanisterStatus {
71    /// Return the native lowercase canister-status label.
72    #[must_use]
73    pub const fn as_str(self) -> &'static str {
74        match self {
75            Self::Running => "running",
76            Self::Stopping => "stopping",
77            Self::Stopped => "stopped",
78        }
79    }
80}
81
82///
83/// SnsCanisterGapKind
84///
85/// Typed reason that Root inventory and health evidence could not be joined.
86///
87
88#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize)]
89#[serde(rename_all = "snake_case")]
90pub enum SnsCanisterGapKind {
91    /// The inventory response omitted a canister id for a native singleton role.
92    InventoryCanisterIdMissing,
93    /// The health response omitted the summary for an inventory canister.
94    SummaryMissing,
95    /// A health summary omitted its canister id.
96    SummaryCanisterIdMissing,
97    /// A singleton health summary identified a different canister than inventory.
98    SummaryCanisterIdMismatch,
99    /// A health summary identified a canister absent from inventory.
100    SummaryNotInInventory,
101    /// More than one health summary identified the same inventory canister and role.
102    DuplicateSummary,
103    /// A matched health summary omitted canister status.
104    StatusMissing,
105    /// The current Root health response does not expose this native role.
106    HealthUnsupported,
107}
108
109impl SnsCanisterGapKind {
110    /// Return the stable lowercase gap label used in text reports.
111    #[must_use]
112    pub const fn as_str(self) -> &'static str {
113        match self {
114            Self::InventoryCanisterIdMissing => "inventory_canister_id_missing",
115            Self::SummaryMissing => "summary_missing",
116            Self::SummaryCanisterIdMissing => "summary_canister_id_missing",
117            Self::SummaryCanisterIdMismatch => "summary_canister_id_mismatch",
118            Self::SummaryNotInInventory => "summary_not_in_inventory",
119            Self::DuplicateSummary => "duplicate_summary",
120            Self::StatusMissing => "status_missing",
121            Self::HealthUnsupported => "health_unsupported",
122        }
123    }
124}
125
126///
127/// SnsCanisterGap
128///
129/// One explicit inventory or health relation gap returned by SNS Root.
130///
131
132#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
133pub struct SnsCanisterGap {
134    /// Typed gap classification.
135    pub kind: SnsCanisterGapKind,
136    /// Native SNS canister role involved in the gap.
137    pub role: SnsCanisterRole,
138    /// Canister id supplied by the inventory response, when available.
139    pub inventory_canister_id: Option<String>,
140    /// Canister id supplied by the health summary, when available.
141    pub summary_canister_id: Option<String>,
142}
143
144///
145/// SnsCanisterRow
146///
147/// One canister in the authoritative SNS Root inventory.
148///
149
150#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
151pub struct SnsCanisterRow {
152    /// Native SNS canister role.
153    pub role: SnsCanisterRole,
154    /// Canonical canister principal text.
155    pub canister_id: String,
156    /// Native canister running state when Root returned health evidence.
157    pub status: Option<SnsCanisterStatus>,
158    /// Running Wasm module hash as lowercase hexadecimal text.
159    pub module_hash_hex: Option<String>,
160    /// Raw cycle balance as unsigned decimal text.
161    pub cycles: Option<String>,
162    /// Raw memory size in bytes as unsigned decimal text.
163    pub memory_size: Option<String>,
164    /// Raw idle cycles burned per day as unsigned decimal text.
165    pub idle_cycles_burned_per_day: Option<String>,
166    /// Canonical controller principals returned by Root.
167    pub controllers: Vec<String>,
168}
169
170///
171/// SnsCanisterReport
172///
173/// Joined SNS Root inventory and operational-health report.
174///
175
176#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
177pub struct SnsCanisterReport {
178    /// Report schema version.
179    pub schema_version: u32,
180    /// Requested IC network identity.
181    pub network: String,
182    /// Mainnet SNS-W canister used to resolve the SNS.
183    pub sns_wasm_canister_id: String,
184    /// Collection timestamp in UTC.
185    pub fetched_at: String,
186    /// IC API endpoint used for SNS-W and Root calls.
187    pub source_endpoint: String,
188    /// Collector identity recorded by the source request.
189    pub fetched_by: String,
190    /// SNS-W list id assigned to this deployed SNS.
191    pub id: usize,
192    /// SNS name resolved during discovery.
193    pub name: String,
194    /// Root canister queried for inventory and health.
195    pub root_canister_id: String,
196    /// Root query method used as the inventory authority.
197    pub inventory_method: String,
198    /// Root ingress method used for operational health.
199    pub health_method: String,
200    /// Transport kind used for the health call.
201    pub health_call_type: String,
202    /// Value sent in the Root health request; always false for this read-only report.
203    pub health_update_canister_list: bool,
204    /// Whether all joined values represent one authoritative point-in-time snapshot.
205    pub point_in_time_guaranteed: bool,
206    /// Number of canisters in the Root inventory response.
207    pub canister_count: usize,
208    /// Number of inventory canisters with returned operational status.
209    pub health_status_count: usize,
210    /// Number of explicit inventory or health relation gaps.
211    pub gap_count: usize,
212    /// Canonically ordered inventory rows.
213    pub canisters: Vec<SnsCanisterRow>,
214    /// Canonically ordered typed relation gaps.
215    pub gaps: Vec<SnsCanisterGap>,
216}