1use std::collections::HashMap;
2use std::convert::TryInto;
3
4use base64::Engine;
5use serde::{Deserialize, Serialize};
6
7use crate::ConfigError;
9
10macro_rules! try_into_impl_basic {
12 ($target_type:ty, $c5_variant:ident, $expected_type_str:literal) => {
16 impl TryInto<$target_type> for C5DataValue {
17 type Error = ConfigError;
18
19 #[inline]
20 fn try_into(self) -> Result<$target_type, Self::Error> {
21 match self {
22 C5DataValue::$c5_variant(inner_value) => Ok(inner_value),
23 other => Err(ConfigError::TypeMismatch {
24 key: "_conversion_".to_string(),
27 expected_type: $expected_type_str,
28 found_type: other.type_name(),
29 }),
30 }
31 }
32 }
33
34 impl TryInto<$target_type> for &C5DataValue {
36 type Error = ConfigError;
37
38 #[inline]
39 fn try_into(self) -> Result<$target_type, Self::Error> {
40 match self {
41 C5DataValue::$c5_variant(inner_value) => Ok(inner_value.clone()),
44 other => Err(ConfigError::TypeMismatch {
45 key: "_conversion_".to_string(),
46 expected_type: $expected_type_str,
47 found_type: other.type_name(),
48 }),
49 }
50 }
51 }
52 };
53 ($target_type:ty, $c5_variant:ident, $expected_type_str:literal, Copy) => {
55 impl TryInto<$target_type> for C5DataValue {
56 type Error = ConfigError;
57
58 #[inline]
59 fn try_into(self) -> Result<$target_type, Self::Error> {
60 match self {
61 C5DataValue::$c5_variant(inner_value) => Ok(inner_value),
62 other => Err(ConfigError::TypeMismatch {
63 key: "_conversion_".to_string(),
64 expected_type: $expected_type_str,
65 found_type: other.type_name(),
66 }),
67 }
68 }
69 }
70
71 impl TryInto<$target_type> for &C5DataValue {
72 type Error = ConfigError;
73
74 #[inline]
75 fn try_into(self) -> Result<$target_type, Self::Error> {
76 match self {
77 C5DataValue::$c5_variant(inner_value) => Ok(*inner_value), other => Err(ConfigError::TypeMismatch {
79 key: "_conversion_".to_string(),
80 expected_type: $expected_type_str,
81 found_type: other.type_name(),
82 }),
83 }
84 }
85 }
86 };
87}
88
89macro_rules! try_into_impl_numeric_cast {
92 ($target_type:ty, $c5_variant:ident, $expected_type_str:literal) => {
96 impl TryInto<$target_type> for C5DataValue {
97 type Error = ConfigError;
98
99 #[inline]
100 fn try_into(self) -> Result<$target_type, Self::Error> {
101 match self {
102 C5DataValue::$c5_variant(inner_value) => Ok(inner_value as $target_type),
106 other => Err(ConfigError::TypeMismatch {
107 key: "_conversion_".to_string(),
108 expected_type: $expected_type_str,
109 found_type: other.type_name(),
110 }),
111 }
112 }
113 }
114
115 impl TryInto<$target_type> for &C5DataValue {
116 type Error = ConfigError;
117
118 #[inline]
119 fn try_into(self) -> Result<$target_type, Self::Error> {
120 match self {
121 C5DataValue::$c5_variant(inner_value) => Ok(*inner_value as $target_type),
122 other => Err(ConfigError::TypeMismatch {
123 key: "_conversion_".to_string(),
124 expected_type: $expected_type_str,
125 found_type: other.type_name(),
126 }),
127 }
128 }
129 }
130 };
131}
132
133macro_rules! from_impl_numeric {
135 ($from_type:ty, $c5_variant:ident, $cast_type:ty) => {
136 impl From<$from_type> for C5DataValue {
137 #[inline]
138 fn from(value: $from_type) -> Self {
139 C5DataValue::$c5_variant(value as $cast_type)
140 }
141 }
142 };
143}
144
145macro_rules! try_into_impl_vec {
147 ($target_element_type:ty) => {
148 impl TryInto<Vec<$target_element_type>> for C5DataValue {
149 type Error = ConfigError;
150
151 fn try_into(self) -> Result<Vec<$target_element_type>, Self::Error> {
152 match self {
153 C5DataValue::Array(inner_value) => inner_value
154 .into_iter()
155 .map(|vec_item| vec_item.try_into()) .collect::<Result<Vec<$target_element_type>, ConfigError>>(), other => Err(ConfigError::TypeMismatch {
158 key: "_conversion_".to_string(),
159 expected_type: "Array",
160 found_type: other.type_name(),
161 }),
162 }
163 }
164 }
165
166 impl TryInto<Vec<$target_element_type>> for &C5DataValue {
167 type Error = ConfigError;
168
169 fn try_into(self) -> Result<Vec<$target_element_type>, Self::Error> {
170 match self {
171 C5DataValue::Array(inner_value) => inner_value
173 .iter() .map(|vec_item_ref| vec_item_ref.try_into()) .collect::<Result<Vec<$target_element_type>, ConfigError>>(),
176 other => Err(ConfigError::TypeMismatch {
177 key: "_conversion_".to_string(),
178 expected_type: "Array",
179 found_type: other.type_name(),
180 }),
181 }
182 }
183 }
184 };
185}
186
187#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
188pub enum C5DataValue {
189 Null,
190 Bytes(Vec<u8>),
191 Boolean(bool),
192 Integer(i64),
194 UInteger(u64),
196 Float(f64),
197 String(String),
198 Array(Vec<C5DataValue>),
199 Map(HashMap<String, C5DataValue>),
200}
201
202impl C5DataValue {
203 pub(crate) fn type_name(&self) -> &'static str {
205 match self {
206 C5DataValue::Null => "Null",
207 C5DataValue::Bytes(_) => "Bytes",
208 C5DataValue::Boolean(_) => "Boolean",
209 C5DataValue::Integer(_) => "Integer",
210 C5DataValue::UInteger(_) => "UInteger",
211 C5DataValue::Float(_) => "Float",
212 C5DataValue::String(_) => "String",
213 C5DataValue::Array(_) => "Array",
214 C5DataValue::Map(_) => "Map",
215 }
216 }
217
218 pub(crate) fn as_bytes(&self) -> Option<Vec<u8>> {
221 match self {
222 C5DataValue::String(value) => Some(value.as_bytes().to_vec()),
223 C5DataValue::Bytes(value) => Some(value.clone()),
224 C5DataValue::Boolean(value) => Some(if *value { vec![1] } else { vec![0] }),
225 C5DataValue::Float(value) => Some(value.to_ne_bytes().to_vec()),
226 C5DataValue::Integer(value) => Some(value.to_ne_bytes().to_vec()),
227 C5DataValue::UInteger(value) => Some(value.to_ne_bytes().to_vec()),
228 _ => None,
229 }
230 }
231}
232
233impl From<()> for C5DataValue {
236 #[inline]
237 fn from(_value: ()) -> Self {
238 C5DataValue::Null
239 }
240}
241impl From<Vec<u8>> for C5DataValue {
242 #[inline]
243 fn from(value: Vec<u8>) -> Self {
244 C5DataValue::Bytes(value)
245 }
246}
247impl From<bool> for C5DataValue {
248 #[inline]
249 fn from(value: bool) -> Self {
250 C5DataValue::Boolean(value)
251 }
252}
253impl From<String> for C5DataValue {
254 #[inline]
255 fn from(value: String) -> Self {
256 C5DataValue::String(value)
257 }
258}
259impl From<&str> for C5DataValue {
260 #[inline]
261 fn from(value: &str) -> Self {
262 C5DataValue::String(value.to_string())
263 }
264}
265impl From<Box<str>> for C5DataValue {
266 #[inline]
267 fn from(value: Box<str>) -> Self {
268 C5DataValue::String(value.into_string())
269 }
270}
271impl From<i64> for C5DataValue {
272 #[inline]
273 fn from(value: i64) -> Self {
274 C5DataValue::Integer(value)
275 }
276}
277impl From<u64> for C5DataValue {
278 #[inline]
279 fn from(value: u64) -> Self {
280 C5DataValue::UInteger(value)
281 }
282}
283impl From<f64> for C5DataValue {
284 #[inline]
285 fn from(value: f64) -> Self {
286 C5DataValue::Float(value)
287 }
288}
289impl From<Vec<C5DataValue>> for C5DataValue {
290 #[inline]
291 fn from(value: Vec<C5DataValue>) -> Self {
292 C5DataValue::Array(value)
293 }
294}
295impl From<HashMap<String, C5DataValue>> for C5DataValue {
296 #[inline]
297 fn from(value: HashMap<String, C5DataValue>) -> Self {
298 C5DataValue::Map(value)
299 }
300}
301
302from_impl_numeric!(i8, Integer, i64);
304from_impl_numeric!(i16, Integer, i64);
305from_impl_numeric!(i32, Integer, i64);
306from_impl_numeric!(isize, Integer, i64);
307from_impl_numeric!(u8, UInteger, u64);
308from_impl_numeric!(u16, UInteger, u64);
309from_impl_numeric!(u32, UInteger, u64);
310from_impl_numeric!(usize, UInteger, u64);
311from_impl_numeric!(f32, Float, f64);
312
313impl TryInto<()> for C5DataValue {
317 type Error = ConfigError;
318 #[inline]
319 fn try_into(self) -> Result<(), Self::Error> {
320 match self {
321 C5DataValue::Null => Ok(()),
322 other => Err(ConfigError::TypeMismatch {
323 key: "_conversion_".to_string(),
324 expected_type: "Null",
325 found_type: other.type_name(),
326 }),
327 }
328 }
329}
330impl TryInto<()> for &C5DataValue {
331 type Error = ConfigError;
332 #[inline]
333 fn try_into(self) -> Result<(), Self::Error> {
334 match self {
335 C5DataValue::Null => Ok(()),
336 other => Err(ConfigError::TypeMismatch {
337 key: "_conversion_".to_string(),
338 expected_type: "Null",
339 found_type: other.type_name(),
340 }),
341 }
342 }
343}
344
345try_into_impl_basic!(Vec<u8>, Bytes, "Bytes");
347
348try_into_impl_basic!(bool, Boolean, "Boolean", Copy);
350
351impl TryInto<String> for C5DataValue {
353 type Error = ConfigError;
354
355 #[inline]
356 fn try_into(self) -> Result<String, Self::Error> {
357 match self {
358 C5DataValue::String(s) => Ok(s),
359 C5DataValue::Bytes(b) => String::from_utf8(b).map_err(|e| ConfigError::ConversionError {
360 key: "_conversion_".to_string(), message: format!("decrypted bytes are not valid UTF-8: {}", e),
362 }),
363 other => Err(ConfigError::TypeMismatch {
364 key: "_conversion_".to_string(),
365 expected_type: "String or Bytes",
366 found_type: other.type_name(),
367 }),
368 }
369 }
370}
371
372impl TryInto<String> for &C5DataValue {
373 type Error = ConfigError;
374
375 #[inline]
376 fn try_into(self) -> Result<String, Self::Error> {
377 match self {
378 C5DataValue::String(s) => Ok(s.clone()), C5DataValue::Bytes(b) => {
380 String::from_utf8(b.clone()).map_err(|e| ConfigError::ConversionError {
381 key: "_conversion_".to_string(),
382 message: format!("decrypted bytes are not valid UTF-8: {}", e),
383 })
384 }
385 other => Err(ConfigError::TypeMismatch {
386 key: "_conversion_".to_string(),
387 expected_type: "String or Bytes",
388 found_type: other.type_name(),
389 }),
390 }
391 }
392}
393
394impl TryInto<Box<str>> for C5DataValue {
396 type Error = ConfigError;
397 #[inline]
398 fn try_into(self) -> Result<Box<str>, Self::Error> {
399 match self {
400 C5DataValue::String(inner_value) => Ok(inner_value.into_boxed_str()),
401 other => Err(ConfigError::TypeMismatch {
402 key: "_conversion_".to_string(),
403 expected_type: "String",
404 found_type: other.type_name(),
405 }),
406 }
407 }
408}
409impl TryInto<Box<str>> for &C5DataValue {
410 type Error = ConfigError;
411 #[inline]
412 fn try_into(self) -> Result<Box<str>, Self::Error> {
413 match self {
414 C5DataValue::String(inner_value) => Ok(inner_value.clone().into_boxed_str()),
415 other => Err(ConfigError::TypeMismatch {
416 key: "_conversion_".to_string(),
417 expected_type: "String",
418 found_type: other.type_name(),
419 }),
420 }
421 }
422}
423
424impl TryInto<i64> for C5DataValue {
428 type Error = ConfigError;
429 #[inline]
430 fn try_into(self) -> Result<i64, Self::Error> {
431 match self {
432 C5DataValue::Integer(i) => Ok(i),
433 C5DataValue::UInteger(u) => {
434 if u <= i64::MAX as u64 {
435 Ok(u as i64)
436 } else {
437 Err(ConfigError::ConversionError {
438 key: "_conversion_".to_string(),
439 message: format!("UInteger value {} out of range for i64", u),
440 })
441 }
442 }
443 other => Err(ConfigError::TypeMismatch {
444 key: "_conversion_".to_string(),
445 expected_type: "Integer or UInteger",
446 found_type: other.type_name(),
447 }),
448 }
449 }
450}
451impl TryInto<i64> for &C5DataValue {
452 type Error = ConfigError;
453 #[inline]
454 fn try_into(self) -> Result<i64, Self::Error> {
455 match self {
456 C5DataValue::Integer(i) => Ok(*i),
457 C5DataValue::UInteger(u) => {
458 if *u <= i64::MAX as u64 {
459 Ok(*u as i64)
460 } else {
461 Err(ConfigError::ConversionError {
462 key: "_conversion_".to_string(),
463 message: format!("UInteger value {} out of range for i64", u),
464 })
465 }
466 }
467 other => Err(ConfigError::TypeMismatch {
468 key: "_conversion_".to_string(),
469 expected_type: "Integer or UInteger",
470 found_type: other.type_name(),
471 }),
472 }
473 }
474}
475
476impl TryInto<u64> for C5DataValue {
478 type Error = ConfigError;
479 #[inline]
480 fn try_into(self) -> Result<u64, Self::Error> {
481 match self {
482 C5DataValue::UInteger(u) => Ok(u),
483 C5DataValue::Integer(i) => {
484 if i >= 0 {
485 Ok(i as u64)
486 } else {
487 Err(ConfigError::ConversionError {
488 key: "_conversion_".to_string(),
489 message: format!("Negative Integer value {} cannot be converted to u64", i),
490 })
491 }
492 }
493 other => Err(ConfigError::TypeMismatch {
494 key: "_conversion_".to_string(),
495 expected_type: "Integer or UInteger",
496 found_type: other.type_name(),
497 }),
498 }
499 }
500}
501impl TryInto<u64> for &C5DataValue {
502 type Error = ConfigError;
503 #[inline]
504 fn try_into(self) -> Result<u64, Self::Error> {
505 match self {
506 C5DataValue::UInteger(u) => Ok(*u),
507 C5DataValue::Integer(i) => {
508 if *i >= 0 {
509 Ok(*i as u64)
510 } else {
511 Err(ConfigError::ConversionError {
512 key: "_conversion_".to_string(),
513 message: format!("Negative Integer value {} cannot be converted to u64", i),
514 })
515 }
516 }
517 other => Err(ConfigError::TypeMismatch {
518 key: "_conversion_".to_string(),
519 expected_type: "Integer or UInteger",
520 found_type: other.type_name(),
521 }),
522 }
523 }
524}
525
526try_into_impl_basic!(f64, Float, "Float", Copy);
528
529try_into_impl_numeric_cast!(i8, Integer, "Integer");
534try_into_impl_numeric_cast!(i16, Integer, "Integer");
535try_into_impl_numeric_cast!(i32, Integer, "Integer");
536try_into_impl_numeric_cast!(isize, Integer, "Integer");
537try_into_impl_numeric_cast!(u8, UInteger, "UInteger");
538try_into_impl_numeric_cast!(u16, UInteger, "UInteger");
539try_into_impl_numeric_cast!(u32, UInteger, "UInteger");
540try_into_impl_numeric_cast!(usize, UInteger, "UInteger");
541
542try_into_impl_numeric_cast!(f32, Float, "Float");
544
545try_into_impl_basic!(Vec<C5DataValue>, Array, "Array");
549
550try_into_impl_basic!(HashMap<String, C5DataValue>, Map, "Map");
552
553try_into_impl_vec!(Vec<u8>);
555try_into_impl_vec!(bool);
556try_into_impl_vec!(String);
557try_into_impl_vec!(Box<str>);
558try_into_impl_vec!(i64);
559try_into_impl_vec!(u64);
560try_into_impl_vec!(f64);
561try_into_impl_vec!(i32);
563try_into_impl_vec!(u32);
564try_into_impl_vec!(f32);
565
566pub(crate) fn c5_value_to_serde_json(
567 c5_value: C5DataValue,
568) -> Result<serde_json::Value, Box<dyn std::error::Error>> {
569 match c5_value {
570 C5DataValue::Null => Ok(serde_json::Value::Null),
571 C5DataValue::Bytes(b) => Ok(serde_json::Value::String(
572 base64::engine::general_purpose::STANDARD.encode(&b),
573 )), C5DataValue::Boolean(b) => Ok(serde_json::Value::Bool(b)),
575 C5DataValue::Integer(i) => Ok(serde_json::json!(i)), C5DataValue::UInteger(u) => Ok(serde_json::json!(u)),
577 C5DataValue::Float(f) => Ok(serde_json::json!(f)),
578 C5DataValue::String(s) => Ok(serde_json::Value::String(s)),
579 C5DataValue::Array(arr) => {
580 let mut json_arr = Vec::with_capacity(arr.len());
581 for item in arr {
582 json_arr.push(c5_value_to_serde_json(item)?);
583 }
584 Ok(serde_json::Value::Array(json_arr))
585 }
586 C5DataValue::Map(map) => {
587 let mut json_map = serde_json::Map::with_capacity(map.len());
588 for (key, value) in map {
589 json_map.insert(key, c5_value_to_serde_json(value)?);
590 }
591 Ok(serde_json::Value::Object(json_map))
592 }
593 }
594}