use std::sync::Arc;
use hidpp::channel::HidppChannel;
use super::features::{ProbedFeatures, probe_features, read_battery};
pub(super) const REFRESH_TICKS: u64 = 15;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(super) enum CacheKey {
Bolt { unit_id: [u8; 4] },
UnifyingSlot { receiver_uid: String, slot: u8 },
Direct(async_hid::DeviceId),
}
pub(super) const CACHE_MISS_GRACE: u8 = 3;
#[derive(Clone)]
pub(super) struct Cached {
pub(super) probe: ProbedFeatures,
pub(super) battery_index: Option<u8>,
pub(super) probed_tick: u64,
}
pub(super) enum CacheOutcome {
Fresh(CacheKey, Cached),
Update(CacheKey, Cached),
Seen(CacheKey),
Unkeyed,
}
pub(super) fn seen(id: Option<CacheKey>) -> CacheOutcome {
id.map_or(CacheOutcome::Unkeyed, CacheOutcome::Seen)
}
pub(super) fn is_stale(cached: &Cached, tick: u64) -> bool {
tick.wrapping_sub(cached.probed_tick) >= REFRESH_TICKS
}
pub(super) async fn probe_or_reuse(
channel: &Arc<HidppChannel>,
index: u8,
id: Option<CacheKey>,
cached: Option<&Cached>,
online: bool,
tick: u64,
) -> (ProbedFeatures, CacheOutcome) {
if online && cached.is_none_or(|c| is_stale(c, tick)) {
let (fresh, battery_index) = probe_features(channel, index).await;
if fresh.capabilities.is_some() {
return match id {
Some(key) => {
let value = Cached {
probe: fresh.clone(),
battery_index,
probed_tick: tick,
};
(fresh, CacheOutcome::Fresh(key, value))
}
None => (fresh, CacheOutcome::Unkeyed),
};
}
return match cached {
Some(c) => (c.probe.clone(), seen(id)),
None => (fresh, seen(id)),
};
}
match cached {
Some(c) => {
if online
&& let Some(feature_index) = c.battery_index
&& let Some(key) = id.clone()
&& let Some(battery) = read_battery(channel, index, feature_index).await
{
let mut entry = c.clone();
entry.probe.battery = Some(battery);
return (entry.probe.clone(), CacheOutcome::Update(key, entry));
}
(c.probe.clone(), seen(id))
}
None => (ProbedFeatures::default(), seen(id)),
}
}