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