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 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}