1#![deny(warnings)]
18#![deny(missing_docs)]
19#![recursion_limit = "1024"]
20#![deny(
21 clippy::all,
22 clippy::unwrap_used,
23 clippy::unnecessary_unwrap,
24 clippy::pedantic,
25 clippy::mod_module_files
26)]
27
28use serde::Deserialize;
29use tremor_value::prelude::*;
30
31#[derive(Clone, Debug, Default)]
33#[allow(clippy::module_name_repetitions)]
34pub struct NameWithConfig {
35 pub name: String,
37 pub config: Option<Value<'static>>,
39}
40
41impl<'v> serde::Deserialize<'v> for NameWithConfig {
42 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
43 where
44 D: serde::Deserializer<'v>,
45 {
46 #[derive(Deserialize, Debug)]
50 #[serde(bound(deserialize = "'de: 'v, 'v: 'de"), untagged)]
51 enum Variants<'v> {
52 Name(String),
54 NameAndConfig { name: String, config: Value<'v> },
56 NameAndNoConfig { name: String },
58 }
59
60 let var = Variants::deserialize(deserializer)?;
61
62 match var {
63 Variants::NameAndConfig { name, config } => Ok(NameWithConfig {
64 name,
65 config: Some(config.into_static()),
66 }),
67 Variants::NameAndNoConfig { name } | Variants::Name(name) => {
68 Ok(NameWithConfig { name, config: None })
69 }
70 }
71 }
72}
73
74impl<'v> TryFrom<&Value<'v>> for NameWithConfig {
75 type Error = Error;
76
77 fn try_from(value: &Value) -> Result<Self, Error> {
78 if let Some(name) = value.as_str() {
79 Ok(Self::from(name))
80 } else if let Some(name) = value.get_str("name") {
81 Ok(Self {
82 name: name.to_string(),
83 config: value.get("config").map(Value::clone_static),
84 })
85 } else {
86 Err(Error::InvalidConfig(value.encode()))
87 }
88 }
89}
90
91#[derive(Debug, Clone, PartialEq)]
93pub enum Error {
94 InvalidConfig(String),
96}
97
98impl std::fmt::Display for Error {
99 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
100 match self {
101 Self::InvalidConfig(v) => write!(f, "Invalid config: {v}"),
102 }
103 }
104}
105
106impl std::error::Error for Error {}
107
108impl From<&str> for NameWithConfig {
109 fn from(name: &str) -> Self {
110 Self {
111 name: name.to_string(),
112 config: None,
113 }
114 }
115}
116impl From<&String> for NameWithConfig {
117 fn from(name: &String) -> Self {
118 name.clone().into()
119 }
120}
121impl From<String> for NameWithConfig {
122 fn from(name: String) -> Self {
123 Self { name, config: None }
124 }
125}
126
127pub trait Impl {
129 fn new(config: &tremor_value::Value) -> Result<Self, tremor_value::Error>
135 where
136 Self: serde::de::Deserialize<'static>,
137 {
138 tremor_value::structurize(config.clone_static())
139 }
140}
141
142pub type Map = Option<tremor_value::Value<'static>>;
144
145#[cfg(test)]
146mod test {
147 use std::collections::HashMap;
148
149 use super::*;
150 #[test]
151 fn name_with_config() {
152 let v = literal!({"name": "json", "config": {"mode": "sorted"}});
153 let nac = NameWithConfig::deserialize(v).expect("could structurize two element struct");
154 assert_eq!(nac.name, "json");
155 assert!(nac.config.as_object().is_some());
156 let v = literal!({"name": "yaml"});
157 let nac = NameWithConfig::deserialize(v).expect("could structurize one element struct");
158 assert_eq!(nac.name, "yaml");
159 assert_eq!(nac.config, None);
160 let v = literal!("name");
161 let nac = NameWithConfig::deserialize(v).expect("could structurize string");
162 assert_eq!(nac.name, "name");
163 assert_eq!(nac.config, None);
164 }
165
166 #[test]
167 fn name_with_config_in_a_hatemap() {
168 let codec = "json";
169 let data = literal!( {
170 "application/json": {"name": "json", "config": {"mode": "sorted"}},
171 "application/yaml": {"name": "yaml"},
172 "*/*": codec,
173 });
174 let nac = HashMap::<String, NameWithConfig>::deserialize(data)
175 .expect("could structurize two element struct");
176
177 assert_eq!(nac.len(), 3);
178 }
179
180 #[test]
181 fn name_with_config_in_a_hatemap_in_struct() {
182 #[derive(Deserialize, Debug, Clone)]
183 #[serde(deny_unknown_fields)]
184 struct Config {
185 mime_mapping: Option<HashMap<String, NameWithConfig>>,
186 }
187 let codec = "json";
188 let data = literal!({ "mime_mapping": {
189 "application/json": {"name": "json", "config": {"mode": "sorted"}},
190 "application/yaml": {"name": "yaml"},
191 "*/*": codec,
192 }});
193 let nac = Config::deserialize(data).expect("could structurize two element struct");
194
195 assert_eq!(nac.mime_mapping.map(|h| h.len()).unwrap_or_default(), 3);
196 }
197
198 #[test]
200 fn name_with_config_invalid() {
201 let v = literal!({"name": "json", "config": {"mode": "sorted"}});
202 let nac = NameWithConfig::try_from(&v).expect("could structurize two element struct");
203 assert_eq!(nac.name, "json");
204 assert!(nac.config.as_object().is_some());
205 let v = literal!({"name": "yaml"});
206 let nac = NameWithConfig::try_from(&v).expect("could structurize one element struct");
207 assert_eq!(nac.name, "yaml");
208 assert_eq!(nac.config, None);
209 let v = literal!("name");
210 let nac = NameWithConfig::try_from(&v).expect("could structurize string");
211 assert_eq!(nac.name, "name");
212 assert_eq!(nac.config, None);
213 let v = literal!({"snot": "yaml"});
214 let nac = NameWithConfig::try_from(&v);
215 assert!(nac.is_err());
216 assert_eq!(
217 nac.err().map(|e| e.to_string()).expect("err"),
218 r#"Invalid config: {"snot":"yaml"}"#
219 );
220 }
221}