opendal/services/mini_moka/
backend.rs1use std::fmt::Debug;
19use std::fmt::Formatter;
20use std::sync::Arc;
21use std::time::Duration;
22
23use log::debug;
24
25use super::core::*;
26use super::delete::MiniMokaDeleter;
27use super::lister::MiniMokaLister;
28use super::writer::MiniMokaWriter;
29use super::DEFAULT_SCHEME;
30use crate::raw::oio;
31use crate::raw::oio::HierarchyLister;
32use crate::raw::*;
33use crate::services::MiniMokaConfig;
34use crate::*;
35impl Configurator for MiniMokaConfig {
36 type Builder = MiniMokaBuilder;
37 fn into_builder(self) -> Self::Builder {
38 MiniMokaBuilder { config: self }
39 }
40}
41
42#[doc = include_str!("docs.md")]
44#[derive(Default)]
45pub struct MiniMokaBuilder {
46 config: MiniMokaConfig,
47}
48
49impl Debug for MiniMokaBuilder {
50 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
51 f.debug_struct("MiniMokaBuilder")
52 .field("config", &self.config)
53 .finish()
54 }
55}
56
57impl MiniMokaBuilder {
58 pub fn new() -> Self {
60 Self::default()
61 }
62
63 pub fn max_capacity(mut self, v: u64) -> Self {
67 if v != 0 {
68 self.config.max_capacity = Some(v);
69 }
70 self
71 }
72
73 pub fn time_to_live(mut self, v: Duration) -> Self {
77 if !v.is_zero() {
78 self.config.time_to_live = Some(v);
79 }
80 self
81 }
82
83 pub fn time_to_idle(mut self, v: Duration) -> Self {
87 if !v.is_zero() {
88 self.config.time_to_idle = Some(v);
89 }
90 self
91 }
92
93 pub fn root(mut self, path: &str) -> Self {
95 self.config.root = if path.is_empty() {
96 None
97 } else {
98 Some(path.to_string())
99 };
100
101 self
102 }
103}
104
105impl Builder for MiniMokaBuilder {
106 type Config = MiniMokaConfig;
107
108 fn build(self) -> Result<impl Access> {
109 debug!("backend build started: {:?}", &self);
110
111 let mut builder: mini_moka::sync::CacheBuilder<String, MiniMokaValue, _> =
112 mini_moka::sync::Cache::builder();
113
114 builder = builder.weigher(|k, v| (k.len() + v.content.len()) as u32);
116
117 if let Some(v) = self.config.max_capacity {
118 builder = builder.max_capacity(v);
119 }
120 if let Some(v) = self.config.time_to_live {
121 builder = builder.time_to_live(v);
122 }
123 if let Some(v) = self.config.time_to_idle {
124 builder = builder.time_to_idle(v);
125 }
126
127 let cache = builder.build();
128
129 let root = normalize_root(self.config.root.as_deref().unwrap_or("/"));
130
131 let core = Arc::new(MiniMokaCore { cache });
132
133 debug!("backend build finished: {root}");
134 Ok(MiniMokaBackend::new(core, root))
135 }
136}
137
138#[derive(Debug)]
139struct MiniMokaBackend {
140 core: Arc<MiniMokaCore>,
141 root: String,
142}
143
144impl MiniMokaBackend {
145 fn new(core: Arc<MiniMokaCore>, root: String) -> Self {
146 Self { core, root }
147 }
148}
149
150impl Access for MiniMokaBackend {
151 type Reader = Buffer;
152 type Writer = MiniMokaWriter;
153 type Lister = HierarchyLister<MiniMokaLister>;
154 type Deleter = oio::OneShotDeleter<MiniMokaDeleter>;
155
156 fn info(&self) -> Arc<AccessorInfo> {
157 let info = AccessorInfo::default();
158 info.set_scheme(DEFAULT_SCHEME)
159 .set_root(&self.root)
160 .set_native_capability(Capability {
161 stat: true,
162 read: true,
163 write: true,
164 write_can_empty: true,
165 delete: true,
166 list: true,
167
168 ..Default::default()
169 });
170
171 Arc::new(info)
172 }
173
174 async fn stat(&self, path: &str, _: OpStat) -> Result<RpStat> {
175 let p = build_abs_path(&self.root, path);
176
177 match self.core.get(&p) {
179 Some(value) => {
180 let mut metadata = value.metadata.clone();
181 if p.ends_with('/') {
182 metadata.set_mode(EntryMode::DIR);
183 } else {
184 metadata.set_mode(EntryMode::FILE);
185 }
186 Ok(RpStat::new(metadata))
187 }
188 None => {
189 if p.ends_with('/') {
190 let is_prefix = self
191 .core
192 .cache
193 .iter()
194 .any(|entry| entry.key().starts_with(&p) && entry.key() != &p);
195
196 if is_prefix {
197 let mut metadata = Metadata::default();
198 metadata.set_mode(EntryMode::DIR);
199 return Ok(RpStat::new(metadata));
200 }
201 }
202
203 Err(Error::new(ErrorKind::NotFound, "path not found"))
204 }
205 }
206 }
207
208 async fn read(&self, path: &str, op: OpRead) -> Result<(RpRead, Self::Reader)> {
209 let p = build_abs_path(&self.root, path);
210
211 match self.core.get(&p) {
212 Some(value) => {
213 let range = op.range();
214
215 if range.is_full() {
217 return Ok((RpRead::new(), value.content));
218 }
219
220 let offset = range.offset() as usize;
221 if offset >= value.content.len() {
222 return Err(Error::new(
223 ErrorKind::RangeNotSatisfied,
224 "range start offset exceeds content length",
225 ));
226 }
227
228 let size = range.size().map(|s| s as usize);
229 let end = size.map_or(value.content.len(), |s| {
230 (offset + s).min(value.content.len())
231 });
232 let sliced_content = value.content.slice(offset..end);
233
234 Ok((RpRead::new(), sliced_content))
235 }
236 None => Err(Error::new(ErrorKind::NotFound, "path not found")),
237 }
238 }
239
240 async fn write(&self, path: &str, op: OpWrite) -> Result<(RpWrite, Self::Writer)> {
241 let p = build_abs_path(&self.root, path);
242 let writer = MiniMokaWriter::new(self.core.clone(), p, op);
243 Ok((RpWrite::new(), writer))
244 }
245
246 async fn delete(&self) -> Result<(RpDelete, Self::Deleter)> {
247 let deleter =
248 oio::OneShotDeleter::new(MiniMokaDeleter::new(self.core.clone(), self.root.clone()));
249 Ok((RpDelete::default(), deleter))
250 }
251
252 async fn list(&self, path: &str, op: OpList) -> Result<(RpList, Self::Lister)> {
253 let p = build_abs_path(&self.root, path);
254
255 let mini_moka_lister = MiniMokaLister::new(self.core.clone(), self.root.clone(), p);
256 let lister = HierarchyLister::new(mini_moka_lister, path, op.recursive());
257
258 Ok((RpList::default(), lister))
259 }
260}