aws_runtime/env_config/
property.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6//! Sections within an AWS config profile.
7
8use std::collections::HashMap;
9use std::fmt;
10
11/// Key-Value property pair
12#[derive(Debug, Clone, Eq, PartialEq)]
13pub struct Property {
14    key: String,
15    value: String,
16}
17
18impl Property {
19    /// Value of this property
20    pub fn value(&self) -> &str {
21        &self.value
22    }
23
24    /// Name of this property
25    pub fn key(&self) -> &str {
26        &self.key
27    }
28
29    /// Creates a new property
30    pub fn new(key: String, value: String) -> Self {
31        Property { key, value }
32    }
33}
34
35type SectionKey = String;
36type SectionName = String;
37type PropertyName = String;
38type SubPropertyName = String;
39type PropertyValue = String;
40
41/// A key for to a property value.
42///
43/// ```txt
44/// # An example AWS profile config section with properties and sub-properties
45/// [section-key section-name]
46/// property-name = property-value
47/// property-name =
48///   sub-property-name = property-value
49/// ```
50#[derive(Clone, Debug, PartialEq, Eq, Hash)]
51pub struct PropertiesKey {
52    section_key: SectionKey,
53    section_name: SectionName,
54    property_name: PropertyName,
55    sub_property_name: Option<SubPropertyName>,
56}
57
58impl PropertiesKey {
59    /// Create a new [`PropertiesKeyBuilder`].
60    pub fn builder() -> PropertiesKeyBuilder {
61        Default::default()
62    }
63}
64
65impl fmt::Display for PropertiesKey {
66    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67        let PropertiesKey {
68            section_key,
69            section_name,
70            property_name,
71            sub_property_name,
72        } = self;
73        match sub_property_name {
74            Some(sub_property_name) => {
75                write!(
76                    f,
77                    "[{section_key} {section_name}].{property_name}.{sub_property_name}"
78                )
79            }
80            None => {
81                write!(f, "[{section_key} {section_name}].{property_name}")
82            }
83        }
84    }
85}
86
87/// Builder for [`PropertiesKey`]s.
88#[derive(Debug, Default)]
89pub struct PropertiesKeyBuilder {
90    section_key: Option<SectionKey>,
91    section_name: Option<SectionName>,
92    property_name: Option<PropertyName>,
93    sub_property_name: Option<SubPropertyName>,
94}
95
96impl PropertiesKeyBuilder {
97    /// Set the section key for this builder.
98    pub fn section_key(mut self, section_key: impl Into<String>) -> Self {
99        self.section_key = Some(section_key.into());
100        self
101    }
102
103    /// Set the section name for this builder.
104    pub fn section_name(mut self, section_name: impl Into<String>) -> Self {
105        self.section_name = Some(section_name.into());
106        self
107    }
108
109    /// Set the property name for this builder.
110    pub fn property_name(mut self, property_name: impl Into<String>) -> Self {
111        self.property_name = Some(property_name.into());
112        self
113    }
114
115    /// Set the sub-property name for this builder.
116    pub fn sub_property_name(mut self, sub_property_name: impl Into<String>) -> Self {
117        self.sub_property_name = Some(sub_property_name.into());
118        self
119    }
120
121    /// Build this builder. If all required fields are set,
122    /// `Ok(PropertiesKey)` is returned. Otherwise, an error is returned.
123    pub fn build(self) -> Result<PropertiesKey, String> {
124        Ok(PropertiesKey {
125            section_key: self
126                .section_key
127                .ok_or("A section_key is required".to_owned())?,
128            section_name: self
129                .section_name
130                .ok_or("A section_name is required".to_owned())?,
131            property_name: self
132                .property_name
133                .ok_or("A property_name is required".to_owned())?,
134            sub_property_name: self.sub_property_name,
135        })
136    }
137}
138
139/// A map of [`PropertiesKey`]s to property values.
140#[derive(Clone, Debug, Default, PartialEq, Eq)]
141pub struct Properties {
142    inner: HashMap<PropertiesKey, PropertyValue>,
143}
144
145#[allow(dead_code)]
146impl Properties {
147    /// Create a new empty [`Properties`].
148    pub fn new() -> Self {
149        Default::default()
150    }
151
152    #[cfg(test)]
153    pub(crate) fn new_from_slice(slice: &[(PropertiesKey, PropertyValue)]) -> Self {
154        let mut properties = Self::new();
155        for (key, value) in slice {
156            properties.insert(key.clone(), value.clone());
157        }
158        properties
159    }
160
161    /// Insert a new key/value pair into this map.
162    pub fn insert(&mut self, properties_key: PropertiesKey, value: PropertyValue) {
163        let _ = self
164            .inner
165            // If we don't clone then we don't get to log a useful warning for a value getting overwritten.
166            .entry(properties_key.clone())
167            .and_modify(|v| {
168                tracing::trace!("overwriting {properties_key}: was {v}, now {value}");
169                v.clone_from(&value);
170            })
171            .or_insert(value);
172    }
173
174    /// Given a [`PropertiesKey`], return the corresponding value, if any.
175    pub fn get(&self, properties_key: &PropertiesKey) -> Option<&PropertyValue> {
176        self.inner.get(properties_key)
177    }
178}