use schemars::JsonSchema;
use serde::Serialize;
use super::{IndexingState, NotificationCache};
use crate::config::ServerId;
use crate::redaction::{Redactions, ServerText};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, JsonSchema)]
pub struct IndexingSignal {
pub indexing_in_progress: bool,
}
impl IndexingSignal {
#[must_use]
pub fn sample(cache: &NotificationCache, route_id: Option<&ServerId>) -> Self {
Self {
indexing_in_progress: route_id
.is_some_and(|id| cache.indexing_state(id) == IndexingState::Loading),
}
}
#[must_use]
pub const fn union(self, later: Self) -> Self {
Self {
indexing_in_progress: self.indexing_in_progress || later.indexing_in_progress,
}
}
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct Indexed<T> {
#[serde(flatten)]
pub result: T,
#[serde(flatten)]
pub indexing: IndexingSignal,
}
impl<T> Indexed<T> {
#[must_use]
pub const fn new(result: T, indexing: IndexingSignal) -> Self {
Self { result, indexing }
}
}
impl<T: ServerText> ServerText for Indexed<T> {
fn redact_server_text(&mut self, redactions: &Redactions) {
let Self {
result,
indexing: _,
} = self;
result.redact_server_text(redactions);
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, JsonSchema)]
pub struct RouteSignals {
pub push_notifications_degraded: bool,
#[serde(flatten)]
pub indexing: IndexingSignal,
}
impl RouteSignals {
#[must_use]
pub fn sample(cache: &NotificationCache, route_id: Option<&ServerId>) -> Self {
Self {
push_notifications_degraded: route_id.is_some_and(|id| cache.is_push_degraded(id)),
indexing: IndexingSignal::sample(cache, route_id),
}
}
#[must_use]
pub const fn union(self, later: Self) -> Self {
Self {
push_notifications_degraded: self.push_notifications_degraded
|| later.push_notifications_degraded,
indexing: self.indexing.union(later.indexing),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_unknown_route_samples_no_signal() {
let cache = NotificationCache::new();
assert_eq!(RouteSignals::sample(&cache, None), RouteSignals::default());
let id = ServerId::from_static("rust");
assert_eq!(
RouteSignals::sample(&cache, Some(&id)),
RouteSignals::default()
);
}
#[test]
fn test_union_keeps_a_signal_set_in_either_sample() {
let degraded = RouteSignals {
push_notifications_degraded: true,
..RouteSignals::default()
};
let loading = RouteSignals {
indexing: IndexingSignal {
indexing_in_progress: true,
},
..RouteSignals::default()
};
let both = degraded.union(loading);
assert!(both.push_notifications_degraded);
assert!(both.indexing.indexing_in_progress);
assert_eq!(loading.union(degraded), both);
}
#[test]
fn test_indexed_flattens_the_signal_beside_the_result() {
#[derive(Serialize)]
struct Items {
items: Vec<u8>,
}
let indexed = Indexed::new(
Items { items: vec![1] },
IndexingSignal {
indexing_in_progress: true,
},
);
assert_eq!(
serde_json::to_string(&indexed).unwrap(),
r#"{"items":[1],"indexing_in_progress":true}"#
);
}
#[test]
fn test_indexing_half_flattens_into_one_object() {
let signals = RouteSignals {
push_notifications_degraded: true,
indexing: IndexingSignal {
indexing_in_progress: true,
},
};
assert_eq!(
serde_json::to_string(&signals).unwrap(),
r#"{"push_notifications_degraded":true,"indexing_in_progress":true}"#
);
assert_eq!(
serde_json::to_string(&IndexingSignal::default()).unwrap(),
r#"{"indexing_in_progress":false}"#
);
}
}