#[cfg(any(feature = "json", feature = "toml", feature = "ron"))]
use crate::store::WhenItWillNotRead;
use crate::store::builder::{Backend, Holds};
use crate::store::{StorageError, WillNotOpen};
use error_stack::Report;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::Duration;
#[derive(Clone)]
#[non_exhaustive]
pub struct RetryPolicy {
pub interval: Duration,
pub budget: Duration,
}
#[derive(Clone)]
#[non_exhaustive]
pub struct Disk {
pub save_debounce: Duration,
pub watch_debounce: Duration,
pub retry_policy: RetryPolicy,
}
impl Disk {
pub fn debounce(mut self, every: Duration) -> Self {
self.save_debounce = every;
self
}
pub fn watch_every(mut self, every: Duration) -> Self {
self.watch_debounce = every;
self
}
pub fn retry_every(mut self, every: Duration) -> Self {
self.retry_policy.interval = every;
self
}
pub fn give_up_after(mut self, within: Duration) -> Self {
self.retry_policy.budget = within;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub struct WriteAttempts {
pub attempts: usize,
pub pause: Duration,
}
impl WriteAttempts {
pub const fn times(attempts: usize) -> Self {
Self {
attempts,
pause: Duration::ZERO,
}
}
pub const fn apart(self, pause: Duration) -> Self {
Self { pause, ..self }
}
pub const fn once() -> Self {
Self::times(1)
}
pub const fn budget(self) -> Duration {
Duration::from_nanos(self.pause.as_nanos() as u64 * self.attempts.saturating_sub(1) as u64)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub struct FileWritePolicy {
pub write: WriteAttempts,
pub replace: WriteAttempts,
}
impl FileWritePolicy {
pub const fn writing(self, attempts: WriteAttempts) -> Self {
Self {
write: attempts,
..self
}
}
pub const fn replacing(self, attempts: WriteAttempts) -> Self {
Self {
replace: attempts,
..self
}
}
}
impl Default for FileWritePolicy {
fn default() -> Self {
Self {
write: WriteAttempts::times(3).apart(Duration::from_millis(15)),
replace: WriteAttempts::times(5).apart(Duration::from_millis(100)),
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
#[non_exhaustive]
pub struct WriteLimits {
pub key_depth: Option<usize>,
pub portable_across: Vec<Backend>,
}
impl WriteLimits {
pub fn key_depth(mut self, levels: usize) -> Self {
self.key_depth = Some(levels);
self
}
pub fn portable_across(mut self, engines: impl IntoIterator<Item = Backend>) -> Self {
self.portable_across = engines.into_iter().collect();
self
}
pub fn ceiling(&self, running: Backend) -> usize {
self.portable_across
.iter()
.map(|engine| engine.depth_ceiling())
.chain(std::iter::once(running.depth_ceiling()))
.min()
.unwrap_or_else(|| running.depth_ceiling())
}
pub fn non_finite_floats(&self, running: Backend) -> bool {
self.everywhere(running, Holds::non_finite_floats)
}
pub fn enums(&self, running: Backend) -> bool {
self.everywhere(running, Holds::enums)
}
pub fn an_integer_past_i64(&self, running: Backend) -> bool {
self.everywhere(running, Holds::an_integer_past_i64)
}
pub fn a_128_bit_integer(&self, running: Backend) -> bool {
self.everywhere(running, Holds::a_128_bit_integer)
}
pub fn an_integer_past_64_bits(&self, running: Backend) -> bool {
self.everywhere(running, Holds::an_integer_past_64_bits)
}
pub fn a_nested_option(&self, running: Backend) -> bool {
self.everywhere(running, Holds::a_nested_option)
}
fn everywhere(&self, running: Backend, ask: fn(Holds) -> bool) -> bool {
ask(running.holds())
&& self
.portable_across
.iter()
.all(|engine| ask(engine.holds()))
}
}
#[non_exhaustive]
pub struct GaveUp<'a> {
pub why: &'a Report<StorageError>,
pub unsaved: &'a [amethystate_core::path::StorePath],
}
#[non_exhaustive]
pub enum PersistEvent<'a> {
GaveUp { failure: &'a GaveUp<'a> },
Recovered,
}
pub type PersistObserver = Arc<dyn Fn(&PersistEvent<'_>) + Send + Sync>;
#[non_exhaustive]
pub struct StoreConfig {
pub path: PathBuf,
pub save_debounce: Duration,
pub watch_debounce: Duration,
pub retry_policy: RetryPolicy,
pub limits: WriteLimits,
pub will_not_open: WillNotOpen,
#[cfg(any(feature = "json", feature = "toml", feature = "ron"))]
pub(crate) file_write: FileWritePolicy,
#[cfg(any(feature = "json", feature = "toml", feature = "ron"))]
pub(crate) will_not_read: WhenItWillNotRead,
#[cfg(any(feature = "redb", feature = "memory"))]
pub(crate) parallel_reads: bool,
#[cfg(feature = "redb")]
pub(crate) redb_cache_size: usize,
}
impl StoreConfig {
pub fn new(path: impl Into<PathBuf>) -> Self {
Self {
path: path.into(),
save_debounce: Duration::from_millis(300),
watch_debounce: Duration::from_millis(500),
retry_policy: RetryPolicy {
interval: Duration::from_secs(5),
budget: Duration::from_secs(60),
},
limits: WriteLimits::default(),
will_not_open: WillNotOpen::default(),
#[cfg(any(feature = "json", feature = "toml", feature = "ron"))]
file_write: FileWritePolicy::default(),
#[cfg(any(feature = "json", feature = "toml", feature = "ron"))]
will_not_read: WhenItWillNotRead::default(),
#[cfg(any(feature = "redb", feature = "memory"))]
parallel_reads: false,
#[cfg(feature = "redb")]
redb_cache_size: 0,
}
}
}