Skip to main content

cutty_config/
lib.rs

1use std::collections::HashMap;
2use std::error::Error;
3use std::path::PathBuf;
4
5use log::LevelFilter;
6use serde::Deserialize;
7use toml::Value;
8
9pub trait SerdeReplace {
10    fn replace(&mut self, value: Value) -> Result<(), Box<dyn Error>>;
11}
12
13#[macro_export]
14macro_rules! impl_replace {
15    ($($ty:ty),*$(,)*) => {
16        $(
17            impl SerdeReplace for $ty {
18                fn replace(&mut self, value: Value) -> Result<(), Box<dyn Error>> {
19                    replace_simple(self, value)
20                }
21            }
22        )*
23    };
24}
25
26#[rustfmt::skip]
27impl_replace!(
28    usize, u8, u16, u32, u64, u128,
29    isize, i8, i16, i32, i64, i128,
30    f32, f64,
31    bool,
32    char,
33    String,
34    PathBuf,
35    LevelFilter,
36);
37
38fn replace_simple<'de, D>(data: &mut D, value: Value) -> Result<(), Box<dyn Error>>
39where
40    D: Deserialize<'de>,
41{
42    *data = D::deserialize(value)?;
43
44    Ok(())
45}
46
47impl<'de, T: Deserialize<'de>> SerdeReplace for Vec<T> {
48    fn replace(&mut self, value: Value) -> Result<(), Box<dyn Error>> {
49        replace_simple(self, value)
50    }
51}
52
53impl<'de, T: SerdeReplace + Deserialize<'de>> SerdeReplace for Option<T> {
54    fn replace(&mut self, value: Value) -> Result<(), Box<dyn Error>> {
55        match self {
56            Some(inner) => inner.replace(value),
57            None => replace_simple(self, value),
58        }
59    }
60}
61
62impl<'de, T: Deserialize<'de>> SerdeReplace for HashMap<String, T> {
63    fn replace(&mut self, value: Value) -> Result<(), Box<dyn Error>> {
64        // Deserialize replacement as HashMap.
65        let hashmap: HashMap<String, T> = Self::deserialize(value)?;
66
67        // Merge the two HashMaps, replacing existing values.
68        for (key, value) in hashmap {
69            self.insert(key, value);
70        }
71
72        Ok(())
73    }
74}