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
123    fn info(&self) -> ServiceInfo {
124        self.inner.info()
125    }
126
127    fn capability(&self) -> Capability {
128        (self.apply)(self.inner.capability())
129    }
130
131    async fn create_dir(
132        &self,
133        ctx: &OperationContext,
134        path: &str,
135        args: OpCreateDir,
136    ) -> Result<RpCreateDir> {
137        self.inner.create_dir(ctx, path, args).await
138    }
139
140    async fn stat(&self, ctx: &OperationContext, path: &str, args: OpStat) -> Result<RpStat> {
141        self.inner.stat(ctx, path, args).await
142    }
143
144    fn read(&self, ctx: &OperationContext, path: &str, args: OpRead) -> Result<Self::Reader> {
145        self.inner.read(ctx, path, args)
146    }
147
148    fn write(&self, ctx: &OperationContext, path: &str, args: OpWrite) -> Result<Self::Writer> {
149        self.inner.write(ctx, path, args)
150    }
151
152    fn delete(&self, ctx: &OperationContext) -> Result<Self::Deleter> {
153        self.inner.delete(ctx)
154    }
155
156    fn list(&self, ctx: &OperationContext, path: &str, args: OpList) -> Result<Self::Lister> {
157        self.inner.list(ctx, path, args)
158    }
159
160    fn copy(
161        &self,
162        ctx: &OperationContext,
163        from: &str,
164        to: &str,
165        args: OpCopy,
166        opts: OpCopier,
167    ) -> Result<Self::Copier> {
168        self.inner.copy(ctx, from, to, args, opts)
169    }
170
171    async fn rename(
172        &self,
173        ctx: &OperationContext,
174        from: &str,
175        to: &str,
176        args: OpRename,
177    ) -> Result<RpRename> {
178        self.inner.rename(ctx, from, to, args).await
179    }
180
181    async fn presign(
182        &self,
183        ctx: &OperationContext,
184        path: &str,
185        args: OpPresign,
186    ) -> Result<RpPresign> {
187        self.inner.presign(ctx, path, args).await
188    }
189}
190
191#[derive(Clone, Debug, Default)]
192struct CapabilityOverrides {
193    values: Map<String, Value>,
194}
195
196impl CapabilityOverrides {
197    fn parse(input: &str) -> Result<Self> {
198        let mut overrides = Self::default();
199
200        for token in input.split(',').map(str::trim).filter(|v| !v.is_empty()) {
201            let (name, value) = parse_capability_override(token)?;
202            overrides.values.insert(name.to_string(), value);
203            overrides.try_apply(Capability::default()).map_err(|err| {
204                invalid_capability_override(token, &format!("failed to apply override: {err}"))
205            })?;
206        }
207
208        Ok(overrides)
209    }
210
211    fn apply(&self, cap: Capability) -> Capability {
212        self.try_apply(cap)
213            .expect("capability overrides must be validated before applying")
214    }
215
216    fn try_apply(&self, cap: Capability) -> Result<Capability> {
217        let mut value = serde_json::to_value(cap).map_err(|err| {
218            Error::new(
219                ErrorKind::Unexpected,
220                format!("failed to serialize capability: {err}"),
221            )
222        })?;
223        let object = value.as_object_mut().ok_or_else(|| {
224            Error::new(
225                ErrorKind::Unexpected,
226                "serialized capability must be a JSON object",
227            )
228        })?;
229        object.extend(self.values.clone());
230
231        serde_json::from_value(value).map_err(|err| {
232            Error::new(
233                ErrorKind::ConfigInvalid,
234                format!("failed to deserialize capability overrides: {err}"),
235            )
236        })
237    }
238}
239
240fn parse_capability_override(token: &str) -> Result<(&str, Value)> {
241    let Some((name, value)) = token.split_once('=') else {
242        return Err(invalid_capability_override(
243            token,
244            "expected `capability=value`",
245        ));
246    };
247
248    Ok((
249        name.trim(),
250        parse_capability_value(value.trim())
251            .map_err(|err| invalid_capability_override(token, &err.to_string()))?,
252    ))
253}
254
255fn invalid_capability_override(token: &str, reason: &str) -> Error {
256    Error::new(
257        ErrorKind::ConfigInvalid,
258        format!("invalid capability override entry `{token}`: {reason}"),
259    )
260}
261
262fn parse_capability_value(value: &str) -> Result<Value> {
263    match value {
264        "true" | "on" | "yes" => Ok(Value::Bool(true)),
265        "false" | "off" | "no" => Ok(Value::Bool(false)),
266        "none" | "null" | "unset" => Ok(Value::Null),
267        _ => value
268            .parse::<usize>()
269            .map(|v| Value::Number(Number::from(v)))
270            .map_err(|_| {
271                Error::new(
272                    ErrorKind::ConfigInvalid,
273                    "expected a boolean, non-negative integer, or `none`",
274                )
275            }),
276    }
277}
278
279#[cfg(test)]
280mod tests {
281    use super::*;
282    use crate::Operator;
283    use crate::services;
284
285    #[test]
286    fn capability_override_updates_capability() -> Result<()> {
287        let op = Operator::new(services::Memory::default())?.layer(CapabilityOverrideLayer::new(
288            |mut cap| {
289                cap.read = false;
290                cap.delete_max_size = Some(7);
291                cap
292            },
293        ));
294
295        assert!(!op.info().capability().read);
296        assert_eq!(op.info().capability().delete_max_size, Some(7));
297
298        Ok(())
299    }
300
301    #[test]
302    fn parse_capability_overrides() -> Result<()> {
303        let layer = CapabilityOverrideLayer::from_overrides(
304            "read=false,write_can_append=true,delete_max_size=7",
305        )?;
306        let op = Operator::new(services::Memory::default())?.layer(layer);
307
308        assert!(!op.info().capability().read);
309        assert!(op.info().capability().write_can_append);
310        assert_eq!(op.info().capability().delete_max_size, Some(7));
311
312        Ok(())
313    }
314
315    #[test]
316    fn parse_bool_assignments_and_unset_sizes() -> Result<()> {
317        let layer =
318            CapabilityOverrideLayer::from_overrides("read=false,write=true,delete_max_size=none")?;
319        let op = Operator::new(services::Memory::default())?.layer(layer);
320
321        assert!(!op.info().capability().read);
322        assert!(op.info().capability().write);
323        assert_eq!(op.info().capability().delete_max_size, None);
324
325        Ok(())
326    }
327
328    #[test]
329    fn reject_unknown_capability() {
330        let err = CapabilityOverrideLayer::from_overrides("not_a_capability=false").unwrap_err();
331        assert_eq!(err.kind(), ErrorKind::ConfigInvalid);
332    }
333
334    #[test]
335    fn reject_capability_shorthand() {
336        let err = CapabilityOverrideLayer::from_overrides("-read").unwrap_err();
337        assert_eq!(err.kind(), ErrorKind::ConfigInvalid);
338    }
339
340    #[test]
341    fn reject_invalid_capability_type() {
342        let err = CapabilityOverrideLayer::from_overrides("read=1").unwrap_err();
343        assert_eq!(err.kind(), ErrorKind::ConfigInvalid);
344    }
345}