opendal/layers/
simulate.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use std::fmt::Debug;
19use std::fmt::Formatter;
20use std::sync::Arc;
21
22use crate::raw::oio::FlatLister;
23use crate::raw::oio::List;
24use crate::raw::oio::PrefixLister;
25use crate::raw::*;
26use crate::*;
27
28/// Simulate missing capabilities for backends in a configurable way.
29#[derive(Debug, Clone)]
30pub struct SimulateLayer {
31    list_recursive: bool,
32    stat_dir: bool,
33    create_dir: bool,
34    delete_recursive: bool,
35}
36
37impl Default for SimulateLayer {
38    fn default() -> Self {
39        Self {
40            list_recursive: true,
41            stat_dir: true,
42            create_dir: true,
43            delete_recursive: true,
44        }
45    }
46}
47
48impl SimulateLayer {
49    /// Enable or disable recursive list simulation. Default: true.
50    pub fn with_list_recursive(mut self, enabled: bool) -> Self {
51        self.list_recursive = enabled;
52        self
53    }
54
55    /// Enable or disable stat dir simulation. Default: true.
56    pub fn with_stat_dir(mut self, enabled: bool) -> Self {
57        self.stat_dir = enabled;
58        self
59    }
60
61    /// Enable or disable create_dir simulation. Default: true.
62    pub fn with_create_dir(mut self, enabled: bool) -> Self {
63        self.create_dir = enabled;
64        self
65    }
66
67    /// Enable or disable recursive delete simulation. Default: true.
68    pub fn with_delete_recursive(mut self, enabled: bool) -> Self {
69        self.delete_recursive = enabled;
70        self
71    }
72}
73
74impl<A: Access> Layer<A> for SimulateLayer {
75    type LayeredAccess = SimulateAccessor<A>;
76
77    fn layer(&self, inner: A) -> Self::LayeredAccess {
78        let info = inner.info();
79        info.update_full_capability(|mut cap| {
80            if self.create_dir && cap.list && cap.write_can_empty {
81                cap.create_dir = true;
82            }
83            if self.delete_recursive && cap.list && cap.delete {
84                cap.delete_with_recursive = true;
85            }
86            cap
87        });
88
89        SimulateAccessor {
90            config: self.clone(),
91            info,
92            inner: Arc::new(inner),
93        }
94    }
95}
96
97/// Accessor that applies capability simulation.
98pub struct SimulateAccessor<A: Access> {
99    config: SimulateLayer,
100    info: Arc<AccessorInfo>,
101    inner: Arc<A>,
102}
103
104impl<A: Access> Debug for SimulateAccessor<A> {
105    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
106        self.inner.fmt(f)
107    }
108}
109
110impl<A: Access> SimulateAccessor<A> {
111    async fn simulate_create_dir(&self, path: &str, args: OpCreateDir) -> Result<RpCreateDir> {
112        let capability = self.info.native_capability();
113
114        if capability.create_dir || !self.config.create_dir {
115            return self.inner().create_dir(path, args).await;
116        }
117
118        if capability.write_can_empty && capability.list {
119            let (_, mut w) = self.inner.write(path, OpWrite::default()).await?;
120            oio::Write::close(&mut w).await?;
121            return Ok(RpCreateDir::default());
122        }
123
124        self.inner.create_dir(path, args).await
125    }
126
127    async fn simulate_stat(&self, path: &str, args: OpStat) -> Result<RpStat> {
128        let capability = self.info.native_capability();
129
130        if path == "/" {
131            return Ok(RpStat::new(Metadata::new(EntryMode::DIR)));
132        }
133
134        if path.ends_with('/') {
135            if capability.create_dir {
136                let meta = self.inner.stat(path, args.clone()).await?.into_metadata();
137
138                if meta.is_file() {
139                    return Err(Error::new(
140                        ErrorKind::NotFound,
141                        "stat expected a directory, but found a file",
142                    ));
143                }
144
145                return Ok(RpStat::new(meta));
146            }
147
148            if self.config.stat_dir && capability.list_with_recursive {
149                let (_, mut l) = self
150                    .inner
151                    .list(path, OpList::default().with_recursive(true).with_limit(1))
152                    .await?;
153
154                return if oio::List::next(&mut l).await?.is_some() {
155                    Ok(RpStat::new(Metadata::new(EntryMode::DIR)))
156                } else {
157                    Err(Error::new(
158                        ErrorKind::NotFound,
159                        "the directory is not found",
160                    ))
161                };
162            }
163        }
164
165        self.inner.stat(path, args).await
166    }
167
168    async fn simulate_list(
169        &self,
170        path: &str,
171        args: OpList,
172    ) -> Result<(RpList, SimulateLister<A, A::Lister>)> {
173        let cap = self.info.native_capability();
174
175        let recursive = args.recursive();
176        let forward = args;
177
178        let (rp, lister) = match (
179            recursive,
180            cap.list_with_recursive,
181            self.config.list_recursive,
182        ) {
183            // Backend supports recursive list, forward directly.
184            (_, true, _) => {
185                let (rp, p) = self.inner.list(path, forward).await?;
186                (rp, SimulateLister::One(p))
187            }
188            // Simulate recursive via flat list when enabled.
189            (true, false, true) => {
190                if path.ends_with('/') {
191                    let p = FlatLister::new(self.inner.clone(), path);
192                    (RpList::default(), SimulateLister::Two(p))
193                } else {
194                    let parent = get_parent(path);
195                    let p = FlatLister::new(self.inner.clone(), parent);
196                    let p = PrefixLister::new(p, path);
197                    (RpList::default(), SimulateLister::Four(p))
198                }
199            }
200            // Recursive requested but simulation disabled; rely on backend and propagate errors.
201            (true, false, false) => {
202                let (rp, p) = self.inner.list(path, forward).await?;
203                (rp, SimulateLister::One(p))
204            }
205            // Non-recursive list: keep existing prefix handling semantics.
206            (false, false, _) => {
207                if path.ends_with('/') {
208                    let (rp, p) = self.inner.list(path, forward).await?;
209                    (rp, SimulateLister::One(p))
210                } else {
211                    let parent = get_parent(path);
212                    let (rp, p) = self.inner.list(parent, forward).await?;
213                    let p = PrefixLister::new(p, path);
214                    (rp, SimulateLister::Three(p))
215                }
216            }
217        };
218
219        Ok((rp, lister))
220    }
221
222    pub(crate) async fn simulate_delete_with_recursive<D: oio::Delete>(
223        &self,
224        deleter: &mut D,
225        path: &str,
226        args: OpDelete,
227    ) -> Result<()> {
228        if !self.info.full_capability().delete_with_recursive {
229            return Err(Error::new(
230                ErrorKind::Unsupported,
231                "recursive delete is not supported",
232            ));
233        }
234
235        let non_recursive = args.clone().with_recursive(false);
236
237        let (_rp, mut lister) = self
238            .simulate_list(path, OpList::new().with_recursive(true))
239            .await?;
240
241        while let Some(entry) = lister.next().await? {
242            let entry = entry.into_entry();
243            let mut entry_args = non_recursive.clone();
244            if let Some(version) = entry.metadata().version() {
245                entry_args = entry_args.with_version(version);
246            }
247            deleter.delete(entry.path(), entry_args).await?;
248        }
249
250        Ok(())
251    }
252}
253
254impl<A: Access> LayeredAccess for SimulateAccessor<A> {
255    type Inner = A;
256    type Reader = A::Reader;
257    type Writer = A::Writer;
258    type Lister = SimulateLister<A, A::Lister>;
259    type Deleter = SimulateDeleter<A, A::Deleter>;
260
261    fn inner(&self) -> &Self::Inner {
262        &self.inner
263    }
264
265    fn info(&self) -> Arc<AccessorInfo> {
266        self.info.clone()
267    }
268
269    async fn create_dir(&self, path: &str, args: OpCreateDir) -> Result<RpCreateDir> {
270        self.simulate_create_dir(path, args).await
271    }
272
273    async fn read(&self, path: &str, args: OpRead) -> Result<(RpRead, Self::Reader)> {
274        self.inner.read(path, args).await
275    }
276
277    async fn write(&self, path: &str, args: OpWrite) -> Result<(RpWrite, Self::Writer)> {
278        self.inner.write(path, args).await
279    }
280
281    async fn stat(&self, path: &str, args: OpStat) -> Result<RpStat> {
282        self.simulate_stat(path, args).await
283    }
284
285    async fn delete(&self) -> Result<(RpDelete, Self::Deleter)> {
286        let (rp, deleter) = self.inner().delete().await?;
287        let accessor = SimulateAccessor {
288            config: self.config.clone(),
289            info: self.info.clone(),
290            inner: self.inner.clone(),
291        };
292
293        Ok((rp, SimulateDeleter::new(accessor, deleter)))
294    }
295
296    async fn list(&self, path: &str, args: OpList) -> Result<(RpList, Self::Lister)> {
297        self.simulate_list(path, args).await
298    }
299
300    async fn presign(&self, path: &str, args: OpPresign) -> Result<RpPresign> {
301        self.inner.presign(path, args).await
302    }
303}
304
305pub type SimulateLister<A, P> =
306    FourWays<P, FlatLister<Arc<A>, P>, PrefixLister<P>, PrefixLister<FlatLister<Arc<A>, P>>>;
307
308/// Deleter wrapper that simulates recursive deletion.
309pub struct SimulateDeleter<A: Access, D> {
310    accessor: SimulateAccessor<A>,
311    deleter: D,
312}
313
314impl<A: Access, D> SimulateDeleter<A, D> {
315    pub fn new(accessor: SimulateAccessor<A>, deleter: D) -> Self {
316        Self { accessor, deleter }
317    }
318}
319
320impl<A: Access, D: oio::Delete> oio::Delete for SimulateDeleter<A, D> {
321    async fn delete(&mut self, path: &str, args: OpDelete) -> Result<()> {
322        if args.recursive() {
323            let cap = self.accessor.info.native_capability();
324
325            if cap.delete_with_recursive {
326                return self.deleter.delete(path, args).await;
327            }
328
329            if self.accessor.config.delete_recursive {
330                return self
331                    .accessor
332                    .simulate_delete_with_recursive(&mut self.deleter, path, args)
333                    .await;
334            }
335        }
336
337        self.deleter.delete(path, args).await
338    }
339
340    fn close(&mut self) -> impl Future<Output = Result<()>> + MaybeSend {
341        self.deleter.close()
342    }
343}