Skip to main content

ic_query/ic/
model.rs

1//! Module: ic::model
2//!
3//! Responsibility: public IC Dashboard 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;
10use std::{fmt, str::FromStr};
11#[cfg(feature = "host")]
12use thiserror::Error as ThisError;
13
14///
15/// IcMetricKind
16///
17/// One bounded network metric exposed by the official Dashboard Metrics API.
18///
19
20#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize)]
21#[serde(rename_all = "kebab-case")]
22pub enum IcMetricKind {
23    /// Network instruction execution rate.
24    InstructionRate,
25    /// Network message execution rate.
26    MessageExecutionRate,
27    /// Network cycle burn rate.
28    CycleBurnRate,
29    /// Network block ingestion rate.
30    BlockRate,
31    /// Total and currently up node counts.
32    IcNodeCount,
33    /// Total Subnet count.
34    IcSubnetTotal,
35    /// Running and stopped canister counts.
36    RegisteredCanistersCount,
37    /// Total estimated IC energy-consumption rate in kWh.
38    TotalIcEnergyConsumptionRateKwh,
39    /// Active boundary-node count.
40    BoundaryNodesCount,
41}
42
43impl IcMetricKind {
44    /// Return every metric supported by the bounded report adapter.
45    #[must_use]
46    pub const fn all() -> [Self; 9] {
47        [
48            Self::InstructionRate,
49            Self::MessageExecutionRate,
50            Self::CycleBurnRate,
51            Self::BlockRate,
52            Self::IcNodeCount,
53            Self::IcSubnetTotal,
54            Self::RegisteredCanistersCount,
55            Self::TotalIcEnergyConsumptionRateKwh,
56            Self::BoundaryNodesCount,
57        ]
58    }
59
60    /// Return the official Dashboard Metrics API path name.
61    #[must_use]
62    pub const fn as_str(self) -> &'static str {
63        match self {
64            Self::InstructionRate => "instruction-rate",
65            Self::MessageExecutionRate => "message-execution-rate",
66            Self::CycleBurnRate => "cycle-burn-rate",
67            Self::BlockRate => "block-rate",
68            Self::IcNodeCount => "ic-node-count",
69            Self::IcSubnetTotal => "ic-subnet-total",
70            Self::RegisteredCanistersCount => "registered-canisters-count",
71            Self::TotalIcEnergyConsumptionRateKwh => "total-ic-energy-consumption-rate-kwh",
72            Self::BoundaryNodesCount => "boundary-nodes-count",
73        }
74    }
75
76    #[cfg(feature = "host")]
77    pub(crate) const fn series_names(self) -> &'static [&'static str] {
78        match self {
79            Self::InstructionRate => &["instruction_rate"],
80            Self::MessageExecutionRate => &["message_execution_rate"],
81            Self::CycleBurnRate => &["cycle_burn_rate"],
82            Self::BlockRate => &["block_rate"],
83            Self::IcNodeCount => &["total_nodes", "up_nodes"],
84            Self::IcSubnetTotal => &["ic_subnet_total"],
85            Self::RegisteredCanistersCount => &["running_canisters", "stopped_canisters"],
86            Self::TotalIcEnergyConsumptionRateKwh => &["energy_consumption_rate"],
87            Self::BoundaryNodesCount => &["boundary_nodes_count"],
88        }
89    }
90}
91
92impl fmt::Display for IcMetricKind {
93    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
94        formatter.write_str(self.as_str())
95    }
96}
97
98impl FromStr for IcMetricKind {
99    type Err = String;
100
101    fn from_str(value: &str) -> Result<Self, Self::Err> {
102        Self::all()
103            .into_iter()
104            .find(|metric| metric.as_str() == value)
105            .ok_or_else(|| format!("unsupported IC Dashboard metric {value:?}"))
106    }
107}
108
109///
110/// IcMetricQuery
111///
112/// One explicitly bounded official Dashboard metric time-series query.
113///
114
115#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
116pub struct IcMetricQuery {
117    /// Official Dashboard metric to retrieve.
118    pub metric: IcMetricKind,
119    /// Inclusive query start as Unix seconds.
120    pub start_unix_secs: u64,
121    /// Inclusive query end as Unix seconds.
122    pub end_unix_secs: u64,
123    /// Requested observation interval in seconds.
124    pub step_secs: u32,
125}
126
127impl IcMetricQuery {
128    /// Construct one explicit metric query window.
129    #[must_use]
130    pub const fn new(
131        metric: IcMetricKind,
132        start_unix_secs: u64,
133        end_unix_secs: u64,
134        step_secs: u32,
135    ) -> Self {
136        Self {
137            metric,
138            start_unix_secs,
139            end_unix_secs,
140            step_secs,
141        }
142    }
143}
144
145///
146/// IcMetricRequest
147///
148/// Request accepted by the bounded official Dashboard metric report builder.
149///
150
151#[derive(Clone, Debug, Eq, PartialEq)]
152pub struct IcMetricRequest {
153    /// Dashboard Metrics API base endpoint.
154    pub source_endpoint: String,
155    /// Collection time as Unix seconds.
156    pub now_unix_secs: u64,
157    /// Explicitly bounded metric query.
158    pub query: IcMetricQuery,
159}
160
161impl IcMetricRequest {
162    /// Construct one bounded live Dashboard metric request.
163    #[must_use]
164    pub fn new(
165        source_endpoint: impl Into<String>,
166        now_unix_secs: u64,
167        query: IcMetricQuery,
168    ) -> Self {
169        Self {
170            source_endpoint: source_endpoint.into(),
171            now_unix_secs,
172            query,
173        }
174    }
175}
176
177///
178/// IcBoundaryNodeDataCentersRequest
179///
180/// Request accepted by the official Dashboard boundary-node data-center builder.
181///
182
183#[derive(Clone, Debug, Eq, PartialEq)]
184pub struct IcBoundaryNodeDataCentersRequest {
185    /// Dashboard API v4 base endpoint.
186    pub source_endpoint: String,
187    /// Collection time as Unix seconds.
188    pub now_unix_secs: u64,
189}
190
191impl IcBoundaryNodeDataCentersRequest {
192    /// Construct one live Dashboard boundary-node data-center request.
193    #[must_use]
194    pub fn new(source_endpoint: impl Into<String>, now_unix_secs: u64) -> Self {
195        Self {
196            source_endpoint: source_endpoint.into(),
197            now_unix_secs,
198        }
199    }
200}
201
202///
203/// IcCanisterRequest
204///
205/// Request accepted by the official Dashboard canister report builder.
206///
207
208#[derive(Clone, Debug, Eq, PartialEq)]
209pub struct IcCanisterRequest {
210    /// Dashboard API base endpoint.
211    pub source_endpoint: String,
212    /// Collection time as Unix seconds.
213    pub now_unix_secs: u64,
214    /// Canister principal to inspect.
215    pub canister_id: String,
216}
217
218impl IcCanisterRequest {
219    /// Construct a live Dashboard canister request.
220    #[must_use]
221    pub fn new(
222        source_endpoint: impl Into<String>,
223        now_unix_secs: u64,
224        canister_id: impl Into<String>,
225    ) -> Self {
226        Self {
227            source_endpoint: source_endpoint.into(),
228            now_unix_secs,
229            canister_id: canister_id.into(),
230        }
231    }
232}
233
234///
235/// IcCanisterFilters
236///
237/// Official Dashboard filters shared by canister count and page requests.
238///
239
240#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize)]
241pub struct IcCanisterFilters {
242    /// Select canisters according to whether the Dashboard records a name.
243    pub has_name: Option<bool>,
244    /// Select canisters assigned to this Subnet principal.
245    pub subnet_id: Option<String>,
246    /// Select canisters controlled by this principal.
247    pub controller_id: Option<String>,
248    /// Raw Dashboard language labels to include.
249    pub languages: Vec<String>,
250    /// Raw Dashboard canister classifications to include.
251    pub canister_types: Vec<String>,
252    /// Raw Dashboard text search, between two and one hundred characters.
253    pub query: Option<String>,
254}
255
256///
257/// IcCanisterCountRequest
258///
259/// Request for one bounded official Dashboard canister-count lookup.
260///
261
262#[derive(Clone, Debug, Eq, PartialEq)]
263pub struct IcCanisterCountRequest {
264    /// Dashboard API v4 base endpoint.
265    pub source_endpoint: String,
266    /// Collection time as Unix seconds.
267    pub now_unix_secs: u64,
268    /// Filters applied by the Dashboard.
269    pub filters: IcCanisterFilters,
270}
271
272impl IcCanisterCountRequest {
273    /// Construct a live Dashboard canister-count request without filters.
274    #[must_use]
275    pub fn new(source_endpoint: impl Into<String>, now_unix_secs: u64) -> Self {
276        Self {
277            source_endpoint: source_endpoint.into(),
278            now_unix_secs,
279            filters: IcCanisterFilters::default(),
280        }
281    }
282
283    /// Set the Dashboard filters used by this request.
284    #[must_use]
285    pub fn with_filters(mut self, filters: IcCanisterFilters) -> Self {
286        self.filters = filters;
287        self
288    }
289}
290
291///
292/// IcCanisterPageRequest
293///
294/// Request for one bounded official Dashboard canister page.
295///
296
297#[derive(Clone, Debug, Eq, PartialEq)]
298pub struct IcCanisterPageRequest {
299    /// Dashboard API v4 base endpoint.
300    pub source_endpoint: String,
301    /// Collection time as Unix seconds.
302    pub now_unix_secs: u64,
303    /// Filters applied by the Dashboard.
304    pub filters: IcCanisterFilters,
305    /// Maximum rows requested from the API.
306    pub limit: u16,
307    /// Exclusive forward cursor returned by an earlier page.
308    pub after: Option<String>,
309    /// Exclusive backward cursor returned by an earlier page.
310    pub before: Option<String>,
311}
312
313impl IcCanisterPageRequest {
314    /// Construct a live Dashboard page request with the default bounded limit.
315    #[must_use]
316    pub fn new(source_endpoint: impl Into<String>, now_unix_secs: u64) -> Self {
317        Self {
318            source_endpoint: source_endpoint.into(),
319            now_unix_secs,
320            filters: IcCanisterFilters::default(),
321            limit: super::DEFAULT_IC_CANISTER_PAGE_LIMIT,
322            after: None,
323            before: None,
324        }
325    }
326
327    /// Set the Dashboard filters used by this request.
328    #[must_use]
329    pub fn with_filters(mut self, filters: IcCanisterFilters) -> Self {
330        self.filters = filters;
331        self
332    }
333
334    /// Set the maximum number of returned rows.
335    #[must_use]
336    pub const fn with_limit(mut self, limit: u16) -> Self {
337        self.limit = limit;
338        self
339    }
340
341    /// Set an exclusive forward cursor.
342    #[must_use]
343    pub fn with_after(mut self, after: impl Into<String>) -> Self {
344        self.after = Some(after.into());
345        self
346    }
347
348    /// Set an exclusive backward cursor.
349    #[must_use]
350    pub fn with_before(mut self, before: impl Into<String>) -> Self {
351        self.before = Some(before.into());
352        self
353    }
354}
355
356///
357/// IcCanisterUpgrade
358///
359/// One proposal-linked canister upgrade recorded by the Dashboard API.
360///
361
362#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
363pub struct IcCanisterUpgrade {
364    /// Proposal execution time as raw Unix seconds.
365    pub executed_timestamp_seconds: u64,
366    /// Wasm module hash as raw lowercase hexadecimal text.
367    pub module_hash: String,
368    /// NNS proposal that installed this module.
369    pub proposal_id: u64,
370}
371
372///
373/// IcDashboardReportProvenance
374///
375/// Shared off-chain provenance and authority guarantees for Dashboard reports.
376///
377
378#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
379pub struct IcDashboardReportProvenance {
380    /// Report schema version.
381    pub schema_version: u32,
382    /// Network represented by the official Dashboard API.
383    pub network: String,
384    /// Authority that supplied the report fields.
385    pub authority: String,
386    /// Dashboard API base endpoint queried by the source.
387    pub source_endpoint: String,
388    /// Time this report was collected.
389    pub fetched_at: String,
390    /// Collector identity.
391    pub fetched_by: String,
392    /// Whether the API response is cryptographically certified IC state.
393    pub certified: bool,
394    /// Whether every returned value is guaranteed to describe one point in time.
395    pub point_in_time_guaranteed: bool,
396}
397
398///
399/// IcMetricObservation
400///
401/// One raw timestamp and value returned by the Dashboard Metrics API.
402///
403
404#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
405pub struct IcMetricObservation {
406    /// Observation timestamp as Unix seconds.
407    pub timestamp_unix_secs: u64,
408    /// Raw value string returned by the Dashboard.
409    pub value: String,
410}
411
412///
413/// IcMetricSeries
414///
415/// One named raw series in a Dashboard metric response.
416///
417
418#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
419pub struct IcMetricSeries {
420    /// Raw Dashboard response field that names this series.
421    pub name: String,
422    /// Observations in strictly increasing timestamp order.
423    pub observations: Vec<IcMetricObservation>,
424}
425
426///
427/// IcMetricReport
428///
429/// One bounded time-series response from the official Dashboard Metrics API.
430///
431
432#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
433pub struct IcMetricReport {
434    /// Shared Dashboard provenance, flattened in serialized report JSON.
435    #[serde(flatten)]
436    pub provenance: IcDashboardReportProvenance,
437    /// Metric and explicit time-series bounds, flattened in report JSON.
438    #[serde(flatten)]
439    pub query: IcMetricQuery,
440    /// Number of named series returned by the API.
441    pub returned_series_count: usize,
442    /// Total number of observations across all returned series.
443    pub returned_observation_count: usize,
444    /// Raw named time series in canonical series-name order.
445    pub series: Vec<IcMetricSeries>,
446}
447
448///
449/// IcBoundaryNodeDataCenterRow
450///
451/// One raw data-center aggregate returned by the boundary-node API.
452///
453
454#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
455pub struct IcBoundaryNodeDataCenterRow {
456    /// Dashboard data-center identifier.
457    pub dc_id: String,
458    /// Raw data-center display name.
459    pub name: String,
460    /// Raw infrastructure-owner label.
461    pub owner: String,
462    /// Raw Dashboard region label.
463    pub region: String,
464    /// Raw decimal latitude.
465    pub latitude: String,
466    /// Raw decimal longitude.
467    pub longitude: String,
468    /// Raw decimal count of boundary nodes assigned to this data center.
469    pub total_nodes: String,
470}
471
472///
473/// IcBoundaryNodeDataCentersReport
474///
475/// One complete response from the official boundary-node data-center resource.
476///
477
478#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
479pub struct IcBoundaryNodeDataCentersReport {
480    /// Shared Dashboard provenance, flattened in serialized report JSON.
481    #[serde(flatten)]
482    pub provenance: IcDashboardReportProvenance,
483    /// Number of data-center rows returned by the API.
484    pub data_center_count: usize,
485    /// Sum of the raw per-data-center boundary-node counts.
486    pub total_node_count: u64,
487    /// Rows in canonical data-center-id order, including zero-node locations.
488    pub rows: Vec<IcBoundaryNodeDataCenterRow>,
489}
490
491///
492/// IcCanisterReport
493///
494/// One live canister metadata report from the official Dashboard API.
495///
496
497#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
498pub struct IcCanisterReport {
499    /// Shared Dashboard provenance, flattened in serialized report JSON.
500    #[serde(flatten)]
501    pub provenance: IcDashboardReportProvenance,
502    /// Canonical canister principal.
503    pub canister_id: String,
504    /// Dashboard database row identifier.
505    pub dashboard_id: u64,
506    /// Raw optional Dashboard canister classification.
507    pub canister_type: Option<String>,
508    /// Raw Dashboard canister name; an empty string means no name was recorded.
509    pub name: String,
510    /// Canonical Subnet principal recorded by the Dashboard.
511    pub subnet_id: String,
512    /// Canonically ordered controller principals recorded by the Dashboard.
513    pub controllers: Vec<String>,
514    /// Raw Dashboard language label; an empty string means no language was recorded.
515    pub language: String,
516    /// Raw current module hash; an empty string means no hash was recorded.
517    pub module_hash: String,
518    /// Raw Dashboard row update timestamp.
519    pub dashboard_updated_at: String,
520    /// Number of proposal-linked upgrades when history is available.
521    pub upgrade_count: Option<usize>,
522    /// Proposal-linked upgrade history, or `None` when the Dashboard returned `null`.
523    pub upgrades: Option<Vec<IcCanisterUpgrade>>,
524}
525
526///
527/// IcCanisterCountReport
528///
529/// One filtered canister count from the official Dashboard API.
530///
531
532#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
533pub struct IcCanisterCountReport {
534    /// Shared Dashboard provenance, flattened in serialized report JSON.
535    #[serde(flatten)]
536    pub provenance: IcDashboardReportProvenance,
537    /// Filters applied by the Dashboard.
538    pub filters: IcCanisterFilters,
539    /// Number of matching Dashboard canister records.
540    pub total: u64,
541}
542
543///
544/// IcCanisterPageController
545///
546/// One controller entry returned by the Dashboard canister collection API.
547///
548
549#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
550pub struct IcCanisterPageController {
551    /// Canonical controller principal.
552    pub principal_id: String,
553    /// Raw optional Dashboard metadata associated with the controller.
554    pub raw_metadata: Option<String>,
555}
556
557///
558/// IcCanisterPageRow
559///
560/// One discovery row from a bounded Dashboard canister page.
561///
562
563#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
564pub struct IcCanisterPageRow {
565    /// Canonical canister principal.
566    pub canister_id: String,
567    /// Dashboard database row identifier.
568    pub dashboard_id: u64,
569    /// Raw optional Dashboard canister classification.
570    pub canister_type: Option<String>,
571    /// Raw Dashboard canister name.
572    pub name: String,
573    /// Canonical Subnet principal recorded by the Dashboard.
574    pub subnet_id: String,
575    /// Canonically ordered controller entries recorded by the Dashboard.
576    pub controllers: Vec<IcCanisterPageController>,
577    /// Raw Dashboard language label.
578    pub language: String,
579    /// Raw current module hash.
580    pub module_hash: String,
581    /// Raw Dashboard row update timestamp.
582    pub dashboard_updated_at: String,
583}
584
585///
586/// IcCanisterPageReport
587///
588/// One explicitly bounded page from the official Dashboard canister collection.
589///
590
591#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
592pub struct IcCanisterPageReport {
593    /// Shared Dashboard provenance, flattened in serialized report JSON.
594    #[serde(flatten)]
595    pub provenance: IcDashboardReportProvenance,
596    /// Filters applied by the Dashboard.
597    pub filters: IcCanisterFilters,
598    /// Maximum rows requested from the API.
599    pub requested_limit: u16,
600    /// Number of rows returned in this report.
601    pub returned_count: usize,
602    /// Exclusive forward cursor supplied to this request.
603    pub after: Option<String>,
604    /// Exclusive backward cursor supplied to this request.
605    pub before: Option<String>,
606    /// Cursor for an explicit request for the preceding page.
607    pub previous_cursor: Option<String>,
608    /// Cursor for an explicit request for the following page.
609    pub next_cursor: Option<String>,
610    /// Canister discovery rows in Dashboard canister-id order.
611    pub rows: Vec<IcCanisterPageRow>,
612}
613
614///
615/// IcSourceRequest
616///
617/// Shared endpoint and collection provenance for IC Dashboard source calls and results.
618///
619
620#[cfg(feature = "host")]
621#[derive(Clone, Debug, Eq, PartialEq)]
622pub struct IcSourceRequest {
623    /// Dashboard API base endpoint.
624    pub endpoint: String,
625    /// Collection timestamp in UTC.
626    pub fetched_at: String,
627    /// Collector identity recorded in report provenance.
628    pub fetched_by: String,
629}
630
631#[cfg(feature = "host")]
632impl IcSourceRequest {
633    /// Construct source-call provenance.
634    #[must_use]
635    pub fn new(
636        endpoint: impl Into<String>,
637        fetched_at: impl Into<String>,
638        fetched_by: impl Into<String>,
639    ) -> Self {
640        Self {
641            endpoint: endpoint.into(),
642            fetched_at: fetched_at.into(),
643            fetched_by: fetched_by.into(),
644        }
645    }
646}
647
648///
649/// IcCanisterSourceData
650///
651/// Raw canister metadata and provenance returned by an IC Dashboard source.
652///
653
654#[cfg(feature = "host")]
655#[derive(Clone, Debug, Eq, PartialEq)]
656pub struct IcCanisterSourceData {
657    /// Source request and provenance preserved by the source.
658    pub source: IcSourceRequest,
659    /// Canister principal returned by the Dashboard.
660    pub canister_id: String,
661    /// Dashboard database row identifier.
662    pub dashboard_id: u64,
663    /// Raw optional Dashboard canister classification.
664    pub canister_type: Option<String>,
665    /// Raw Dashboard canister name.
666    pub name: String,
667    /// Subnet principal returned by the Dashboard.
668    pub subnet_id: String,
669    /// Controller principals returned by the Dashboard.
670    pub controllers: Vec<String>,
671    /// Raw Dashboard language label.
672    pub language: String,
673    /// Raw current module hash.
674    pub module_hash: String,
675    /// Raw Dashboard row update timestamp.
676    pub dashboard_updated_at: String,
677    /// Proposal-linked upgrades, or `None` when the Dashboard returned `null`.
678    pub upgrades: Option<Vec<IcCanisterUpgrade>>,
679}
680
681///
682/// IcCanisterCountSourceData
683///
684/// Raw filtered count and provenance returned by a Dashboard source.
685///
686
687#[cfg(feature = "host")]
688#[derive(Clone, Debug, Eq, PartialEq)]
689pub struct IcCanisterCountSourceData {
690    /// Source request and provenance preserved by the source.
691    pub source: IcSourceRequest,
692    /// Filters applied by the source.
693    pub filters: IcCanisterFilters,
694    /// Number of matching Dashboard canister records.
695    pub total: u64,
696}
697
698///
699/// IcCanisterPageSourceData
700///
701/// Raw bounded canister page and provenance returned by a Dashboard source.
702///
703
704#[cfg(feature = "host")]
705#[derive(Clone, Debug, Eq, PartialEq)]
706pub struct IcCanisterPageSourceData {
707    /// Source request and provenance preserved by the source.
708    pub source: IcSourceRequest,
709    /// Filters applied by the source.
710    pub filters: IcCanisterFilters,
711    /// Maximum rows requested from the source.
712    pub requested_limit: u16,
713    /// Exclusive forward cursor supplied to the source.
714    pub after: Option<String>,
715    /// Exclusive backward cursor supplied to the source.
716    pub before: Option<String>,
717    /// Cursor for an explicit request for the preceding page.
718    pub previous_cursor: Option<String>,
719    /// Cursor for an explicit request for the following page.
720    pub next_cursor: Option<String>,
721    /// Canister discovery rows returned by the source.
722    pub rows: Vec<IcCanisterPageRow>,
723}
724
725///
726/// IcMetricSourceData
727///
728/// Raw bounded metric series and provenance returned by a Dashboard source.
729///
730
731#[cfg(feature = "host")]
732#[derive(Clone, Debug, Eq, PartialEq)]
733pub struct IcMetricSourceData {
734    /// Source request and provenance preserved by the source.
735    pub source: IcSourceRequest,
736    /// Metric query applied by the source.
737    pub query: IcMetricQuery,
738    /// Raw named time series returned by the source.
739    pub series: Vec<IcMetricSeries>,
740}
741
742///
743/// IcBoundaryNodeDataCentersSourceData
744///
745/// Raw boundary-node data-center rows and provenance returned by a Dashboard source.
746///
747
748#[cfg(feature = "host")]
749#[derive(Clone, Debug, Eq, PartialEq)]
750pub struct IcBoundaryNodeDataCentersSourceData {
751    /// Source request and provenance preserved by the source.
752    pub source: IcSourceRequest,
753    /// Raw data-center rows returned by the source.
754    pub rows: Vec<IcBoundaryNodeDataCenterRow>,
755}
756
757///
758/// IcHostError
759///
760/// Typed error returned by IC Dashboard report builders and live sources.
761///
762
763#[cfg(feature = "host")]
764#[derive(Debug, ThisError)]
765pub enum IcHostError {
766    /// The synchronous adapter could not create its local async runtime.
767    #[error("failed to run IC Dashboard query: {0}")]
768    Runtime(#[from] RuntimeError),
769
770    /// A request supplied an invalid canister principal.
771    #[error("invalid {field}: {reason}")]
772    InvalidPrincipal {
773        /// Principal field being validated.
774        field: &'static str,
775        /// Principal parser diagnostic.
776        reason: String,
777    },
778
779    /// A request violates the bounded Dashboard query contract.
780    #[error("invalid {field}: {reason}")]
781    InvalidRequest {
782        /// Request field being validated.
783        field: &'static str,
784        /// Deterministic validation diagnostic.
785        reason: String,
786    },
787
788    /// The Dashboard API base endpoint is malformed or unsupported.
789    #[error("invalid IC Dashboard endpoint {endpoint}: {reason}")]
790    InvalidEndpoint {
791        /// Rejected endpoint.
792        endpoint: String,
793        /// URL validation diagnostic.
794        reason: String,
795    },
796
797    /// The HTTP client could not be constructed.
798    #[error("failed to build IC Dashboard HTTP client: {reason}")]
799    HttpClientBuild {
800        /// HTTP client construction diagnostic.
801        reason: String,
802    },
803
804    /// The live Dashboard request failed before a response was received.
805    #[error("IC Dashboard request to {url} failed: {reason}")]
806    HttpRequest {
807        /// Fully resolved request URL.
808        url: String,
809        /// HTTP transport error.
810        reason: String,
811    },
812
813    /// The Dashboard returned a non-success HTTP status.
814    #[error("IC Dashboard request to {url} returned HTTP status {status}")]
815    HttpStatus {
816        /// Fully resolved request URL.
817        url: String,
818        /// Numeric HTTP status.
819        status: u16,
820    },
821
822    /// The Dashboard response did not match the expected JSON shape.
823    #[error("failed to decode IC Dashboard response from {url}: {reason}")]
824    JsonDecode {
825        /// Fully resolved request URL.
826        url: String,
827        /// JSON response decoding error.
828        reason: String,
829    },
830
831    /// A source capability returned data that violates its public result contract.
832    #[error("invalid IC Dashboard source data: {reason}")]
833    InvalidSourceData {
834        /// Deterministic invariant failure.
835        reason: String,
836    },
837}