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::PrefixLister;
24use crate::raw::*;
25use crate::*;
26
27/// Simulate missing capabilities for backends in a configurable way.
28#[derive(Debug, Clone)]
29pub struct SimulateLayer {
30    list_recursive: bool,
31    stat_dir: bool,
32    create_dir: bool,
33}
34
35impl Default for SimulateLayer {
36    fn default() -> Self {
37        Self {
38            list_recursive: true,
39            stat_dir: true,
40            create_dir: true,
41        }
42    }
43}
44
45impl SimulateLayer {
46    /// Enable or disable recursive list simulation. Default: true.
47    pub fn with_list_recursive(mut self, enabled: bool) -> Self {
48        self.list_recursive = enabled;
49        self
50    }
51
52    /// Enable or disable stat dir simulation. Default: true.
53    pub fn with_stat_dir(mut self, enabled: bool) -> Self {
54        self.stat_dir = enabled;
55        self
56    }
57
58    /// Enable or disable create_dir simulation. Default: true.
59    pub fn with_create_dir(mut self, enabled: bool) -> Self {
60        self.create_dir = enabled;
61        self
62    }
63}
64
65impl<A: Access> Layer<A> for SimulateLayer {
66    type LayeredAccess = SimulateAccessor<A>;
67
68    fn layer(&self, inner: A) -> Self::LayeredAccess {
69        let info = inner.info();
70        info.update_full_capability(|mut cap| {
71            if self.create_dir && cap.list && cap.write_can_empty {
72                cap.create_dir = true;
73            }
74            cap
75        });
76
77        SimulateAccessor {
78            config: self.clone(),
79            info,
80            inner: Arc::new(inner),
81        }
82    }
83}
84
85/// Accessor that applies capability simulation.
86pub struct SimulateAccessor<A: Access> {
87    config: SimulateLayer,
88    info: Arc<AccessorInfo>,
89    inner: Arc<A>,
90}
91
92impl<A: Access> Debug for SimulateAccessor<A> {
93    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
94        self.inner.fmt(f)
95    }
96}
97
98impl<A: Access> SimulateAccessor<A> {
99    async fn simulate_create_dir(&self, path: &str, args: OpCreateDir) -> Result<RpCreateDir> {
100        let capability = self.info.native_capability();
101
102        if capability.create_dir || !self.config.create_dir {
103            return self.inner().create_dir(path, args).await;
104        }
105
106        if capability.write_can_empty && capability.list {
107            let (_, mut w) = self.inner.write(path, OpWrite::default()).await?;
108            oio::Write::close(&mut w).await?;
109            return Ok(RpCreateDir::default());
110        }
111
112        self.inner.create_dir(path, args).await
113    }
114
115    async fn simulate_stat(&self, path: &str, args: OpStat) -> Result<RpStat> {
116        let capability = self.info.native_capability();
117
118        if path == "/" {
119            return Ok(RpStat::new(Metadata::new(EntryMode::DIR)));
120        }
121
122        if path.ends_with('/') {
123            if capability.create_dir {
124                let meta = self.inner.stat(path, args.clone()).await?.into_metadata();
125
126                if meta.is_file() {
127                    return Err(Error::new(
128                        ErrorKind::NotFound,
129                        "stat expected a directory, but found a file",
130                    ));
131                }
132
133                return Ok(RpStat::new(meta));
134            }
135
136            if self.config.stat_dir && capability.list_with_recursive {
137                let (_, mut l) = self
138                    .inner
139                    .list(path, OpList::default().with_recursive(true).with_limit(1))
140                    .await?;
141
142                return if oio::List::next(&mut l).await?.is_some() {
143                    Ok(RpStat::new(Metadata::new(EntryMode::DIR)))
144                } else {
145                    Err(Error::new(
146                        ErrorKind::NotFound,
147                        "the directory is not found",
148                    ))
149                };
150            }
151        }
152
153        self.inner.stat(path, args).await
154    }
155
156    async fn simulate_list(
157        &self,
158        path: &str,
159        args: OpList,
160    ) -> Result<(RpList, SimulateLister<A, A::Lister>)> {
161        let cap = self.info.native_capability();
162
163        let recursive = args.recursive();
164        let forward = args;
165
166        let (rp, lister) = match (
167            recursive,
168            cap.list_with_recursive,
169            self.config.list_recursive,
170        ) {
171            // Backend supports recursive list, forward directly.
172            (_, true, _) => {
173                let (rp, p) = self.inner.list(path, forward).await?;
174                (rp, SimulateLister::One(p))
175            }
176            // Simulate recursive via flat list when enabled.
177            (true, false, true) => {
178                if path.ends_with('/') {
179                    let p = FlatLister::new(self.inner.clone(), path);
180                    (RpList::default(), SimulateLister::Two(p))
181                } else {
182                    let parent = get_parent(path);
183                    let p = FlatLister::new(self.inner.clone(), parent);
184                    let p = PrefixLister::new(p, path);
185                    (RpList::default(), SimulateLister::Four(p))
186                }
187            }
188            // Recursive requested but simulation disabled; rely on backend and propagate errors.
189            (true, false, false) => {
190                let (rp, p) = self.inner.list(path, forward).await?;
191                (rp, SimulateLister::One(p))
192            }
193            // Non-recursive list: keep existing prefix handling semantics.
194            (false, false, _) => {
195                if path.ends_with('/') {
196                    let (rp, p) = self.inner.list(path, forward).await?;
197                    (rp, SimulateLister::One(p))
198                } else {
199                    let parent = get_parent(path);
200                    let (rp, p) = self.inner.list(parent, forward).await?;
201                    let p = PrefixLister::new(p, path);
202                    (rp, SimulateLister::Three(p))
203                }
204            }
205        };
206
207        Ok((rp, lister))
208    }
209}
210
211impl<A: Access> LayeredAccess for SimulateAccessor<A> {
212    type Inner = A;
213    type Reader = A::Reader;
214    type Writer = A::Writer;
215    type Lister = SimulateLister<A, A::Lister>;
216    type Deleter = A::Deleter;
217
218    fn inner(&self) -> &Self::Inner {
219        &self.inner
220    }
221
222    fn info(&self) -> Arc<AccessorInfo> {
223        self.info.clone()
224    }
225
226    async fn create_dir(&self, path: &str, args: OpCreateDir) -> Result<RpCreateDir> {
227        self.simulate_create_dir(path, args).await
228    }
229
230    async fn read(&self, path: &str, args: OpRead) -> Result<(RpRead, Self::Reader)> {
231        self.inner.read(path, args).await
232    }
233
234    async fn write(&self, path: &str, args: OpWrite) -> Result<(RpWrite, Self::Writer)> {
235        self.inner.write(path, args).await
236    }
237
238    async fn stat(&self, path: &str, args: OpStat) -> Result<RpStat> {
239        self.simulate_stat(path, args).await
240    }
241
242    async fn delete(&self) -> Result<(RpDelete, Self::Deleter)> {
243        self.inner().delete().await
244    }
245
246    async fn list(&self, path: &str, args: OpList) -> Result<(RpList, Self::Lister)> {
247        self.simulate_list(path, args).await
248    }
249
250    async fn presign(&self, path: &str, args: OpPresign) -> Result<RpPresign> {
251        self.inner.presign(path, args).await
252    }
253}
254
255pub type SimulateLister<A, P> =
256    FourWays<P, FlatLister<Arc<A>, P>, PrefixLister<P>, PrefixLister<FlatLister<Arc<A>, P>>>;