teller_providers/
config.rs

1use std::cmp::Ordering;
2use std::collections::BTreeMap;
3
4use serde_derive::{Deserialize, Serialize};
5
6use crate::providers::ProviderKind;
7
8fn is_default<T: Default + PartialEq>(t: &T) -> bool {
9    t == &T::default()
10}
11
12#[derive(Serialize, Deserialize, Debug, Clone, Default)]
13pub struct ProviderCfg {
14    #[serde(rename = "kind")]
15    pub kind: ProviderKind,
16    #[serde(rename = "options", skip_serializing_if = "Option::is_none")]
17    pub options: Option<serde_json::Value>,
18    #[serde(rename = "name", skip_serializing_if = "Option::is_none")]
19    pub name: Option<String>,
20    pub maps: Vec<PathMap>,
21}
22
23#[derive(Serialize, Deserialize, Debug, Clone, Default, Eq, PartialEq)]
24pub enum Sensitivity {
25    #[default]
26    None,
27    Low,
28    Medium,
29    High,
30    Critical,
31}
32
33#[derive(Serialize, Deserialize, Debug, Clone, Default, Eq, PartialEq)]
34pub struct ProviderInfo {
35    pub kind: ProviderKind,
36    pub name: String,
37}
38#[derive(Serialize, Deserialize, Debug, Clone, Default, Eq, PartialEq)]
39pub struct PathInfo {
40    pub id: String,
41    pub path: String,
42}
43
44#[derive(Serialize, Deserialize, Debug, Clone, Default, Eq, PartialEq)]
45pub struct MetaInfo {
46    pub sensitivity: Sensitivity,
47    pub redact_with: Option<String>,
48    pub source: Option<String>,
49    pub sink: Option<String>,
50}
51#[derive(Serialize, Deserialize, Debug, Clone, Default, Eq, PartialEq)]
52pub struct KV {
53    pub value: String,
54    pub key: String, // mapped-to key
55    pub from_key: String,
56    pub path: Option<PathInfo>, // always toplevel
57    pub provider: Option<ProviderInfo>,
58    pub meta: Option<MetaInfo>,
59}
60
61impl PartialOrd for KV {
62    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
63        Some(self.cmp(other))
64    }
65}
66
67impl Ord for KV {
68    fn cmp(&self, other: &Self) -> Ordering {
69        if let (Some(provider), Some(other_provider)) = (&self.provider, &other.provider) {
70            let provider_cmp = provider.kind.cmp(&other_provider.kind);
71            if provider_cmp != Ordering::Equal {
72                return provider_cmp;
73            }
74        }
75
76        self.key.cmp(&other.key)
77    }
78}
79
80impl KV {
81    #[must_use]
82    pub fn to_data(kvs: &[Self]) -> BTreeMap<String, String> {
83        let mut data = BTreeMap::new();
84        for kv in kvs {
85            data.insert(kv.key.clone(), kv.value.clone());
86        }
87        data
88    }
89
90    #[must_use]
91    pub fn from_data(
92        data: &BTreeMap<String, String>,
93        pm: &PathMap,
94        provider: &ProviderInfo,
95    ) -> Vec<Self> {
96        // map all of the data found
97        if pm.keys.is_empty() {
98            data.iter()
99                .map(|(k, v)| Self::from_value(v, k, k, pm, provider.clone()))
100                .collect::<Vec<_>>()
101        } else {
102            // selectively map only keys from pathmap
103            pm.keys
104                .iter()
105                .filter_map(|(from_key, to_key)| {
106                    data.get(from_key).map(|found_val| {
107                        Self::from_value(found_val, from_key, to_key, pm, provider.clone())
108                    })
109                })
110                .collect::<Vec<_>>()
111        }
112    }
113    #[must_use]
114    pub fn from_value(
115        found_val: &str,
116        from_key: &str,
117        to_key: &str,
118        pm: &PathMap,
119        provider: ProviderInfo,
120    ) -> Self {
121        Self {
122            value: found_val.to_string(),
123            key: to_key.to_string(),
124            from_key: from_key.to_string(),
125            path: Some(PathInfo {
126                path: pm.path.clone(),
127                id: pm.id.to_string(),
128            }),
129            provider: Some(provider),
130            meta: Some(MetaInfo {
131                sensitivity: pm.sensitivity.clone(),
132                redact_with: pm.redact_with.clone(),
133                source: pm.source.clone(),
134                sink: pm.sink.clone(),
135            }),
136        }
137    }
138    #[must_use]
139    pub fn from_literal(path: &str, key: &str, value: &str, provider: ProviderInfo) -> Self {
140        Self {
141            value: value.to_string(),
142            key: key.to_string(),
143            from_key: key.to_string(),
144            path: Some(PathInfo {
145                id: path.to_string(),
146                path: path.to_string(),
147            }),
148            provider: Some(provider),
149            ..Default::default()
150        }
151    }
152
153    /// represents a KV without any source (e.g. created manually by a user, pending insert to
154    /// one of the providers)
155    #[must_use]
156    pub fn from_kv(key: &str, value: &str) -> Self {
157        Self {
158            value: value.to_string(),
159            key: key.to_string(),
160            from_key: key.to_string(),
161            ..Default::default()
162        }
163    }
164}
165
166#[derive(Serialize, Deserialize, Debug, Clone, Default)]
167pub struct PathMap {
168    pub id: String,
169    #[serde(rename = "protocol", skip_serializing_if = "Option::is_none")]
170    pub protocol: Option<String>,
171    #[serde(rename = "path")]
172    pub path: String,
173    #[serde(default, rename = "keys", skip_serializing_if = "is_default")]
174    pub keys: BTreeMap<String, String>,
175    #[serde(default, rename = "decrypt", skip_serializing_if = "is_default")]
176    pub decrypt: bool,
177    #[serde(default, rename = "sensitivity", skip_serializing_if = "is_default")]
178    pub sensitivity: Sensitivity,
179    #[serde(
180        default,
181        rename = "redact_with",
182        skip_serializing_if = "Option::is_none"
183    )]
184    pub redact_with: Option<String>,
185    #[serde(default, rename = "source", skip_serializing_if = "Option::is_none")]
186    pub source: Option<String>,
187    #[serde(default, rename = "sink", skip_serializing_if = "Option::is_none")]
188    pub sink: Option<String>,
189    // ignore population if optional + we got error
190    #[serde(default, rename = "optional", skip_serializing_if = "is_default")]
191    pub optional: bool,
192}
193
194impl PathMap {
195    #[must_use]
196    pub fn from_path(path: &str) -> Self {
197        Self {
198            path: path.to_string(),
199            ..Default::default()
200        }
201    }
202}