1use serde::{Deserialize, Serialize};
4use std::fmt;
5#[cfg(any(feature = "nns-host", feature = "sns-host"))]
6use std::path::Path;
7
8#[derive(Clone, Debug, Eq, PartialEq)]
16pub struct CollectionSourceRequest {
17 pub network: String,
19 pub endpoint: String,
21 pub fetched_at: String,
23 pub fetched_by: String,
25}
26
27impl CollectionSourceRequest {
28 #[must_use]
30 pub fn new(
31 network: impl Into<String>,
32 endpoint: impl Into<String>,
33 fetched_at: impl Into<String>,
34 fetched_by: impl Into<String>,
35 ) -> Self {
36 Self {
37 network: network.into(),
38 endpoint: endpoint.into(),
39 fetched_at: fetched_at.into(),
40 fetched_by: fetched_by.into(),
41 }
42 }
43
44 #[must_use]
46 pub fn from_unix_secs(
47 network: impl Into<String>,
48 endpoint: impl Into<String>,
49 fetched_at_unix_secs: u64,
50 fetched_by: impl Into<String>,
51 ) -> Self {
52 Self::new(
53 network,
54 endpoint,
55 ic_host_text::timestamp::format_utc_timestamp_secs(fetched_at_unix_secs),
56 fetched_by,
57 )
58 }
59}
60
61#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
68pub enum SortDirection {
69 Asc,
71 #[default]
73 Desc,
74}
75
76impl SortDirection {
77 #[must_use]
79 pub const fn as_str(self) -> &'static str {
80 match self {
81 Self::Asc => "asc",
82 Self::Desc => "desc",
83 }
84 }
85}
86
87#[cfg(any(feature = "cloud-engine-host", feature = "dashboard-host"))]
89pub(crate) fn source_provenance_mismatch(
90 expected: (&str, &str, &str),
91 actual: (&str, &str, &str),
92) -> Option<String> {
93 [
94 ("source_endpoint", expected.0, actual.0),
95 ("fetched_at", expected.1, actual.1),
96 ("fetched_by", expected.2, actual.2),
97 ]
98 .into_iter()
99 .find_map(|(field, expected, actual)| {
100 (actual != expected)
101 .then(|| format!("{field} is {actual:?}, expected requested value {expected:?}"))
102 })
103}
104
105pub type SourceFuture<'a, T> = std::pin::Pin<Box<dyn std::future::Future<Output = T> + Send + 'a>>;
112
113#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
120#[serde(tag = "data_source", rename_all = "snake_case", deny_unknown_fields)]
121pub enum ReportProvenance {
122 Live,
124 Cache {
126 cache_path: String,
128 },
129}
130
131impl ReportProvenance {
132 pub(crate) const fn live() -> Self {
134 Self::Live
135 }
136
137 #[cfg(any(feature = "nns-host", feature = "sns-host"))]
139 pub(crate) fn cache(cache_path: &Path) -> Self {
140 Self::Cache {
141 cache_path: cache_path.display().to_string(),
142 }
143 }
144
145 #[must_use]
147 pub const fn data_source(&self) -> ReportDataSource {
148 match self {
149 Self::Live => ReportDataSource::Live,
150 Self::Cache { .. } => ReportDataSource::Cache,
151 }
152 }
153
154 #[must_use]
156 pub fn cache_path(&self) -> Option<&str> {
157 match self {
158 Self::Live => None,
159 Self::Cache { cache_path } => Some(cache_path),
160 }
161 }
162
163 #[must_use]
165 pub const fn cache_complete(&self) -> Option<bool> {
166 match self {
167 Self::Live => None,
168 Self::Cache { .. } => Some(true),
169 }
170 }
171}
172
173#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
180#[serde(rename_all = "snake_case")]
181pub enum ReportDataSource {
182 Live,
184 Cache,
186}
187
188impl ReportDataSource {
189 #[must_use]
191 pub const fn as_str(self) -> &'static str {
192 match self {
193 Self::Live => "live",
194 Self::Cache => "cache",
195 }
196 }
197}
198
199impl fmt::Display for ReportDataSource {
200 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
201 formatter.write_str(self.as_str())
202 }
203}
204
205#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
212#[serde(rename_all = "kebab-case")]
213pub enum ReportResultScope {
214 BoundedLive,
216 CompleteCache,
218}
219
220impl ReportResultScope {
221 #[must_use]
223 pub const fn as_str(self) -> &'static str {
224 match self {
225 Self::BoundedLive => "bounded-live",
226 Self::CompleteCache => "complete-cache",
227 }
228 }
229}
230
231impl fmt::Display for ReportResultScope {
232 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
233 formatter.write_str(self.as_str())
234 }
235}
236
237#[cfg(test)]
238mod tests;