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 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}