1use crate::disk::{ensure_dir, exists, FileObjectExists};
2use crate::error::{Error, Result};
3use crate::{DirCache, DirCacheInner};
4use std::fmt::Display;
5use std::num::NonZeroUsize;
6use std::path::Path;
7use std::time::Duration;
8
9#[derive(Debug, Copy, Clone, Default)]
12pub struct DirCacheOpts {
13 pub mem_pull_opt: MemPullOpt,
14 pub mem_push_opt: MemPushOpt,
15 pub generation_opt: GenerationOpt,
16 pub sync_opt: SyncOpt,
17}
18
19impl DirCacheOpts {
20 #[must_use]
21 pub const fn new(
22 mem_pull_opt: MemPullOpt,
23 mem_push_opt: MemPushOpt,
24 generation_opt: GenerationOpt,
25 sync_opt: SyncOpt,
26 ) -> Self {
27 Self {
28 mem_pull_opt,
29 mem_push_opt,
30 generation_opt,
31 sync_opt,
32 }
33 }
34
35 #[must_use]
36 pub const fn with_mem_pull_opt(mut self, mem_pull_opt: MemPullOpt) -> Self {
37 self.mem_pull_opt = mem_pull_opt;
38 self
39 }
40
41 #[must_use]
42 pub const fn with_mem_push_opt(mut self, mem_push_opt: MemPushOpt) -> Self {
43 self.mem_push_opt = mem_push_opt;
44 self
45 }
46
47 #[must_use]
48 pub const fn with_generation_opt(mut self, generation_opt: GenerationOpt) -> Self {
49 self.generation_opt = generation_opt;
50 self
51 }
52
53 #[must_use]
54 pub const fn with_sync_opt(mut self, sync_opt: SyncOpt) -> Self {
55 self.sync_opt = sync_opt;
56 self
57 }
58
59 pub fn open(self, path: &Path, cache_open_options: CacheOpenOptions) -> Result<DirCache> {
64 match cache_open_options.dir_open {
65 DirOpenOpt::OnlyIfExists => {
66 match exists(path)? {
67 FileObjectExists::AsDir => {}
68 FileObjectExists::No => {
69 return Err(Error::Open(format!(
70 "Opened with OnlyIfExists but path {path:?} does not exist"
71 )));
72 }
73 FileObjectExists::AsFile => {
74 return Err(Error::Open(format!(
75 "Wanted to open at {path:?}, but path is a file"
76 )));
77 }
78 };
79 }
80 DirOpenOpt::CreateIfMissing => {
81 ensure_dir(path)?;
82 }
83 }
84 let inner = DirCacheInner::read_from_disk(
85 path.to_path_buf(),
86 cache_open_options.eager_load_to_ram,
87 self.generation_opt,
88 )?;
89 Ok(DirCache { inner, opts: self })
90 }
91}
92
93#[derive(Debug, Copy, Clone, Default)]
94pub struct CacheOpenOptions {
95 pub(crate) dir_open: DirOpenOpt,
96 pub(crate) eager_load_to_ram: bool,
97}
98
99impl CacheOpenOptions {
100 #[must_use]
101 pub fn new(dir_open: DirOpenOpt, eager_load_to_ram: bool) -> Self {
102 Self {
103 dir_open,
104 eager_load_to_ram,
105 }
106 }
107}
108
109#[derive(Debug, Copy, Clone, Default)]
111pub enum DirOpenOpt {
112 OnlyIfExists,
114 #[default]
116 CreateIfMissing,
117}
118
119#[derive(Debug, Copy, Clone, Default)]
121pub enum MemPushOpt {
122 RetainAndWrite,
124 MemoryOnly,
126 #[default]
128 PassthroughWrite,
129}
130
131#[derive(Debug, Copy, Clone, Default)]
134pub enum MemPullOpt {
135 #[default]
137 KeepInMemoryOnRead,
138 DontKeepInMemoryOnRead,
140}
141
142#[derive(Debug, Copy, Clone, Default)]
144pub enum ExpirationOpt {
145 #[default]
147 NoExpiry,
148 ExpiresAfter(Duration),
150}
151
152impl ExpirationOpt {
153 #[inline]
154 pub(crate) fn as_dur(self) -> Duration {
155 match self {
156 ExpirationOpt::NoExpiry => Duration::MAX,
158 ExpirationOpt::ExpiresAfter(dur) => dur,
159 }
160 }
161}
162
163#[derive(Debug, Copy, Clone)]
166pub struct GenerationOpt {
167 pub max_generations: NonZeroUsize,
169 pub(crate) old_gen_encoding: Encoding,
171 pub(crate) expiration: ExpirationOpt,
173}
174
175impl Default for GenerationOpt {
176 #[inline]
177 fn default() -> Self {
178 Self::new(NonZeroUsize::MIN, Encoding::Plain, ExpirationOpt::NoExpiry)
179 }
180}
181
182impl GenerationOpt {
183 #[must_use]
184 pub const fn new(
185 max_generations: NonZeroUsize,
186 old_gen_encoding: Encoding,
187 expiration: ExpirationOpt,
188 ) -> Self {
189 Self {
190 max_generations,
191 old_gen_encoding,
192 expiration,
193 }
194 }
195}
196
197#[derive(Copy, Clone, Debug)]
199pub enum Encoding {
200 Plain,
202 #[cfg(feature = "lz4")]
204 Lz4,
205}
206
207impl Encoding {
208 pub(crate) fn serialize(self) -> impl Display {
209 match self {
210 Encoding::Plain => 0u8,
211 #[cfg(feature = "lz4")]
212 Encoding::Lz4 => 1u8,
213 }
214 }
215
216 pub(crate) fn deserialize(s: &str) -> Result<Self> {
217 match s {
218 "0" => Ok(Self::Plain),
219 #[cfg(feature = "lz4")]
220 "1" => Ok(Self::Lz4),
221 v => Err(Error::ParseMetadata(format!(
222 "Failed to parse encoding from {v}"
223 ))),
224 }
225 }
226
227 #[inline]
228 #[allow(clippy::unnecessary_wraps)]
229 pub(crate) fn encode(self, content: Vec<u8>) -> Result<Vec<u8>> {
230 match self {
231 Encoding::Plain => Ok(content),
232 #[cfg(feature = "lz4")]
233 Encoding::Lz4 => {
234 let mut buf = Vec::new();
235 let mut encoder = lz4::EncoderBuilder::new().build(&mut buf).map_err(|e| {
236 Error::EncodingError(format!("Failed to create lz4 encoder builder: {e}"))
237 })?;
238 std::io::Write::write(&mut encoder, &content).map_err(|e| {
239 Error::EncodingError(format!("Failed to lz4 encode content: {e}"))
240 })?;
241 Ok(buf)
242 }
243 }
244 }
245}
246
247#[derive(Debug, Copy, Clone, Default)]
250pub enum SyncOpt {
251 SyncOnDrop,
253 #[default]
255 ManualSync,
256}