use crate::disk::{ensure_dir, exists, FileObjectExists};
use crate::error::{Error, Result};
use crate::{DirCache, DirCacheInner};
use std::fmt::Display;
use std::num::NonZeroUsize;
use std::path::Path;
use std::time::Duration;
#[derive(Debug, Copy, Clone, Default)]
pub struct DirCacheOpts {
pub mem_pull_opt: MemPullOpt,
pub mem_push_opt: MemPushOpt,
pub generation_opt: GenerationOpt,
pub sync_opt: SyncOpt,
}
impl DirCacheOpts {
#[must_use]
pub const fn new(
mem_pull_opt: MemPullOpt,
mem_push_opt: MemPushOpt,
generation_opt: GenerationOpt,
sync_opt: SyncOpt,
) -> Self {
Self {
mem_pull_opt,
mem_push_opt,
generation_opt,
sync_opt,
}
}
#[must_use]
pub const fn with_mem_pull_opt(mut self, mem_pull_opt: MemPullOpt) -> Self {
self.mem_pull_opt = mem_pull_opt;
self
}
#[must_use]
pub const fn with_mem_push_opt(mut self, mem_push_opt: MemPushOpt) -> Self {
self.mem_push_opt = mem_push_opt;
self
}
#[must_use]
pub const fn with_generation_opt(mut self, generation_opt: GenerationOpt) -> Self {
self.generation_opt = generation_opt;
self
}
#[must_use]
pub const fn with_sync_opt(mut self, sync_opt: SyncOpt) -> Self {
self.sync_opt = sync_opt;
self
}
pub fn open(self, path: &Path, cache_open_options: CacheOpenOptions) -> Result<DirCache> {
match cache_open_options.dir_open {
DirOpenOpt::OnlyIfExists => {
match exists(path)? {
FileObjectExists::AsDir => {}
FileObjectExists::No => {
return Err(Error::Open(format!(
"Opened with OnlyIfExists but path {path:?} does not exist"
)));
}
FileObjectExists::AsFile => {
return Err(Error::Open(format!(
"Wanted to open at {path:?}, but path is a file"
)));
}
};
}
DirOpenOpt::CreateIfMissing => {
ensure_dir(path)?;
}
}
let inner = DirCacheInner::read_from_disk(
path.to_path_buf(),
cache_open_options.eager_load_to_ram,
self.generation_opt,
)?;
Ok(DirCache { inner, opts: self })
}
}
#[derive(Debug, Copy, Clone, Default)]
pub struct CacheOpenOptions {
pub(crate) dir_open: DirOpenOpt,
pub(crate) eager_load_to_ram: bool,
}
impl CacheOpenOptions {
#[must_use]
pub fn new(dir_open: DirOpenOpt, eager_load_to_ram: bool) -> Self {
Self {
dir_open,
eager_load_to_ram,
}
}
}
#[derive(Debug, Copy, Clone, Default)]
pub enum DirOpenOpt {
OnlyIfExists,
#[default]
CreateIfMissing,
}
#[derive(Debug, Copy, Clone, Default)]
pub enum MemPushOpt {
RetainAndWrite,
MemoryOnly,
#[default]
PassthroughWrite,
}
#[derive(Debug, Copy, Clone, Default)]
pub enum MemPullOpt {
#[default]
KeepInMemoryOnRead,
DontKeepInMemoryOnRead,
}
#[derive(Debug, Copy, Clone, Default)]
pub enum ExpirationOpt {
#[default]
NoExpiry,
ExpiresAfter(Duration),
}
impl ExpirationOpt {
#[inline]
pub(crate) fn as_dur(self) -> Duration {
match self {
ExpirationOpt::NoExpiry => Duration::MAX,
ExpirationOpt::ExpiresAfter(dur) => dur,
}
}
}
#[derive(Debug, Copy, Clone)]
pub struct GenerationOpt {
pub max_generations: NonZeroUsize,
pub(crate) old_gen_encoding: Encoding,
pub(crate) expiration: ExpirationOpt,
}
impl Default for GenerationOpt {
#[inline]
fn default() -> Self {
Self::new(NonZeroUsize::MIN, Encoding::Plain, ExpirationOpt::NoExpiry)
}
}
impl GenerationOpt {
#[must_use]
pub const fn new(
max_generations: NonZeroUsize,
old_gen_encoding: Encoding,
expiration: ExpirationOpt,
) -> Self {
Self {
max_generations,
old_gen_encoding,
expiration,
}
}
}
#[derive(Copy, Clone, Debug)]
pub enum Encoding {
Plain,
#[cfg(feature = "lz4")]
Lz4,
}
impl Encoding {
pub(crate) fn serialize(self) -> impl Display {
match self {
Encoding::Plain => 0u8,
#[cfg(feature = "lz4")]
Encoding::Lz4 => 1u8,
}
}
pub(crate) fn deserialize(s: &str) -> Result<Self> {
match s {
"0" => Ok(Self::Plain),
#[cfg(feature = "lz4")]
"1" => Ok(Self::Lz4),
v => Err(Error::ParseMetadata(format!(
"Failed to parse encoding from {v}"
))),
}
}
#[inline]
#[allow(clippy::unnecessary_wraps)]
pub(crate) fn encode(self, content: Vec<u8>) -> Result<Vec<u8>> {
match self {
Encoding::Plain => Ok(content),
#[cfg(feature = "lz4")]
Encoding::Lz4 => {
let mut buf = Vec::new();
let mut encoder = lz4::EncoderBuilder::new().build(&mut buf).map_err(|e| {
Error::EncodingError(format!("Failed to create lz4 encoder builder: {e}"))
})?;
std::io::Write::write(&mut encoder, &content).map_err(|e| {
Error::EncodingError(format!("Failed to lz4 encode content: {e}"))
})?;
Ok(buf)
}
}
}
}
#[derive(Debug, Copy, Clone, Default)]
pub enum SyncOpt {
SyncOnDrop,
#[default]
ManualSync,
}