Skip to main content

opendal_core/layers/
capability_override.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;
19use std::sync::Arc;
20
21use serde_json::Map;
22use serde_json::Number;
23use serde_json::Value;
24
25use crate::raw::*;
26use crate::*;
27
28/// Layer for overriding an operator's effective capability.
29///
30/// This layer updates [`Capability`] exposed by
31/// [`OperatorInfo::capability`][crate::OperatorInfo::capability]
32/// without changing the service's native capability. It is useful when the
33/// backend implementation supports a capability, but the specific endpoint or
34/// test setup needs to disable or tune it.
35///
36/// # Examples
37///
38/// ```no_run
39/// use opendal_core::layers::CapabilityOverrideLayer;
40/// use opendal_core::services;
41/// use opendal_core::Operator;
42/// use opendal_core::Result;
43///
44/// # fn main() -> Result<()> {
45/// let op = Operator::new(services::Memory::default())?
46///     .layer(CapabilityOverrideLayer::new(|mut cap| {
47///         cap.write_with_if_match = false;
48///         cap.delete_max_size = Some(700);
49///         cap
50///     }));
51/// # Ok(())
52/// # }
53/// ```
54#[derive(Clone)]
55pub struct CapabilityOverrideLayer {
56    apply: Arc<dyn Fn(Capability) -> Capability + Send + Sync>,
57}
58
59impl fmt::Debug for CapabilityOverrideLayer {
60    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61        f.debug_struct("CapabilityOverrideLayer")
62            .finish_non_exhaustive()
63    }
64}
65
66impl CapabilityOverrideLayer {
67    /// Create a new [`CapabilityOverrideLayer`].
68    pub fn new(apply: impl Fn(Capability) -> Capability + Send + Sync + 'static) -> Self {
69        Self {
70            apply: Arc::new(apply),
71        }
72    }
73
74    /// Create a new [`CapabilityOverrideLayer`] from capability override entries.
75    ///
76    /// The input is a comma-separated list of capability assignments:
77    ///
78    /// - `read=true` sets a boolean capability to `true`
79    /// - `read=false` sets a boolean capability to `false`
80    /// - `delete_max_size=1000` sets a numeric capability
81    /// - `delete_max_size=none` unsets an optional capability
82    pub fn from_overrides(input: &str) -> Result<Self> {
83        let overrides = CapabilityOverrides::parse(input)?;
84        Ok(Self::new(move |cap| overrides.apply(cap)))
85    }
86}
87
88impl Layer for CapabilityOverrideLayer {
89    fn apply_service(&self, inner: Servicer) -> Servicer {
90        Arc::new(self.layer(inner))
91    }
92}
93
94impl CapabilityOverrideLayer {
95    fn layer(&self, inner: Servicer) -> CapabilityOverrideService {
96        CapabilityOverrideService {
97            inner,
98            apply: self.apply.clone(),
99        }
100    }
101}
102
103pub struct CapabilityOverrideService {
104    inner: Servicer,
105    apply: Arc<dyn Fn(Capability) -> Capability + Send + Sync>,
106}
107
108impl fmt::Debug for CapabilityOverrideService {
109    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110        f.debug_struct("CapabilityOverrideService")
111            .field("inner", &self.inner)
112            .finish_non_exhaustive()
113    }
114}
115
116impl Service for CapabilityOverrideService {
117    type Reader = oio::Reader;
118    type Writer = oio::Writer;
119    type Lister = oio::Lister;
120    type Deleter = oio::Deleter;
121    type Copier = oio::Copier;
122    type Composer = oio::Composer;
123
124    fn info(&self) -> ServiceInfo {
125        self.inner.info()
126    }
127
128    fn capability(&self) -> Capability {
129        (self.apply)(self.inner.capability())
130    }
131
132    async fn create_dir(
133        &self,
134        ctx: &OperationContext,
135        path: &str,
136        args: OpCreateDir,
137    ) -> Result<RpCreateDir> {
138        self.inner.create_dir(ctx, path, args).await
139    }
140
141    async fn stat(&self, ctx: &OperationContext, path: &str, args: OpStat) -> Result<RpStat> {
142        self.inner.stat(ctx, path, args).await
143    }
144
145    fn read(&self, ctx: &OperationContext, path: &str, args: OpRead) -> Result<Self::Reader> {
146        self.inner.read(ctx, path, args)
147    }
148
149    fn write(&self, ctx: &OperationContext, path: &str, args: OpWrite) -> Result<Self::Writer> {
150        self.inner.write(ctx, path, args)
151    }
152
153    fn delete(&self, ctx: &OperationContext) -> Result<Self::Deleter> {
154        self.inner.delete(ctx)
155    }
156
157    fn list(&self, ctx: &OperationContext, path: &str, args: OpList) -> Result<Self::Lister> {
158        self.inner.list(ctx, path, args)
159    }
160
161    fn copy(
162        &self,
163        ctx: &OperationContext,
164        from: &str,
165        to: &str,
166        args: OpCopy,
167    ) -> Result<Self::Copier> {
168        self.inner.copy(ctx, from, to, args)
169    }
170
171    fn compose(&self, ctx: &OperationContext, to: &str, args: OpCompose) -> Result<Self::Composer> {
172        self.inner.compose(ctx, to, args)
173    }
174
175    async fn rename(
176        &self,
177        ctx: &OperationContext,
178        from: &str,
179        to: &str,
180        args: OpRename,
181    ) -> Result<RpRename> {
182        self.inner.rename(ctx, from, to, args).await
183    }
184
185    async fn restore(
186        &self,
187        ctx: &OperationContext,
188        path: &str,
189        args: OpRestore,
190    ) -> Result<RpRestore> {
191        self.inner.restore(ctx, path, args).await
192    }
193
194    async fn presign(
195        &self,
196        ctx: &OperationContext,
197        path: &str,
198        args: OpPresign,
199    ) -> Result<RpPresign> {
200        self.inner.presign(ctx, path, args).await
201    }
202}
203
204#[derive(Clone, Debug, Default)]
205struct CapabilityOverrides {
206    values: Map<String, Value>,
207}
208
209impl CapabilityOverrides {
210    fn parse(input: &str) -> Result<Self> {
211        let mut overrides = Self::default();
212
213        for token in input.split(',').map(str::trim).filter(|v| !v.is_empty()) {
214            let (name, value) = parse_capability_override(token)?;
215            overrides.values.insert(name.to_string(), value);
216            overrides.try_apply(Capability::default()).map_err(|err| {
217                invalid_capability_override(token, &format!("failed to apply override: {err}"))
218            })?;
219        }
220
221        Ok(overrides)
222    }
223
224    fn apply(&self, cap: Capability) -> Capability {
225        self.try_apply(cap)
226            .expect("capability overrides must be validated before applying")
227    }
228
229    fn try_apply(&self, cap: Capability) -> Result<Capability> {
230        let mut value = serde_json::to_value(cap).map_err(|err| {
231            Error::new(
232                ErrorKind::Unexpected,
233                format!("failed to serialize capability: {err}"),
234            )
235        })?;
236        let object = value.as_object_mut().ok_or_else(|| {
237            Error::new(
238                ErrorKind::Unexpected,
239                "serialized capability must be a JSON object",
240            )
241        })?;
242        object.extend(self.values.clone());
243
244        serde_json::from_value(value).map_err(|err| {
245            Error::new(
246                ErrorKind::ConfigInvalid,
247                format!("failed to deserialize capability overrides: {err}"),
248            )
249        })
250    }
251}
252
253fn parse_capability_override(token: &str) -> Result<(&str, Value)> {
254    let Some((name, value)) = token.split_once('=') else {
255        return Err(invalid_capability_override(
256            token,
257            "expected `capability=value`",
258        ));
259    };
260
261    Ok((
262        name.trim(),
263        parse_capability_value(value.trim())
264            .map_err(|err| invalid_capability_override(token, &err.to_string()))?,
265    ))
266}
267
268fn invalid_capability_override(token: &str, reason: &str) -> Error {
269    Error::new(
270        ErrorKind::ConfigInvalid,
271        format!("invalid capability override entry `{token}`: {reason}"),
272    )
273}
274
275fn parse_capability_value(value: &str) -> Result<Value> {
276    match value {
277        "true" | "on" | "yes" => Ok(Value::Bool(true)),
278        "false" | "off" | "no" => Ok(Value::Bool(false)),
279        "none" | "null" | "unset" => Ok(Value::Null),
280        _ => value
281            .parse::<usize>()
282            .map(|v| Value::Number(Number::from(v)))
283            .map_err(|_| {
284                Error::new(
285                    ErrorKind::ConfigInvalid,
286                    "expected a boolean, non-negative integer, or `none`",
287                )
288            }),
289    }
290}
291
292#[cfg(test)]
293mod tests {
294    use super::*;
295    use crate::Operator;
296    use crate::services;
297
298    #[test]
299    fn capability_override_updates_capability() -> Result<()> {
300        let op = Operator::new(services::Memory::default())?.layer(CapabilityOverrideLayer::new(
301            |mut cap| {
302                cap.read = false;
303                cap.delete_max_size = Some(7);
304                cap
305            },
306        ));
307
308        assert!(!op.info().capability().read);
309        assert_eq!(op.info().capability().delete_max_size, Some(7));
310
311        Ok(())
312    }
313
314    #[test]
315    fn parse_capability_overrides() -> Result<()> {
316        let layer = CapabilityOverrideLayer::from_overrides(
317            "read=false,write_can_append=true,delete_max_size=7",
318        )?;
319        let op = Operator::new(services::Memory::default())?.layer(layer);
320
321        assert!(!op.info().capability().read);
322        assert!(op.info().capability().write_can_append);
323        assert_eq!(op.info().capability().delete_max_size, Some(7));
324
325        Ok(())
326    }
327
328    #[test]
329    fn parse_bool_assignments_and_unset_sizes() -> Result<()> {
330        let layer =
331            CapabilityOverrideLayer::from_overrides("read=false,write=true,delete_max_size=none")?;
332        let op = Operator::new(services::Memory::default())?.layer(layer);
333
334        assert!(!op.info().capability().read);
335        assert!(op.info().capability().write);
336        assert_eq!(op.info().capability().delete_max_size, None);
337
338        Ok(())
339    }
340
341    #[test]
342    fn reject_unknown_capability() {
343        let err = CapabilityOverrideLayer::from_overrides("not_a_capability=false").unwrap_err();
344        assert_eq!(err.kind(), ErrorKind::ConfigInvalid);
345    }
346
347    #[test]
348    fn reject_capability_shorthand() {
349        let err = CapabilityOverrideLayer::from_overrides("-read").unwrap_err();
350        assert_eq!(err.kind(), ErrorKind::ConfigInvalid);
351    }
352
353    #[test]
354    fn reject_invalid_capability_type() {
355        let err = CapabilityOverrideLayer::from_overrides("read=1").unwrap_err();
356        assert_eq!(err.kind(), ErrorKind::ConfigInvalid);
357    }
358}