Skip to main content

ic_query/
report.rs

1//! Shared report provenance classifications.
2
3use serde::{Deserialize, Serialize};
4use std::fmt;
5
6///
7/// SourceFuture
8///
9/// Caller-runtime future returned by an object-safe report source capability.
10///
11
12pub type SourceFuture<'a, T> = std::pin::Pin<Box<dyn std::future::Future<Output = T> + Send + 'a>>;
13
14///
15/// ReportDataSource
16///
17/// Origin of the data exposed by a report.
18///
19
20#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
21#[serde(rename_all = "snake_case")]
22pub enum ReportDataSource {
23    /// Rows were collected from live IC query calls.
24    Live,
25    /// Rows were read from a complete local snapshot.
26    Cache,
27}
28
29impl ReportDataSource {
30    /// Return the stable JSON and text label.
31    #[must_use]
32    pub const fn as_str(self) -> &'static str {
33        match self {
34            Self::Live => "live",
35            Self::Cache => "cache",
36        }
37    }
38}
39
40impl fmt::Display for ReportDataSource {
41    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
42        formatter.write_str(self.as_str())
43    }
44}
45
46///
47/// ReportResultScope
48///
49/// Completeness boundary represented by a report view.
50///
51
52#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
53#[serde(rename_all = "kebab-case")]
54pub enum ReportResultScope {
55    /// A bounded page or detail view collected live.
56    BoundedLive,
57    /// A view derived from an API-exhausted complete cache.
58    CompleteCache,
59}
60
61impl ReportResultScope {
62    /// Return the stable JSON and text label.
63    #[must_use]
64    pub const fn as_str(self) -> &'static str {
65        match self {
66            Self::BoundedLive => "bounded-live",
67            Self::CompleteCache => "complete-cache",
68        }
69    }
70}
71
72impl fmt::Display for ReportResultScope {
73    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
74        formatter.write_str(self.as_str())
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use super::{ReportDataSource, ReportResultScope};
81
82    #[test]
83    fn report_provenance_labels_round_trip() {
84        for (source, label) in [
85            (ReportDataSource::Live, "live"),
86            (ReportDataSource::Cache, "cache"),
87        ] {
88            assert_eq!(source.as_str(), label);
89            assert_eq!(source.to_string(), label);
90            assert_eq!(
91                serde_json::to_string(&source).expect("serialize report data source"),
92                format!("\"{label}\"")
93            );
94            assert_eq!(
95                serde_json::from_str::<ReportDataSource>(&format!("\"{label}\""))
96                    .expect("deserialize report data source"),
97                source
98            );
99        }
100        for (scope, label) in [
101            (ReportResultScope::BoundedLive, "bounded-live"),
102            (ReportResultScope::CompleteCache, "complete-cache"),
103        ] {
104            assert_eq!(scope.as_str(), label);
105            assert_eq!(scope.to_string(), label);
106            assert_eq!(
107                serde_json::to_string(&scope).expect("serialize report result scope"),
108                format!("\"{label}\"")
109            );
110            assert_eq!(
111                serde_json::from_str::<ReportResultScope>(&format!("\"{label}\""))
112                    .expect("deserialize report result scope"),
113                scope
114            );
115        }
116        assert!(serde_json::from_str::<ReportDataSource>("\"api\"").is_err());
117        assert!(serde_json::from_str::<ReportResultScope>("\"partial\"").is_err());
118    }
119}