1use std::fmt::Debug;
19use std::sync::Arc;
20
21use log::debug;
22
23use super::MOKA_SCHEME;
24use super::config::MokaConfig;
25use super::core::*;
26use super::deleter::MokaDeleter;
27use super::lister::MokaLister;
28use super::reader::*;
29use super::writer::MokaWriter;
30use opendal_core::raw::*;
31use opendal_core::*;
32
33pub type MokaCache<K, V> = moka::future::Cache<K, V>;
35pub type MokaCacheBuilder<K, V> = moka::future::CacheBuilder<K, V, MokaCache<K, V>>;
37
38#[doc = include_str!("docs.md")]
40#[derive(Default)]
41pub struct MokaBuilder {
42 pub(super) config: MokaConfig,
43 pub(super) builder: MokaCacheBuilder<String, MokaValue>,
44}
45
46impl Debug for MokaBuilder {
47 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 f.debug_struct("MokaBuilder")
49 .field("config", &self.config)
50 .finish_non_exhaustive()
51 }
52}
53
54impl MokaBuilder {
55 pub fn new(builder: MokaCacheBuilder<String, MokaValue>) -> Self {
85 Self {
86 builder,
87 ..Default::default()
88 }
89 }
90
91 pub fn name(mut self, v: &str) -> Self {
95 if !v.is_empty() {
96 self.config.name = Some(v.to_owned());
97 }
98 self
99 }
100
101 pub fn max_capacity(mut self, v: u64) -> Self {
105 if v != 0 {
106 self.config.max_capacity = Some(v);
107 }
108 self
109 }
110
111 pub fn time_to_live(mut self, v: Duration) -> Self {
115 if !v.is_zero() {
116 self.config.time_to_live = Some(format!("{}s", v.as_secs()));
117 }
118 self
119 }
120
121 pub fn time_to_idle(mut self, v: Duration) -> Self {
125 if !v.is_zero() {
126 self.config.time_to_idle = Some(format!("{}s", v.as_secs()));
127 }
128 self
129 }
130
131 pub fn root(mut self, path: &str) -> Self {
133 self.config.root = if path.is_empty() {
134 None
135 } else {
136 Some(path.to_string())
137 };
138 self
139 }
140}
141
142impl Builder for MokaBuilder {
143 type Config = MokaConfig;
144
145 fn build(self) -> Result<impl Service> {
146 debug!("backend build started: {:?}", self);
147
148 let root = normalize_root(
149 self.config
150 .root
151 .clone()
152 .unwrap_or_else(|| "/".to_string())
153 .as_str(),
154 );
155
156 let mut builder = self.builder;
157
158 if let Some(v) = &self.config.name {
159 builder = builder.name(v);
160 }
161 if let Some(v) = self.config.max_capacity {
162 builder = builder.max_capacity(v);
163 }
164 if let Some(value) = self.config.time_to_live.as_deref() {
165 let duration = signed_to_duration(value)?;
166 builder = builder.time_to_live(duration);
167 }
168 if let Some(value) = self.config.time_to_idle.as_deref() {
169 let duration = signed_to_duration(value)?;
170 builder = builder.time_to_idle(duration);
171 }
172
173 debug!("backend build finished: {:?}", self.config);
174
175 let core = MokaCore {
176 cache: builder.build(),
177 };
178
179 Ok(MokaBackend::new(core).with_normalized_root(root))
180 }
181}
182
183#[derive(Debug, Clone)]
184pub struct MokaBackend {
185 pub(crate) core: Arc<MokaCore>,
186 pub(crate) root: String,
187 pub(crate) info: ServiceInfo,
188 pub(crate) capability: Capability,
189}
190
191impl MokaBackend {
192 fn new(core: MokaCore) -> Self {
193 let info = ServiceInfo::new(MOKA_SCHEME, "/", core.cache.name().unwrap_or("moka"));
194 let capability = Capability {
195 read: true,
196 write: true,
197 write_can_empty: true,
198 write_with_cache_control: true,
199 write_with_content_type: true,
200 write_with_content_disposition: true,
201 write_with_content_encoding: true,
202 delete: true,
203 stat: true,
204 list: true,
205 ..Default::default()
206 };
207
208 Self {
209 core: Arc::new(core),
210 root: "/".to_string(),
211 info,
212 capability,
213 }
214 }
215
216 fn with_normalized_root(mut self, root: String) -> Self {
217 self.info = self.info.with_root(&root);
218 self.root = root;
219 self
220 }
221}
222
223impl Service for MokaBackend {
224 type Reader = oio::StreamReader<MokaReader>;
225 type Writer = MokaWriter;
226 type Lister = oio::HierarchyLister<MokaLister>;
227 type Deleter = oio::OneShotDeleter<MokaDeleter>;
228 type Copier = ();
229
230 fn info(&self) -> ServiceInfo {
231 self.info.clone()
232 }
233
234 fn capability(&self) -> Capability {
235 self.capability
236 }
237
238 async fn create_dir(
239 &self,
240 _ctx: &OperationContext,
241 _path: &str,
242 _args: OpCreateDir,
243 ) -> Result<RpCreateDir> {
244 Err(Error::new(
245 ErrorKind::Unsupported,
246 "operation is not supported",
247 ))
248 }
249
250 async fn stat(&self, _ctx: &OperationContext, path: &str, _: OpStat) -> Result<RpStat> {
251 let p = build_abs_path(&self.root, path);
252
253 if p == build_abs_path(&self.root, "") {
254 Ok(RpStat::new(Metadata::new(EntryMode::DIR)))
255 } else {
256 match self.core.get(&p).await? {
258 Some(value) => {
259 let mut metadata = value.metadata.clone();
261 if p.ends_with('/') && metadata.mode() != EntryMode::DIR {
264 metadata.set_mode(EntryMode::DIR);
265 }
266 Ok(RpStat::new(metadata))
267 }
268 None => {
269 if p.ends_with('/') {
271 let has_children = self
272 .core
273 .cache
274 .iter()
275 .any(|kv| kv.0.starts_with(&p) && kv.0.len() > p.len());
276
277 if has_children {
278 Ok(RpStat::new(Metadata::new(EntryMode::DIR)))
279 } else {
280 Err(Error::new(ErrorKind::NotFound, "key not found in moka"))
281 }
282 } else {
283 Err(Error::new(ErrorKind::NotFound, "key not found in moka"))
284 }
285 }
286 }
287 }
288 }
289 fn read(&self, _ctx: &OperationContext, path: &str, args: OpRead) -> Result<Self::Reader> {
290 let output: oio::StreamReader<MokaReader> = {
291 Ok(oio::StreamReader::new(MokaReader::new(
292 self.clone(),
293 path,
294 args,
295 )))
296 }?;
297
298 Ok(output)
299 }
300
301 fn write(&self, _ctx: &OperationContext, path: &str, args: OpWrite) -> Result<Self::Writer> {
302 let output: MokaWriter = {
303 let p = build_abs_path(&self.root, path);
304 Ok(MokaWriter::new(self.core.clone(), p, args))
305 }?;
306
307 Ok(output)
308 }
309
310 fn delete(&self, _ctx: &OperationContext) -> Result<Self::Deleter> {
311 let output: oio::OneShotDeleter<MokaDeleter> = {
312 Ok(oio::OneShotDeleter::new(MokaDeleter::new(
313 self.core.clone(),
314 self.root.clone(),
315 )))
316 }?;
317
318 Ok(output)
319 }
320
321 fn list(&self, _ctx: &OperationContext, path: &str, args: OpList) -> Result<Self::Lister> {
322 let output: oio::HierarchyLister<MokaLister> = {
323 let lister = MokaLister::new(self.core.clone(), self.root.clone(), path.to_string());
326 let lister = oio::HierarchyLister::new(lister, path, args.recursive());
327 Ok(lister)
328 }?;
329
330 Ok(output)
331 }
332
333 fn copy(
334 &self,
335 _ctx: &OperationContext,
336 _from: &str,
337 _to: &str,
338 _args: OpCopy,
339 _opts: OpCopier,
340 ) -> Result<Self::Copier> {
341 Err(Error::new(
342 ErrorKind::Unsupported,
343 "operation is not supported",
344 ))
345 }
346
347 async fn rename(
348 &self,
349 _ctx: &OperationContext,
350 _from: &str,
351 _to: &str,
352 _args: OpRename,
353 ) -> Result<RpRename> {
354 Err(Error::new(
355 ErrorKind::Unsupported,
356 "operation is not supported",
357 ))
358 }
359
360 async fn presign(
361 &self,
362 _ctx: &OperationContext,
363 _path: &str,
364 _args: OpPresign,
365 ) -> Result<RpPresign> {
366 Err(Error::new(
367 ErrorKind::Unsupported,
368 "operation is not supported",
369 ))
370 }
371}