object_store/
config.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.
17use std::fmt::{Debug, Display, Formatter};
18use std::str::FromStr;
19use std::time::Duration;
20
21use humantime::{format_duration, parse_duration};
22use reqwest::header::HeaderValue;
23
24use crate::{Error, Result};
25
26/// Provides deferred parsing of a value
27///
28/// This allows builders to defer fallibility to build
29#[derive(Debug, Clone)]
30pub enum ConfigValue<T> {
31    Parsed(T),
32    Deferred(String),
33}
34
35impl<T: Display> Display for ConfigValue<T> {
36    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
37        match self {
38            Self::Parsed(v) => write!(f, "{v}"),
39            Self::Deferred(v) => write!(f, "{v}"),
40        }
41    }
42}
43
44impl<T> From<T> for ConfigValue<T> {
45    fn from(value: T) -> Self {
46        Self::Parsed(value)
47    }
48}
49
50impl<T: Parse + Clone> ConfigValue<T> {
51    pub fn parse(&mut self, v: impl Into<String>) {
52        *self = Self::Deferred(v.into())
53    }
54
55    pub fn get(&self) -> Result<T> {
56        match self {
57            Self::Parsed(v) => Ok(v.clone()),
58            Self::Deferred(v) => T::parse(v),
59        }
60    }
61}
62
63impl<T: Default> Default for ConfigValue<T> {
64    fn default() -> Self {
65        Self::Parsed(T::default())
66    }
67}
68
69/// A value that can be stored in [`ConfigValue`]
70pub trait Parse: Sized {
71    fn parse(v: &str) -> Result<Self>;
72}
73
74impl Parse for bool {
75    fn parse(v: &str) -> Result<Self> {
76        let lower = v.to_ascii_lowercase();
77        match lower.as_str() {
78            "1" | "true" | "on" | "yes" | "y" => Ok(true),
79            "0" | "false" | "off" | "no" | "n" => Ok(false),
80            _ => Err(Error::Generic {
81                store: "Config",
82                source: format!("failed to parse \"{v}\" as boolean").into(),
83            }),
84        }
85    }
86}
87
88impl Parse for Duration {
89    fn parse(v: &str) -> Result<Self> {
90        parse_duration(v).map_err(|_| Error::Generic {
91            store: "Config",
92            source: format!("failed to parse \"{v}\" as Duration").into(),
93        })
94    }
95}
96
97impl Parse for usize {
98    fn parse(v: &str) -> Result<Self> {
99        Self::from_str(v).map_err(|_| Error::Generic {
100            store: "Config",
101            source: format!("failed to parse \"{v}\" as usize").into(),
102        })
103    }
104}
105
106impl Parse for HeaderValue {
107    fn parse(v: &str) -> Result<Self> {
108        Self::from_str(v).map_err(|_| Error::Generic {
109            store: "Config",
110            source: format!("failed to parse \"{v}\" as HeaderValue").into(),
111        })
112    }
113}
114
115pub(crate) fn fmt_duration(duration: &ConfigValue<Duration>) -> String {
116    match duration {
117        ConfigValue::Parsed(v) => format_duration(*v).to_string(),
118        ConfigValue::Deferred(v) => v.clone(),
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125    use std::time::Duration;
126
127    #[test]
128    fn test_parse_duration() {
129        let duration = Duration::from_secs(60);
130        assert_eq!(Duration::parse("60 seconds").unwrap(), duration);
131        assert_eq!(Duration::parse("60 s").unwrap(), duration);
132        assert_eq!(Duration::parse("60s").unwrap(), duration)
133    }
134}