1use std::path::Path;
2use std::env;
3use std::fs::File;
4use std::io::prelude::*;
5
6use pest::Parser;
7use pest::iterators::Pair;
8
9use errors::Error;
10use value::{Date, Dict, Value};
11
12
13#[cfg(debug_assertions)]
16const _GRAMMAR: &str = include_str!("scl.pest");
17
18
19#[derive(Parser)]
20#[grammar = "scl.pest"]
21pub struct SclParser;
22
23
24#[derive(Debug, PartialEq, Default)]
29struct ParserState<'a> {
30 path: Option<&'a Path>,
33}
34
35impl<'a> ParserState<'a> {
36 fn parse_env_var(&self, pair: Pair<Rule>) -> Value {
38 let mut key = None;
39 let mut cast = None;
40 let mut default = None;
41
42 for p in pair.into_inner() {
43 match p.as_rule() {
44 Rule::key => {
45 key = Some(p.into_span().as_str().to_string());
46 },
47 Rule::env_var_cast => {
48 cast = Some(p.into_span().as_str().to_string());
49 },
50 _ => {
51 default = Some(self.parse_value(p));
52 }
53 };
54 }
55
56 if let Some(ref c) = cast {
57 if let Some(ref d) = default {
58 if c != d.type_str() {
59 panic!("TO IMPLEMENT: error on different cast/default type")
60 }
61 }
62 }
63
64 match env::var(&key.unwrap()) {
65 Ok(s) => {
66 if let Some(c) = cast {
67 match c.as_str() {
69 "integer" => Value::Integer(s.parse().unwrap()),
70 "float" => Value::Float(s.parse().unwrap()),
71 "bool" => Value::Boolean(s.parse().unwrap()),
72 "date" => Value::Date(Date::from_str(&c)),
73 _ => unreachable!()
74 }
75 } else {
76 Value::String(s)
77 }
78 },
79 Err(_) => default.unwrap(),
80 }
81 }
82
83 fn parse_array(&self, pair: Pair<Rule>) -> Value {
85 let mut items = vec![];
86
87 for p in pair.into_inner() {
88 let val = self.parse_value(p.into_inner().next().unwrap());
90 if let Some(last) = items.last() {
91 if !val.same_type(last) {
92 }
94 }
95 items.push(val);
96 }
97
98 Value::Array(items)
99 }
100
101 fn parse_byte_size(&self, pair: Pair<Rule>) -> Value {
102 let mut num: Option<f64> = None;
103
104 for p in pair.into_inner() {
105 match p.as_rule() {
106 Rule::byte_size_number => {
107 num = Some(p.as_str().parse().unwrap());
108 }
109 Rule::byte_size_unit => {
110 let n = num.unwrap();
111 let res = match p.as_str() {
112 "kB" | "KB" => n * 1e3,
113 "MB" => n * 1e6,
114 "GB" => n * 1e9,
115 "TB" => n * 1e12,
116 "PB" => n * 1e15,
117 _ => unreachable!(),
118 };
119
120 return Value::Integer(res as i64);
121 }
122 _ => unreachable!(),
123 }
124 }
125
126 unreachable!("Got a byte size without a unit?")
127 }
128
129 fn parse_value(&self, pair: Pair<Rule>) -> Value {
130 match pair.as_rule() {
131 Rule::int => Value::Integer(pair.as_str().parse().unwrap()),
132 Rule::float => Value::Float(pair.as_str().parse().unwrap()),
133 Rule::byte_size => self.parse_byte_size(pair),
134 Rule::boolean => match pair.as_str() {
135 "true" => Value::Boolean(true),
136 "false" => Value::Boolean(false),
137 _ => unreachable!(),
138 },
139 Rule::string => Value::String(pair.as_str().replace("\"", "").to_string()),
140 Rule::multiline_string => {
141 let text = pair.as_str().replace("\"\"\"", "");
142 if text.starts_with('\n') {
143 Value::String(text.trim_left().to_string())
144 } else {
145 Value::String(text.to_string())
146 }
147 }
148 Rule::env_var => self.parse_env_var(pair),
149 Rule::date => Value::Date(Date::from_str(pair.as_str())),
150 Rule::array => self.parse_array(pair),
151 Rule::dict => Value::Dict(self.parse_dict(pair).unwrap()), _ => unreachable!("Got an unexpected value: {:?}", pair),
153 }
154 }
155
156 fn parse_key_value(&self, pair: Pair<Rule>) -> (String, Value) {
157 let mut key = None;
158 let mut value = None;
159
160 for p in pair.into_inner() {
161 match p.as_rule() {
162 Rule::key => {
163 key = Some(p.into_span().as_str().to_string());
164 }
165 Rule::value => {
167 value = Some(self.parse_value(p.into_inner().next().unwrap()));
168 }
169 Rule::include => {
170 value = Some(Value::Dict(self.parse_include(p).unwrap())); }
172 _ => unreachable!("Got something in key/value other than a key/value: {:?}", p),
173 };
174 }
175
176 (key.unwrap(), value.unwrap())
177 }
178
179 fn parse_dict(&self, pair: Pair<Rule>) -> Result<Dict, Error> {
180 let mut dict = Dict::new();
181
182 for p in pair.into_inner() {
183 match p.as_rule() {
184 Rule::include => {
185 let included = self.parse_include(p)?;
186 dict.extend(included);
187 }
188 Rule::key_value => {
189 let (key, value) = self.parse_key_value(p);
190 dict.insert(key, value);
191 }
192 _ => unreachable!("unknown dict rule: {:?}", p.as_rule()),
193 }
194 }
195
196 Ok(dict)
197 }
198
199 fn parse_include(&self, pair: Pair<Rule>) -> Result<Dict, Error> {
200 let path = pair.into_inner()
202 .next()
203 .unwrap()
204 .into_span()
205 .as_str()
206 .replace("\"", "");
207
208 if let Some(current_path) = self.path {
214 if path.starts_with('/') {
216 parse_file(path)
217 } else {
218 let full_path = current_path.parent().unwrap().join(path);
220 parse_file(&full_path)
221 }
222 } else {
223 parse_file(path)
224 }
225 }
226
227 pub fn parse_str(&self, input: &str) -> Result<Dict, Error> {
229 let mut pairs = match SclParser::parse(Rule::document, input) {
230 Ok(p) => p,
231 Err(e) => {
232 let fancy_e = e.renamed_rules(|rule| {
233 match *rule {
234 Rule::document => "a key value, an include or a comment".to_string(),
235 Rule::key => "a key".to_string(),
236 Rule::boolean => "a boolean (true / false)".to_string(),
237 Rule::string => "a string".to_string(),
238 Rule::multiline_string => "a multiline string".to_string(),
239 Rule::int => "an integer".to_string(),
240 Rule::float => "a float".to_string(),
241 Rule::date => "a date".to_string(),
242 Rule::key_value => "a key value".to_string(),
243 Rule::byte_size_unit => "a byte size unit (kB / MB / GB / TB / PB)".to_string(),
244 Rule::value => "string / int / float / byte size / date / bool / array / dict / environment variable".to_string(),
245 Rule::include => "include".to_string(),
246 Rule::byte_size_number => "a number".to_string(),
247 Rule::env_var => "an environment variable".to_string(),
248 Rule::env_var_cast => "a cast to integer/float/date/bool".to_string(),
249 Rule::array => "an array".to_string(),
250 Rule::dict => "a dictionary".to_string(),
251 _ => format!("TODO: {:?}", rule),
252 }
253 });
254 return Err(Error::InvalidSyntax(format!("{}", fancy_e)));
255 }
256 };
257
258 self.parse_dict(pairs.next().unwrap())
260 }
261
262}
263
264pub fn parse_file<T: AsRef<Path>>(path: T) -> Result<Dict, Error> {
266 let mut f = File::open(&path).expect("file not found");
267 let mut contents = String::new();
268 f.read_to_string(&mut contents).expect("something went wrong reading the file");
270
271 let state = ParserState { path: Some(&path.as_ref()) };
272
273 state.parse_str(&contents)
274}
275
276pub fn parse_str(input: &str) -> Result<Dict, Error> {
278 let state = ParserState { path: None };
279
280 state.parse_str(&input)
281}