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