ion/
ion.rs

1mod display;
2mod from_ion;
3mod from_row;
4mod ion_error;
5mod section;
6mod value;
7
8pub use self::from_ion::*;
9pub use self::from_row::*;
10pub use self::ion_error::*;
11pub use self::section::*;
12pub use self::value::*;
13use crate::Parser;
14use std::collections::BTreeMap;
15use std::str;
16
17#[derive(Clone, Debug)]
18pub struct Ion {
19    sections: BTreeMap<String, Section>,
20}
21
22impl Ion {
23    pub fn new(sections: BTreeMap<String, Section>) -> Ion {
24        Ion { sections }
25    }
26
27    pub fn from_str_filtered(s: &str, accepted_sections: Vec<&str>) -> Result<Self, IonError> {
28        parser_to_ion(Parser::new_filtered(s, accepted_sections))
29    }
30
31    pub fn get(&self, key: &str) -> Option<&Section> {
32        self.sections.get(key)
33    }
34
35    /// Returns a mutable reference to the section associated with the given key.
36    ///
37    /// If a section exists for the provided key, a mutable reference to that section is returned.
38    /// If no section is associated with the key, `None` is returned.
39    pub fn get_mut(&mut self, key: &str) -> Option<&mut Section> {
40        self.sections.get_mut(key)
41    }
42
43    pub fn fetch(&self, key: &str) -> Result<&Section, IonError> {
44        self.get(key)
45            .ok_or_else(|| IonError::MissingSection(key.to_owned()))
46    }
47
48    pub fn remove(&mut self, key: &str) -> Option<Section> {
49        self.sections.remove(key)
50    }
51
52    pub fn iter(&self) -> impl Iterator<Item = (&String, &Section)> {
53        self.sections.iter()
54    }
55}
56
57impl str::FromStr for Ion {
58    type Err = IonError;
59
60    fn from_str(s: &str) -> Result<Ion, IonError> {
61        parser_to_ion(Parser::new(s))
62    }
63}
64
65fn parser_to_ion(mut parser: Parser) -> Result<Ion, IonError> {
66    match parser.read() {
67        Some(ion) => Ok(Ion::new(ion)),
68        None => Err(IonError::ParserErrors(parser.errors)),
69    }
70}
71
72#[macro_export]
73macro_rules! ion {
74    ($raw:expr) => {{ $raw.parse::<Ion>().expect("Failed parsing to 'Ion'") }};
75}
76
77#[macro_export]
78macro_rules! ion_filtered {
79    ($raw:expr, $accepted_sections:expr) => {
80        Ion::from_str_filtered($raw, $accepted_sections)
81            .expect("Failed parsing by 'from_str_filtered' to 'Ion'")
82    };
83}
84
85#[cfg(test)]
86mod tests {
87    use crate::{Ion, Value};
88
89    #[test]
90    fn as_string() {
91        let v = Value::String("foo".into());
92        assert_eq!(Some(&"foo".into()), v.as_string());
93
94        let v = Value::Integer(1);
95        assert_eq!(None, v.as_string());
96    }
97
98    #[test]
99    fn as_boolean() {
100        let v = Value::Boolean(true);
101        assert_eq!(Some(true), v.as_boolean());
102
103        let v = Value::Integer(1);
104        assert_eq!(None, v.as_boolean());
105    }
106
107    #[test]
108    fn as_integer() {
109        let v = Value::Integer(1);
110        assert_eq!(Some(1), v.as_integer());
111
112        let v = Value::String("foo".into());
113        assert_eq!(None, v.as_integer());
114    }
115
116    #[test]
117    fn as_str() {
118        let v = Value::String("foo".into());
119        assert_eq!(Some("foo"), v.as_str());
120
121        let v = Value::Integer(1);
122        assert_eq!(None, v.as_str());
123    }
124
125    #[test]
126    fn row_without_header() {
127        let ion = ion!(
128            r#"
129            [FOO]
130            |1||2|
131            |1|   |2|
132            |1|2|3|
133        "#
134        );
135
136        let rows = ion.get("FOO").unwrap().rows_without_header();
137        assert!(rows.len() == 3);
138    }
139
140    #[test]
141    fn row_with_header() {
142        let ion = ion!(
143            r#"
144            [FOO]
145            | 1 | 2 | 3 |
146            |---|---|---|
147            |1||2|
148            |1|   |2|
149        "#
150        );
151
152        let rows = ion.get("FOO").unwrap().rows_without_header();
153        assert!(rows.len() == 2);
154    }
155
156    #[test]
157    fn no_rows_with_header() {
158        let ion = ion!(
159            r#"
160            [FOO]
161            | 1 | 2 | 3 |
162            |---|---|---|
163        "#
164        );
165
166        let rows = ion.get("FOO").unwrap().rows_without_header();
167        assert_eq!(0, rows.len());
168    }
169
170    #[test]
171    fn filtered_section() {
172        let ion = ion_filtered!(
173            r#"
174            [FOO]
175            |1||2|
176            |1|   |2|
177            |1|2|3|
178            [BAR]
179            |1||2|
180        "#,
181            vec!["FOO"]
182        );
183
184        let rows = ion.get("FOO").unwrap().rows_without_header();
185        assert_eq!(3, rows.len());
186        assert!(ion.get("BAR").is_none());
187    }
188}