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 crate::raw::oio;
30use crate::raw::oio::HierarchyLister;
31use crate::raw::*;
32use crate::services::MiniMokaConfig;
33use crate::*;
34
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 const SCHEME: Scheme = Scheme::MiniMoka;
107 type Config = MiniMokaConfig;
108
109 fn build(self) -> Result<impl Access> {
110 debug!("backend build started: {:?}", &self);
111
112 let mut builder: mini_moka::sync::CacheBuilder<String, MiniMokaValue, _> =
113 mini_moka::sync::Cache::builder();
114
115 builder = builder.weigher(|k, v| (k.len() + v.content.len()) as u32);
117
118 if let Some(v) = self.config.max_capacity {
119 builder = builder.max_capacity(v);
120 }
121 if let Some(v) = self.config.time_to_live {
122 builder = builder.time_to_live(v);
123 }
124 if let Some(v) = self.config.time_to_idle {
125 builder = builder.time_to_idle(v);
126 }
127
128 let cache = builder.build();
129
130 let root = normalize_root(self.config.root.as_deref().unwrap_or("/"));
131
132 let core = Arc::new(MiniMokaCore { cache });
133
134 debug!("backend build finished: {root}");
135 Ok(MiniMokaBackend::new(core, root))
136 }
137}
138
139#[derive(Debug)]
140struct MiniMokaBackend {
141 core: Arc<MiniMokaCore>,
142 root: String,
143}
144
145impl MiniMokaBackend {
146 fn new(core: Arc<MiniMokaCore>, root: String) -> Self {
147 Self { core, root }
148 }
149}
150
151impl Access for MiniMokaBackend {
152 type Reader = Buffer;
153 type Writer = MiniMokaWriter;
154 type Lister = HierarchyLister<MiniMokaLister>;
155 type Deleter = oio::OneShotDeleter<MiniMokaDeleter>;
156
157 fn info(&self) -> Arc<AccessorInfo> {
158 let info = AccessorInfo::default();
159 info.set_scheme(Scheme::MiniMoka)
160 .set_root(&self.root)
161 .set_native_capability(Capability {
162 stat: true,
163 read: true,
164 write: true,
165 write_can_empty: true,
166 delete: true,
167 list: true,
168
169 ..Default::default()
170 });
171
172 Arc::new(info)
173 }
174
175 async fn stat(&self, path: &str, _: OpStat) -> Result<RpStat> {
176 let p = build_abs_path(&self.root, path);
177
178 match self.core.get(&p) {
180 Some(value) => {
181 let mut metadata = value.metadata.clone();
182 if p.ends_with('/') {
183 metadata.set_mode(EntryMode::DIR);
184 } else {
185 metadata.set_mode(EntryMode::FILE);
186 }
187 Ok(RpStat::new(metadata))
188 }
189 None => {
190 if p.ends_with('/') {
191 let is_prefix = self
192 .core
193 .cache
194 .iter()
195 .any(|entry| entry.key().starts_with(&p) && entry.key() != &p);
196
197 if is_prefix {
198 let mut metadata = Metadata::default();
199 metadata.set_mode(EntryMode::DIR);
200 return Ok(RpStat::new(metadata));
201 }
202 }
203
204 Err(Error::new(ErrorKind::NotFound, "path not found"))
205 }
206 }
207 }
208
209 async fn read(&self, path: &str, op: OpRead) -> Result<(RpRead, Self::Reader)> {
210 let p = build_abs_path(&self.root, path);
211
212 match self.core.get(&p) {
213 Some(value) => {
214 let range = op.range();
215
216 if range.is_full() {
218 return Ok((RpRead::new(), value.content));
219 }
220
221 let offset = range.offset() as usize;
222 if offset >= value.content.len() {
223 return Err(Error::new(
224 ErrorKind::RangeNotSatisfied,
225 "range start offset exceeds content length",
226 ));
227 }
228
229 let size = range.size().map(|s| s as usize);
230 let end = size.map_or(value.content.len(), |s| {
231 (offset + s).min(value.content.len())
232 });
233 let sliced_content = value.content.slice(offset..end);
234
235 Ok((RpRead::new(), sliced_content))
236 }
237 None => Err(Error::new(ErrorKind::NotFound, "path not found")),
238 }
239 }
240
241 async fn write(&self, path: &str, op: OpWrite) -> Result<(RpWrite, Self::Writer)> {
242 let p = build_abs_path(&self.root, path);
243 let writer = MiniMokaWriter::new(self.core.clone(), p, op);
244 Ok((RpWrite::new(), writer))
245 }
246
247 async fn delete(&self) -> Result<(RpDelete, Self::Deleter)> {
248 let deleter =
249 oio::OneShotDeleter::new(MiniMokaDeleter::new(self.core.clone(), self.root.clone()));
250 Ok((RpDelete::default(), deleter))
251 }
252
253 async fn list(&self, path: &str, op: OpList) -> Result<(RpList, Self::Lister)> {
254 let p = build_abs_path(&self.root, path);
255
256 let mini_moka_lister = MiniMokaLister::new(self.core.clone(), self.root.clone(), p);
257 let lister = HierarchyLister::new(mini_moka_lister, path, op.recursive());
258
259 Ok((RpList::default(), lister))
260 }
261}