use std::convert::Infallible;
use std::fmt::Write as _;
use std::io;
use std::time::Duration;
use flagset::{FlagSet, flags};
use serde_json as json;
use thiserror::Error;
use crate::{PrevEntry, UpdateFn};
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum QueryError {
#[error("cache miss")]
Miss,
#[error("io error")]
Io(#[from] io::Error),
#[error("deserialization error")]
BadData(#[from] json::Error),
}
flags! {
pub enum QueryPolicy: u16 {
UpdateAlways,
UpdateBadData,
UpdateChecksumMismatch,
UpdateExpired,
ReturnAlways,
ReturnBadDataErr,
ReturnChecksumMismatch,
ReturnExpired,
}
}
impl QueryPolicy {
pub fn default_set() -> FlagSet<Self> {
QueryPolicy::UpdateBadData
| QueryPolicy::UpdateChecksumMismatch
| QueryPolicy::UpdateExpired
| QueryPolicy::ReturnExpired
}
}
pub struct Query<'a, T, E = Infallible> {
pub(crate) key: &'a str,
pub(crate) update_fn: Option<UpdateFn<'a, T, E>>,
pub(crate) policy: Option<FlagSet<QueryPolicy>>,
pub(crate) checksum: Option<String>,
pub(crate) ttl: Option<Duration>,
pub(crate) initial_poll: Option<Duration>,
}
impl<'a> Query<'a, (), Infallible> {
#[inline]
pub fn new(key: &'a str) -> Self {
Query {
key,
update_fn: None,
policy: None,
checksum: None,
ttl: None,
initial_poll: None,
}
}
#[inline]
pub fn update_fn<F, T, E>(self, update_fn: F) -> Query<'a, T, E>
where
F: FnOnce(Option<PrevEntry<T>>) -> Result<T, E> + 'a,
{
Query {
key: self.key,
checksum: self.checksum,
policy: self.policy,
ttl: self.ttl,
initial_poll: self.initial_poll,
update_fn: Some(Box::new(update_fn)),
}
}
}
impl<T, E> Query<'_, T, E> {
#[inline]
pub fn checksum<C>(mut self, checksum: C) -> Self
where
C: AsRef<[u8]>,
{
self.checksum = Some(to_hex(checksum.as_ref()));
self
}
#[inline]
pub fn policy(mut self, policy: impl Into<FlagSet<QueryPolicy>>) -> Self {
self.policy = Some(policy.into());
self
}
#[inline]
pub fn ttl(mut self, ttl: Duration) -> Self {
self.ttl = Some(ttl);
self
}
#[inline]
pub fn initial_poll(mut self, initial_poll: Duration) -> Self {
self.initial_poll = Some(initial_poll);
self
}
}
fn to_hex(b: &[u8]) -> String {
let mut s = String::with_capacity(b.len() * 2);
for byte in b {
write!(&mut s, "{:02x}", byte).unwrap();
}
s
}