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 type Composer = ();
230
231 fn info(&self) -> ServiceInfo {
232 self.info.clone()
233 }
234
235 fn capability(&self) -> Capability {
236 self.capability
237 }
238
239 async fn create_dir(
240 &self,
241 _ctx: &OperationContext,
242 _path: &str,
243 _args: OpCreateDir,
244 ) -> Result<RpCreateDir> {
245 Err(Error::new(
246 ErrorKind::Unsupported,
247 "operation is not supported",
248 ))
249 }
250
251 async fn stat(&self, _ctx: &OperationContext, path: &str, _: OpStat) -> Result<RpStat> {
252 let p = build_abs_path(&self.root, path);
253
254 if p == build_abs_path(&self.root, "") {
255 Ok(RpStat::new(MetadataBuilder::dir().build()))
256 } else {
257 match self.core.get(&p).await? {
259 Some(value) => {
260 let mut metadata = value.metadata.clone().into_builder();
262 if p.ends_with('/') && value.metadata.mode() != EntryMode::DIR {
265 metadata.set_dir();
266 }
267 Ok(RpStat::new(metadata.build()))
268 }
269 None => {
270 if p.ends_with('/') {
272 let has_children = self
273 .core
274 .cache
275 .iter()
276 .any(|kv| kv.0.starts_with(&p) && kv.0.len() > p.len());
277
278 if has_children {
279 Ok(RpStat::new(MetadataBuilder::dir().build()))
280 } else {
281 Err(Error::new(ErrorKind::NotFound, "key not found in moka"))
282 }
283 } else {
284 Err(Error::new(ErrorKind::NotFound, "key not found in moka"))
285 }
286 }
287 }
288 }
289 }
290 fn read(&self, _ctx: &OperationContext, path: &str, args: OpRead) -> Result<Self::Reader> {
291 let output: oio::StreamReader<MokaReader> = {
292 Ok(oio::StreamReader::new(MokaReader::new(
293 self.clone(),
294 path,
295 args,
296 )))
297 }?;
298
299 Ok(output)
300 }
301
302 fn write(&self, _ctx: &OperationContext, path: &str, args: OpWrite) -> Result<Self::Writer> {
303 let output: MokaWriter = {
304 let p = build_abs_path(&self.root, path);
305 Ok(MokaWriter::new(self.core.clone(), p, args))
306 }?;
307
308 Ok(output)
309 }
310
311 fn delete(&self, _ctx: &OperationContext) -> Result<Self::Deleter> {
312 let output: oio::OneShotDeleter<MokaDeleter> = {
313 Ok(oio::OneShotDeleter::new(MokaDeleter::new(
314 self.core.clone(),
315 self.root.clone(),
316 )))
317 }?;
318
319 Ok(output)
320 }
321
322 fn list(&self, _ctx: &OperationContext, path: &str, args: OpList) -> Result<Self::Lister> {
323 let output: oio::HierarchyLister<MokaLister> = {
324 let lister = MokaLister::new(self.core.clone(), self.root.clone(), path.to_string());
327 let lister = oio::HierarchyLister::new(lister, path, args.recursive());
328 Ok(lister)
329 }?;
330
331 Ok(output)
332 }
333
334 fn copy(
335 &self,
336 _ctx: &OperationContext,
337 _from: &str,
338 _to: &str,
339 _args: OpCopy,
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}