use serde::{Deserialize, Serialize};
use std::fmt;
#[cfg(any(feature = "nns-host", feature = "sns-host"))]
use std::path::Path;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CollectionSourceRequest {
pub network: String,
pub endpoint: String,
pub fetched_at: String,
pub fetched_by: String,
}
impl CollectionSourceRequest {
#[must_use]
pub fn new(
network: impl Into<String>,
endpoint: impl Into<String>,
fetched_at: impl Into<String>,
fetched_by: impl Into<String>,
) -> Self {
Self {
network: network.into(),
endpoint: endpoint.into(),
fetched_at: fetched_at.into(),
fetched_by: fetched_by.into(),
}
}
#[must_use]
pub fn from_unix_secs(
network: impl Into<String>,
endpoint: impl Into<String>,
fetched_at_unix_secs: u64,
fetched_by: impl Into<String>,
) -> Self {
Self::new(
network,
endpoint,
ic_host_text::timestamp::format_utc_timestamp_secs(fetched_at_unix_secs),
fetched_by,
)
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum SortDirection {
Asc,
#[default]
Desc,
}
impl SortDirection {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Asc => "asc",
Self::Desc => "desc",
}
}
}
#[cfg(any(feature = "cloud-engine-host", feature = "dashboard-host"))]
pub(crate) fn source_provenance_mismatch(
expected: (&str, &str, &str),
actual: (&str, &str, &str),
) -> Option<String> {
[
("source_endpoint", expected.0, actual.0),
("fetched_at", expected.1, actual.1),
("fetched_by", expected.2, actual.2),
]
.into_iter()
.find_map(|(field, expected, actual)| {
(actual != expected)
.then(|| format!("{field} is {actual:?}, expected requested value {expected:?}"))
})
}
pub type SourceFuture<'a, T> = std::pin::Pin<Box<dyn std::future::Future<Output = T> + Send + 'a>>;
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(tag = "data_source", rename_all = "snake_case", deny_unknown_fields)]
pub enum ReportProvenance {
Live,
Cache {
cache_path: String,
},
}
impl ReportProvenance {
pub(crate) const fn live() -> Self {
Self::Live
}
#[cfg(any(feature = "nns-host", feature = "sns-host"))]
pub(crate) fn cache(cache_path: &Path) -> Self {
Self::Cache {
cache_path: cache_path.display().to_string(),
}
}
#[must_use]
pub const fn data_source(&self) -> ReportDataSource {
match self {
Self::Live => ReportDataSource::Live,
Self::Cache { .. } => ReportDataSource::Cache,
}
}
#[must_use]
pub fn cache_path(&self) -> Option<&str> {
match self {
Self::Live => None,
Self::Cache { cache_path } => Some(cache_path),
}
}
#[must_use]
pub const fn cache_complete(&self) -> Option<bool> {
match self {
Self::Live => None,
Self::Cache { .. } => Some(true),
}
}
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum ReportDataSource {
Live,
Cache,
}
impl ReportDataSource {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Live => "live",
Self::Cache => "cache",
}
}
}
impl fmt::Display for ReportDataSource {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(self.as_str())
}
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum ReportResultScope {
BoundedLive,
CompleteCache,
}
impl ReportResultScope {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::BoundedLive => "bounded-live",
Self::CompleteCache => "complete-cache",
}
}
}
impl fmt::Display for ReportResultScope {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(self.as_str())
}
}
#[cfg(test)]
mod tests;