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 accessor's full capability.
29///
30/// This layer updates [`Capability`] exposed by
31/// [`OperatorInfo::full_capability`][crate::OperatorInfo::full_capability]
32/// without changing the accessor'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///     .finish();
52/// # Ok(())
53/// # }
54/// ```
55#[derive(Clone)]
56pub struct CapabilityOverrideLayer {
57    apply: Arc<dyn Fn(Capability) -> Capability + Send + Sync>,
58}
59
60impl fmt::Debug for CapabilityOverrideLayer {
61    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62        f.debug_struct("CapabilityOverrideLayer")
63            .finish_non_exhaustive()
64    }
65}
66
67impl CapabilityOverrideLayer {
68    /// Create a new [`CapabilityOverrideLayer`].
69    pub fn new(apply: impl Fn(Capability) -> Capability + Send + Sync + 'static) -> Self {
70        Self {
71            apply: Arc::new(apply),
72        }
73    }
74
75    /// Create a new [`CapabilityOverrideLayer`] from capability override entries.
76    ///
77    /// The input is a comma-separated list of capability assignments:
78    ///
79    /// - `read=true` sets a boolean capability to `true`
80    /// - `read=false` sets a boolean capability to `false`
81    /// - `delete_max_size=1000` sets a numeric capability
82    /// - `delete_max_size=none` unsets an optional capability
83    pub fn from_overrides(input: &str) -> Result<Self> {
84        let overrides = CapabilityOverrides::parse(input)?;
85        Ok(Self::new(move |cap| overrides.apply(cap)))
86    }
87}
88
89impl<A: Access> Layer<A> for CapabilityOverrideLayer {
90    type LayeredAccess = A;
91
92    fn layer(&self, inner: A) -> Self::LayeredAccess {
93        let info = inner.info();
94        let apply = self.apply.clone();
95        info.update_full_capability(|cap| apply(cap));
96        inner
97    }
98}
99
100#[derive(Clone, Debug, Default)]
101struct CapabilityOverrides {
102    values: Map<String, Value>,
103}
104
105impl CapabilityOverrides {
106    fn parse(input: &str) -> Result<Self> {
107        let mut overrides = Self::default();
108
109        for token in input.split(',').map(str::trim).filter(|v| !v.is_empty()) {
110            let (name, value) = parse_capability_override(token)?;
111            overrides.values.insert(name.to_string(), value);
112            overrides.try_apply(Capability::default()).map_err(|err| {
113                invalid_capability_override(token, &format!("failed to apply override: {err}"))
114            })?;
115        }
116
117        Ok(overrides)
118    }
119
120    fn apply(&self, cap: Capability) -> Capability {
121        self.try_apply(cap)
122            .expect("capability overrides must be validated before applying")
123    }
124
125    fn try_apply(&self, cap: Capability) -> Result<Capability> {
126        let mut value = serde_json::to_value(cap).map_err(|err| {
127            Error::new(
128                ErrorKind::Unexpected,
129                format!("failed to serialize capability: {err}"),
130            )
131        })?;
132        let object = value.as_object_mut().ok_or_else(|| {
133            Error::new(
134                ErrorKind::Unexpected,
135                "serialized capability must be a JSON object",
136            )
137        })?;
138        object.extend(self.values.clone());
139
140        serde_json::from_value(value).map_err(|err| {
141            Error::new(
142                ErrorKind::ConfigInvalid,
143                format!("failed to deserialize capability overrides: {err}"),
144            )
145        })
146    }
147}
148
149fn parse_capability_override(token: &str) -> Result<(&str, Value)> {
150    let Some((name, value)) = token.split_once('=') else {
151        return Err(invalid_capability_override(
152            token,
153            "expected `capability=value`",
154        ));
155    };
156
157    Ok((
158        name.trim(),
159        parse_capability_value(value.trim())
160            .map_err(|err| invalid_capability_override(token, &err.to_string()))?,
161    ))
162}
163
164fn invalid_capability_override(token: &str, reason: &str) -> Error {
165    Error::new(
166        ErrorKind::ConfigInvalid,
167        format!("invalid capability override entry `{token}`: {reason}"),
168    )
169}
170
171fn parse_capability_value(value: &str) -> Result<Value> {
172    match value {
173        "true" | "on" | "yes" => Ok(Value::Bool(true)),
174        "false" | "off" | "no" => Ok(Value::Bool(false)),
175        "none" | "null" | "unset" => Ok(Value::Null),
176        _ => value
177            .parse::<usize>()
178            .map(|v| Value::Number(Number::from(v)))
179            .map_err(|_| {
180                Error::new(
181                    ErrorKind::ConfigInvalid,
182                    "expected a boolean, non-negative integer, or `none`",
183                )
184            }),
185    }
186}
187
188#[cfg(test)]
189mod tests {
190    use super::*;
191    use crate::Operator;
192    use crate::services;
193
194    #[test]
195    fn capability_override_updates_full_capability_only() -> Result<()> {
196        let op = Operator::new(services::Memory::default())?
197            .layer(CapabilityOverrideLayer::new(|mut cap| {
198                cap.read = false;
199                cap.delete_max_size = Some(7);
200                cap
201            }))
202            .finish();
203
204        assert!(!op.info().full_capability().read);
205        assert_eq!(op.info().full_capability().delete_max_size, Some(7));
206
207        assert!(op.info().native_capability().read);
208        assert_ne!(
209            op.info().native_capability().delete_max_size,
210            op.info().full_capability().delete_max_size
211        );
212
213        Ok(())
214    }
215
216    #[test]
217    fn parse_capability_overrides() -> Result<()> {
218        let layer = CapabilityOverrideLayer::from_overrides(
219            "read=false,write_can_append=true,delete_max_size=7",
220        )?;
221        let op = Operator::new(services::Memory::default())?
222            .layer(layer)
223            .finish();
224
225        assert!(!op.info().full_capability().read);
226        assert!(op.info().full_capability().write_can_append);
227        assert_eq!(op.info().full_capability().delete_max_size, Some(7));
228
229        Ok(())
230    }
231
232    #[test]
233    fn parse_bool_assignments_and_unset_sizes() -> Result<()> {
234        let layer =
235            CapabilityOverrideLayer::from_overrides("read=false,write=true,delete_max_size=none")?;
236        let op = Operator::new(services::Memory::default())?
237            .layer(layer)
238            .finish();
239
240        assert!(!op.info().full_capability().read);
241        assert!(op.info().full_capability().write);
242        assert_eq!(op.info().full_capability().delete_max_size, None);
243
244        Ok(())
245    }
246
247    #[test]
248    fn reject_unknown_capability() {
249        let err = CapabilityOverrideLayer::from_overrides("not_a_capability=false").unwrap_err();
250        assert_eq!(err.kind(), ErrorKind::ConfigInvalid);
251    }
252
253    #[test]
254    fn reject_capability_shorthand() {
255        let err = CapabilityOverrideLayer::from_overrides("-read").unwrap_err();
256        assert_eq!(err.kind(), ErrorKind::ConfigInvalid);
257    }
258
259    #[test]
260    fn reject_invalid_capability_type() {
261        let err = CapabilityOverrideLayer::from_overrides("read=1").unwrap_err();
262        assert_eq!(err.kind(), ErrorKind::ConfigInvalid);
263    }
264}