struct_to_config/
lib.rs

1extern crate self as struct_to_config;
2
3use config::{ConfigBuilder, Source, ValueKind};
4use config::builder::DefaultState;
5use lazy_static::lazy_static;
6use serde_json::{Value};
7#[cfg(test)]
8mod tests;
9
10
11const COULD_NOT_SET_ITEM : &'static str = "Could not set default for key/value pair on config_builder.";
12const NOT_SUPPORTED_TYPE : &'static str = "Serialized object was not one of supported types.";
13
14
15enum Kind {
16    String, Float, Boolean, Object, Nil
17}
18
19lazy_static! {
20    static ref DEFAULT_VEC : Vec<Value> = Vec::<Value>::new();
21}
22fn get_value_kind_from_value(val: &Value, should_panic: bool) -> (ValueKind, Kind) {
23    match val {
24        Value::Null => {
25            (ValueKind::Nil, Kind::Nil)
26        }
27        Value::String(f ) => {
28            (ValueKind::String(f.to_string()), Kind::String)
29        },
30        Value::Number(f ) => {
31            (ValueKind::Float(f.as_f64().unwrap()), Kind::Float)
32        },
33        Value::Bool(f ) => {
34            (ValueKind::Boolean(*f), Kind::Boolean)
35        },
36        Value::Object(o) => {
37            let values : Vec<ValueKind> = o.iter().map(|i| {
38               return get_value_kind_from_value(i.1, should_panic).0.clone();
39            }).collect();
40            let val_kind = config::ValueKind::from(values);
41            (val_kind, Kind::Object)
42        },
43        _ => {
44            if should_panic {
45                panic!("Found a value in array that is not a String, Number, Struct, or Bool");
46            } else {
47                (ValueKind::Nil, Kind::Nil)
48            }
49        }
50    }
51}
52pub fn convert_panic(value : &Value, cfg_builder: Option<ConfigBuilder<DefaultState>>) -> ConfigBuilder<DefaultState> {
53    let mut config_builder = cfg_builder.unwrap_or(config::Config::builder());
54    if value.is_object() {
55        let map = value.as_object();
56        for item in map.unwrap() {
57            if item.1.is_array() {
58                let arr = item.1.as_array().expect(COULD_NOT_SET_ITEM);
59                let mut new_arr : Vec<ValueKind> = Vec::new();
60                for ref i in arr.iter() {
61                    let val : ValueKind = get_value_kind_from_value(i, true).0;
62                    new_arr.push(val);
63                }
64                config_builder = config_builder.set_default(item.0.as_str(), new_arr).expect(COULD_NOT_SET_ITEM);
65            }else if item.1.is_string() {
66                config_builder = config_builder.set_default(item.0.as_str(), item.1.as_str().unwrap()).expect(COULD_NOT_SET_ITEM);
67            } else if item.1.is_i64() {
68                config_builder = config_builder.set_default(item.0.as_str(), item.1.as_i64().unwrap()).expect(COULD_NOT_SET_ITEM);
69            } else if item.1.is_boolean() {
70                config_builder = config_builder.set_default(item.0.as_str(), item.1.as_bool().unwrap()).expect(COULD_NOT_SET_ITEM);
71            } else if item.1.is_f64() {
72                config_builder = config_builder.set_default(item.0.as_str(), item.1.as_f64().unwrap()).expect(COULD_NOT_SET_ITEM);
73            } else if item.1.is_u64() {
74                config_builder = config_builder.set_default(item.0.as_str(), item.1.as_u64().unwrap()).expect(COULD_NOT_SET_ITEM);
75            } else if item.1.is_object() {
76                config_builder = convert_panic(item.1, Some(config_builder));
77            } else if item.1.is_null() {
78                continue;
79            } else {
80                panic!("{}", NOT_SUPPORTED_TYPE);
81            }
82        }
83    }
84    config_builder
85}
86
87pub fn convert_non_panic(value : &Value, cfg_builder: Option<ConfigBuilder<DefaultState>>) -> ConfigBuilder<DefaultState> {
88    let mut config_builder = cfg_builder.unwrap_or(config::Config::builder());
89    if value.is_object() {
90        let map = value.as_object();
91        for item in map.unwrap() {
92            if item.1.is_array() {
93                let arr = item.1.as_array().unwrap_or(&DEFAULT_VEC);
94                let mut new_arr : Vec<ValueKind> = Vec::new();
95                for ref i in arr.iter() {
96                    let val : ValueKind = get_value_kind_from_value(i, false).0;
97                    new_arr.push(val);
98                }
99                config_builder = config_builder.set_default(item.0.as_str(), new_arr).unwrap_or_default();
100            }else if item.1.is_string() {
101                config_builder = config_builder.set_default(item.0.as_str(), item.1.as_str().unwrap()).unwrap_or_default();
102            } else if item.1.is_i64() {
103                config_builder = config_builder.set_default(item.0.as_str(), item.1.as_i64().unwrap()).unwrap_or_default();
104            } else if item.1.is_boolean() {
105                config_builder = config_builder.set_default(item.0.as_str(), item.1.as_bool().unwrap()).unwrap_or_default();
106            } else if item.1.is_f64() {
107                config_builder = config_builder.set_default(item.0.as_str(), item.1.as_f64().unwrap()).unwrap_or_default();
108            } else if item.1.is_u64() {
109                config_builder = config_builder.set_default(item.0.as_str(), item.1.as_u64().unwrap()).unwrap_or_default();
110            } else if item.1.is_object() {
111                config_builder = convert_non_panic(item.1, Some(config_builder));
112            } else if item.1.is_null() {
113                continue;
114            } else {
115                println!("Deserialized object is not one of supported types, so it was not added to the config.");
116            }
117        }
118    }
119    config_builder
120}
121#[macro_export]
122/// Takes one or more identities of which are struct objects that implement serde Serializable.
123/// Optionally, for the first argument, you may provide a boolean flag that if false, you would want the behavior of the macro to default on any struct attribute it cannot convert.
124/// Otherwise, if true, it will panic.
125/// If not providing a boolean literal as the first argument, you may list as many structs, with the last structs being of higher priority, i.e. it will override the prior key/value, if a matching key is found for its value.
126/// Default behavior is that calling this macro will panic if a struct has a non-implemented type, such as (currently) an array of arrays.
127macro_rules! make_cfg {
128    ($($serializeable:ident),+) => {{
129        use serde_json::Value;
130        use struct_to_config::convert_panic;
131        let mut config_builder = Config::builder();
132        $(
133        let value = serde_json::to_value($serializeable).unwrap();
134        config_builder = convert_panic(&value, Some(config_builder));
135        )+
136        config_builder.build().unwrap()
137    }};
138    ($bool_should_panic:literal,$($serializeable:ident ),+) => {{
139        use struct_to_config::convert_panic;
140        use struct_to_config::convert_non_panic;
141        let mut config_builder = Config::builder();
142        if !$bool_should_panic {
143            $(
144            let value = serde_json::to_value($serializeable).unwrap();
145            config_builder = convert_non_panic(&value, Some(config_builder));
146            )+
147        } else {
148            $(
149            let value = serde_json::to_value($serializeable).unwrap();
150            config_builder = convert_panic(&value, Some(config_builder));
151            )+
152        }
153        config_builder.build().unwrap()
154    }};
155}