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}