apolloconfig/objects/
configurations.rs

1use core::fmt;
2
3use serde::{ser, Serialize};
4use serde_json::{Map, Value};
5
6use crate::types::NamespaceFormat;
7
8//
9pub const KEY_CONTENT: &str = "content";
10
11//
12#[derive(Debug, Clone)]
13pub enum Configurations {
14    Properties(Map<String, Value>),
15    Json(Value),
16    Txt(Box<str>),
17    Other(Box<str>),
18}
19
20impl Configurations {
21    pub fn from_map(
22        map: &Map<String, Value>,
23        namespace_format: NamespaceFormat,
24    ) -> Result<Self, ConfigurationsFromMapError> {
25        match namespace_format {
26            NamespaceFormat::Properties => Ok(Self::Properties(map.to_owned())),
27            NamespaceFormat::Xml
28            | NamespaceFormat::Json
29            | NamespaceFormat::Yml
30            | NamespaceFormat::Yaml
31            | NamespaceFormat::Txt => {
32                let content = map
33                    .get(KEY_CONTENT)
34                    .ok_or(ConfigurationsFromMapError::ContentMissing)?;
35
36                match namespace_format {
37                    NamespaceFormat::Properties => unreachable!(),
38                    NamespaceFormat::Xml => {
39                        let content = content
40                            .as_str()
41                            .ok_or(ConfigurationsFromMapError::ContentTypeMismatch("String"))?;
42                        Ok(Self::Other(content.into()))
43                    }
44                    NamespaceFormat::Json => {
45                        let content = content
46                            .as_str()
47                            .ok_or(ConfigurationsFromMapError::ContentTypeMismatch("String"))?;
48                        let value = serde_json::from_str(content)
49                            .map_err(ConfigurationsFromMapError::JsonError)?;
50                        Ok(Self::Json(value))
51                    }
52                    NamespaceFormat::Yml => {
53                        let content = content
54                            .as_str()
55                            .ok_or(ConfigurationsFromMapError::ContentTypeMismatch("String"))?;
56                        Ok(Self::Other(content.into()))
57                    }
58                    NamespaceFormat::Yaml => {
59                        let content = content
60                            .as_str()
61                            .ok_or(ConfigurationsFromMapError::ContentTypeMismatch("String"))?;
62                        Ok(Self::Other(content.into()))
63                    }
64                    NamespaceFormat::Txt => {
65                        let content = content
66                            .as_str()
67                            .ok_or(ConfigurationsFromMapError::ContentTypeMismatch("String"))?;
68                        Ok(Self::Txt(content.into()))
69                    }
70                }
71            }
72        }
73    }
74
75    pub fn to_map(&self) -> Result<Map<String, Value>, ConfigurationsToMapError> {
76        match self {
77            Configurations::Properties(map) => Ok(map.to_owned()),
78            Configurations::Json(value) => {
79                let s = serde_json::to_string(value)
80                    .map_err(ser::Error::custom)
81                    .map_err(ConfigurationsToMapError::JsonError)?;
82                let mut map = Map::new();
83                map.insert(KEY_CONTENT.to_string(), Value::String(s));
84                Ok(map.to_owned())
85            }
86            Configurations::Txt(s) | Configurations::Other(s) => {
87                let mut map = Map::new();
88                map.insert(KEY_CONTENT.to_string(), Value::String(s.to_string()));
89                Ok(map.to_owned())
90            }
91        }
92    }
93}
94
95//
96#[derive(Debug)]
97pub enum ConfigurationsFromMapError {
98    ContentMissing,
99    ContentTypeMismatch(&'static str),
100    JsonError(serde_json::Error),
101}
102
103impl fmt::Display for ConfigurationsFromMapError {
104    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105        write!(f, "{:?}", self)
106    }
107}
108
109impl std::error::Error for ConfigurationsFromMapError {}
110
111//
112#[derive(Debug)]
113pub enum ConfigurationsToMapError {
114    JsonError(serde_json::Error),
115}
116
117impl fmt::Display for ConfigurationsToMapError {
118    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119        write!(f, "{:?}", self)
120    }
121}
122
123impl std::error::Error for ConfigurationsToMapError {}
124
125//
126impl Serialize for Configurations {
127    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
128    where
129        S: ser::Serializer,
130    {
131        let map = self.to_map().map_err(ser::Error::custom)?;
132        map.serialize(serializer)
133    }
134}
135
136#[cfg(test)]
137mod tests {
138    use super::*;
139
140    #[derive(Serialize)]
141    struct Foo {
142        configurations: Configurations,
143    }
144
145    #[test]
146    fn test_ser() {
147        //
148        let mut map = Map::new();
149        map.insert("content".to_string(), Value::String("content".into()));
150        assert_eq!(
151            serde_json::to_string(&Foo {
152                configurations: Configurations::Properties(map)
153            })
154            .unwrap(),
155            r#"{"configurations":{"content":"content"}}"#
156        );
157
158        //
159        let mut map = Map::new();
160        map.insert("k".to_string(), Value::String("v".into()));
161        assert_eq!(
162            serde_json::to_string(&Foo {
163                configurations: Configurations::Json(Value::Object(map))
164            })
165            .unwrap(),
166            r#"{"configurations":{"content":"{\"k\":\"v\"}"}}"#
167        );
168
169        //
170        assert_eq!(
171            serde_json::to_string(&Foo {
172                configurations: Configurations::Txt("test".into())
173            })
174            .unwrap(),
175            r#"{"configurations":{"content":"test"}}"#
176        );
177
178        //
179        assert_eq!(
180            serde_json::to_string(&Foo {
181                configurations: Configurations::Other("xxx".into())
182            })
183            .unwrap(),
184            r#"{"configurations":{"content":"xxx"}}"#
185        );
186    }
187}