settingsfile/structs/
types.rs

1use std::collections::HashMap;
2use std::fmt;
3
4
5/// Generic type enum used to work with data inside a `Settings`
6/// 
7/// Only should be used when interacting with the result of a 
8/// `get_value`.
9/// 
10/// ```rust
11/// # extern crate settingsfile;
12/// # use settingsfile::Settings;
13/// # use settingsfile::EmptyConfig;
14/// use settingsfile::Type;
15/// 
16/// let settings = Settings::new(EmptyConfig{});
17/// 
18/// match settings.get_value("options") {
19///     Some(Type::Text(option)) => { }, // a single option selected
20///     Some(Type::Array(options)) => { }, // multiple options here,
21///     _ => { }, // probably not valid because options in this case are strings
22/// }
23/// ```
24#[derive(Serialize,Deserialize,Debug,Clone,PartialEq)]
25#[serde(untagged)]
26pub enum Type {
27    Text(String),
28    Switch(bool),
29    Int(i32),
30    Float(f32),
31    Complex(HashMap<String,Type>),
32    Array(Vec<Type>),
33    None,
34}
35
36impl Type {
37    // Checking types to see if `Type` is what you think it is, or want it to be.
38    pub fn is_text(&self) -> bool { if let &Type::Text(_) = self { true } else { false } }
39    pub fn is_switch(&self) -> bool { if let &Type::Switch(_) = self { true } else { false } }
40    pub fn is_int(&self) -> bool { if let &Type::Int(_) = self { true } else { false } }
41    pub fn is_float(&self) -> bool { if let &Type::Float(_) = self { true } else { false } }
42    pub fn is_complex(&self) -> bool { if let &Type::Complex(_) = self { true } else { false } }
43    pub fn is_array(&self) -> bool { if let &Type::Array(_) = self { true } else { false } }
44    pub fn is_none(&self) -> bool { if let &Type::None = self { true } else { false } }
45
46    // Casts to get the inner value of the type. If you cast to the wrong thing you will get a None.
47    // These don't "use" the original data but instead clone it.
48    pub fn to_text(&self) -> Option<String> { if let &Type::Text(ref inner) = self { Some(inner.clone()) } else { None } }
49    pub fn to_switch(&self) -> Option<bool> { if let &Type::Switch(ref inner) = self { Some(inner.clone()) } else { None } }
50    pub fn to_int(&self) -> Option<i32> { if let &Type::Int(ref inner) = self { Some(inner.clone()) } else { None } }
51    pub fn to_float(&self) -> Option<f32> { if let &Type::Float(ref inner) = self { Some(inner.clone()) } else { None } }
52    pub fn to_complex(&self) -> Option<HashMap<String,Type>> { if let &Type::Complex(ref inner) = self { Some(inner.clone()) } else { None } }
53    pub fn to_array(&self) -> Option<Vec<Type>> { if let &Type::Array(ref inner) = self { Some(inner.clone()) } else { None } }
54
55    // pub fn move_it(self) -> Type { self }
56
57    pub fn to_string(&self) -> String {
58        format!("{}",self)
59    }
60
61    pub fn flatten(&self , parent_key : Option<String>) -> Type {
62        //! Flattens the `Type`. 
63        //! 
64        //! If the type is anything but a `Type::Complex` it just 
65        //! returns a copy of the original `Type`.
66        //! 
67        //! If the type is a `Type::Complex` it returns a 1D Complex,
68        //! thus flattening it. This is usually used recursively to
69        //! flatten an entire `Settings`
70
71        match self {
72            &Type::Text(ref text) => Type::Text(text.clone()),
73            &Type::Switch(ref boolean) => Type::Switch(boolean.clone()),
74            &Type::Int(ref int) => Type::Int(int.clone()),
75            &Type::Float(ref float) => Type::Float(float.clone()),
76            &Type::Array(ref array) => Type::Array(array.clone()),
77            &Type::None => Type::None,
78            &Type::Complex(ref numb) => {
79                let mut flat : HashMap<String,Type> = HashMap::new();
80
81                for (key,value) in numb {
82                    let parent = if let Some(ref parent_key) = parent_key { 
83                        format!("{}.{}",parent_key,key) 
84                    } else { 
85                        key.to_string() 
86                    };
87
88                    let flattened = value.flatten(Some(parent.to_string()));
89
90                    if flattened.is_complex() {
91                        for (key,value) in flattened.to_complex().unwrap() {
92                            flat.insert(key,value);
93                        }
94                    } else {
95                        flat.insert(parent.to_string(),flattened);
96                    }
97                }
98
99                Type::Complex(flat)
100            }
101        }
102    }
103}
104
105impl fmt::Display for Type {
106    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
107        match self {
108            Type::Int(ref value) => write!(f,"{}",value),
109            Type::Switch(ref value) => write!(f,"{}",value),
110            Type::Float(ref value) => write!(f,"{}",value),
111            Type::Text(ref value) => write!(f,"{}",value),
112            Type::None => write!(f,"[BLANK]"),
113            Type::Array(ref value) => {
114                write!(f,"[ ")?;
115                for i in 0..value.len() {
116                    write!(f,"{}",value[i])?;
117                    if i < value.len() - 1 { 
118                        write!(f,", ")?;
119                    }
120                }
121                write!(f," ]")
122            },
123            Type::Complex(ref value) => {
124                write!(f,"{{ ")?;
125                for (k,v) in value {
126                    write!(f,"{} : {}, ", k,v)?;
127                }
128                write!(f," }}")
129            }
130        }
131    }
132}
133
134#[cfg(test)]
135mod tests {
136    use Type;
137    use std::collections::HashMap;
138
139    #[test]
140    fn flatten() {
141        //! Testing if flattening works correctly, something very basic.
142        
143        let mut hash : HashMap<String,Type> = HashMap::new();
144        let mut hash2 : HashMap<String,Type> = HashMap::new();
145        hash2.insert("a".to_string(), Type::Switch(true));
146        hash2.insert("float".to_string(), Type::Float(10.23));
147        hash2.insert("int".to_string(), Type::Int(10));
148        hash2.insert("array".to_string(), Type::Array(vec![Type::Int(1), Type::Switch(false), Type::Text("testing here".to_string())]));
149        
150        hash.insert("b".to_string(),Type::Complex(hash2));
151
152        let complex = Type::Complex(hash).flatten(None);
153
154        if let Type::Complex(ref stuff) = complex {
155            for (k,v) in stuff {
156                println!("{} : {:?}",k,v);
157            }
158        }
159
160        assert!(complex.to_complex().unwrap().get("b.a").unwrap().to_switch().unwrap());
161        assert!(complex.to_complex().unwrap().get("a") == None);
162        assert!(complex.to_complex().unwrap().get("b.int").unwrap().to_int().unwrap() == 10);
163        assert!(complex.to_complex().unwrap().get("b.float").unwrap().to_float().unwrap() == 10.23);
164    }
165
166    #[test]
167    fn display_print() {
168        let test1 = Type::Int(12);
169        let test2 = Type::Switch(false);
170        let test3 = Type::Float(12.01);
171        let test4 = Type::Text("Wjat os tjos".to_string());
172        let test5 = Type::Array(vec![ Type::Int(1),Type::Float(2.2) ]);
173
174        let mut hash : HashMap<String,Type> = HashMap::new();
175        hash.insert("1".to_string(),test1.clone());
176        hash.insert("2".to_string(),test2.clone());
177        hash.insert("3".to_string(),test3.clone());
178        hash.insert("4".to_string(),test4.clone());
179        hash.insert("5".to_string(),test5.clone());
180        let test6 = Type::Complex(hash);
181
182        println!("{}",test1);
183        println!("{}",test2);
184        println!("{}",test3);
185        println!("{}",test4);
186        println!("{}",test5);
187        println!("{}",test6);
188
189        assert!(true);
190    }
191}