ic_query/ic/model.rs
1//! Module: ic::model
2//!
3//! Responsibility: public IC Dashboard canister requests, source data, reports, and errors.
4//! Does not own: HTTP transport, source validation, report assembly, or rendering.
5//! Boundary: preserves raw Dashboard values and explicit off-chain provenance.
6
7#[cfg(feature = "host")]
8use crate::runtime::RuntimeError;
9use serde::Serialize;
10#[cfg(feature = "host")]
11use thiserror::Error as ThisError;
12
13///
14/// IcCanisterRequest
15///
16/// Request accepted by the official Dashboard canister report builder.
17///
18
19#[derive(Clone, Debug, Eq, PartialEq)]
20pub struct IcCanisterRequest {
21 /// Dashboard API base endpoint.
22 pub source_endpoint: String,
23 /// Collection time as Unix seconds.
24 pub now_unix_secs: u64,
25 /// Canister principal to inspect.
26 pub canister_id: String,
27}
28
29impl IcCanisterRequest {
30 /// Construct a live Dashboard canister request.
31 #[must_use]
32 pub fn new(
33 source_endpoint: impl Into<String>,
34 now_unix_secs: u64,
35 canister_id: impl Into<String>,
36 ) -> Self {
37 Self {
38 source_endpoint: source_endpoint.into(),
39 now_unix_secs,
40 canister_id: canister_id.into(),
41 }
42 }
43}
44
45///
46/// IcCanisterUpgrade
47///
48/// One proposal-linked canister upgrade recorded by the Dashboard API.
49///
50
51#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
52pub struct IcCanisterUpgrade {
53 /// Proposal execution time as raw Unix seconds.
54 pub executed_timestamp_seconds: u64,
55 /// Wasm module hash as raw lowercase hexadecimal text.
56 pub module_hash: String,
57 /// NNS proposal that installed this module.
58 pub proposal_id: u64,
59}
60
61///
62/// IcCanisterReport
63///
64/// One live canister metadata report from the official Dashboard API.
65///
66
67#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
68pub struct IcCanisterReport {
69 /// Report schema version.
70 pub schema_version: u32,
71 /// Network represented by the official Dashboard API.
72 pub network: String,
73 /// Authority that supplied the report fields.
74 pub authority: String,
75 /// Dashboard API base endpoint queried by the source.
76 pub source_endpoint: String,
77 /// Time this report was collected.
78 pub fetched_at: String,
79 /// Collector identity.
80 pub fetched_by: String,
81 /// Whether the API response is cryptographically certified IC state.
82 pub certified: bool,
83 /// Whether every returned value is guaranteed to describe one point in time.
84 pub point_in_time_guaranteed: bool,
85 /// Canonical canister principal.
86 pub canister_id: String,
87 /// Dashboard database row identifier.
88 pub dashboard_id: u64,
89 /// Raw optional Dashboard canister classification.
90 pub canister_type: Option<String>,
91 /// Raw Dashboard canister name; an empty string means no name was recorded.
92 pub name: String,
93 /// Canonical Subnet principal recorded by the Dashboard.
94 pub subnet_id: String,
95 /// Canonically ordered controller principals recorded by the Dashboard.
96 pub controllers: Vec<String>,
97 /// Raw Dashboard language label; an empty string means no language was recorded.
98 pub language: String,
99 /// Raw current module hash; an empty string means no hash was recorded.
100 pub module_hash: String,
101 /// Raw Dashboard row update timestamp.
102 pub dashboard_updated_at: String,
103 /// Number of proposal-linked upgrades when history is available.
104 pub upgrade_count: Option<usize>,
105 /// Proposal-linked upgrade history, or `None` when the Dashboard returned `null`.
106 pub upgrades: Option<Vec<IcCanisterUpgrade>>,
107}
108
109///
110/// IcSourceRequest
111///
112/// Shared endpoint and collection provenance for IC Dashboard source calls.
113///
114
115#[cfg(feature = "host")]
116#[derive(Clone, Debug, Eq, PartialEq)]
117pub struct IcSourceRequest {
118 /// Dashboard API base endpoint.
119 pub endpoint: String,
120 /// Collection timestamp in UTC.
121 pub fetched_at: String,
122 /// Collector identity recorded in report provenance.
123 pub fetched_by: String,
124}
125
126#[cfg(feature = "host")]
127impl IcSourceRequest {
128 /// Construct source-call provenance.
129 #[must_use]
130 pub fn new(
131 endpoint: impl Into<String>,
132 fetched_at: impl Into<String>,
133 fetched_by: impl Into<String>,
134 ) -> Self {
135 Self {
136 endpoint: endpoint.into(),
137 fetched_at: fetched_at.into(),
138 fetched_by: fetched_by.into(),
139 }
140 }
141}
142
143///
144/// IcCanisterSourceData
145///
146/// Raw canister metadata and provenance returned by an IC Dashboard source.
147///
148
149#[cfg(feature = "host")]
150#[derive(Clone, Debug, Eq, PartialEq)]
151pub struct IcCanisterSourceData {
152 /// Dashboard API base endpoint used by the source.
153 pub source_endpoint: String,
154 /// Collection timestamp supplied in the source request.
155 pub fetched_at: String,
156 /// Collector identity supplied in the source request.
157 pub fetched_by: String,
158 /// Canister principal returned by the Dashboard.
159 pub canister_id: String,
160 /// Dashboard database row identifier.
161 pub dashboard_id: u64,
162 /// Raw optional Dashboard canister classification.
163 pub canister_type: Option<String>,
164 /// Raw Dashboard canister name.
165 pub name: String,
166 /// Subnet principal returned by the Dashboard.
167 pub subnet_id: String,
168 /// Controller principals returned by the Dashboard.
169 pub controllers: Vec<String>,
170 /// Raw Dashboard language label.
171 pub language: String,
172 /// Raw current module hash.
173 pub module_hash: String,
174 /// Raw Dashboard row update timestamp.
175 pub dashboard_updated_at: String,
176 /// Proposal-linked upgrades, or `None` when the Dashboard returned `null`.
177 pub upgrades: Option<Vec<IcCanisterUpgrade>>,
178}
179
180///
181/// IcHostError
182///
183/// Typed error returned by IC Dashboard report builders and live sources.
184///
185
186#[cfg(feature = "host")]
187#[derive(Debug, ThisError)]
188pub enum IcHostError {
189 /// The synchronous adapter could not create its local async runtime.
190 #[error("failed to run IC Dashboard query: {0}")]
191 Runtime(#[from] RuntimeError),
192
193 /// A request supplied an invalid canister principal.
194 #[error("invalid {field}: {reason}")]
195 InvalidPrincipal {
196 /// Principal field being validated.
197 field: &'static str,
198 /// Principal parser diagnostic.
199 reason: String,
200 },
201
202 /// The Dashboard API base endpoint is malformed or unsupported.
203 #[error("invalid IC Dashboard endpoint {endpoint}: {reason}")]
204 InvalidEndpoint {
205 /// Rejected endpoint.
206 endpoint: String,
207 /// URL validation diagnostic.
208 reason: String,
209 },
210
211 /// The HTTP client could not be constructed.
212 #[error("failed to build IC Dashboard HTTP client: {reason}")]
213 HttpClientBuild {
214 /// HTTP client construction diagnostic.
215 reason: String,
216 },
217
218 /// The live Dashboard request failed before a response was received.
219 #[error("IC Dashboard request to {url} failed: {reason}")]
220 HttpRequest {
221 /// Fully resolved request URL.
222 url: String,
223 /// HTTP transport error.
224 reason: String,
225 },
226
227 /// The Dashboard returned a non-success HTTP status.
228 #[error("IC Dashboard request to {url} returned HTTP status {status}")]
229 HttpStatus {
230 /// Fully resolved request URL.
231 url: String,
232 /// Numeric HTTP status.
233 status: u16,
234 },
235
236 /// The Dashboard response did not match the expected JSON shape.
237 #[error("failed to decode IC Dashboard response from {url}: {reason}")]
238 JsonDecode {
239 /// Fully resolved request URL.
240 url: String,
241 /// JSON response decoding error.
242 reason: String,
243 },
244
245 /// A source capability returned data that violates its public result contract.
246 #[error("invalid IC Dashboard canister source data: {reason}")]
247 InvalidSourceData {
248 /// Deterministic invariant failure.
249 reason: String,
250 },
251}