dyon/dyon_std/
data.rs

1use std::collections::HashSet;
2#[cfg(feature = "file")]
3use std::fs::File;
4#[cfg(feature = "file")]
5use std::io::Read;
6use std::sync::Arc;
7
8use range::Range;
9use read_token::{NumberSettings, ReadToken};
10
11#[cfg(feature = "file")]
12use super::io::io_error;
13
14use crate::Variable;
15
16type Strings = HashSet<Arc<String>>;
17
18/// Loads data from a file.
19#[cfg(feature = "file")]
20pub fn load_file(file: &str) -> Result<Variable, String> {
21    let mut data_file = File::open(file).map_err(|err| io_error("open", file, &err))?;
22    let mut d = String::new();
23    data_file
24        .read_to_string(&mut d)
25        .map_err(|err| io_error("read", file, &err))?;
26    load_data(&d)
27}
28
29#[cfg(not(feature = "file"))]
30pub fn load_file(_: &str) -> Result<Variable, String> {
31    Err(super::FILE_SUPPORT_DISABLED.into())
32}
33
34/// Loads data from text.
35pub fn load_data(data: &str) -> Result<Variable, String> {
36    let mut read = ReadToken::new(data, 0);
37    let mut strings: Strings = HashSet::new();
38    opt_w(&mut read);
39    expr(&mut read, &mut strings, data)
40}
41
42static NUMBER_SETTINGS: NumberSettings = NumberSettings {
43    allow_underscore: true,
44};
45
46const SEPS: &str = "(){}[],.:;\n\"\\";
47
48fn expr(read: &mut ReadToken, strings: &mut Strings, data: &str) -> Result<Variable, String> {
49    if let Some(range) = read.tag("{") {
50        // Object.
51        *read = read.consume(range.length);
52        return object(read, strings, data);
53    }
54    if let Some(range) = read.tag("[") {
55        // Array.
56        *read = read.consume(range.length);
57        return array(read, strings, data);
58    }
59    if let Some(range) = read.tag("(") {
60        // Vec4.
61        *read = read.consume(range.length);
62        return vec4(read, data);
63    }
64    if let Some(range) = read.tag("#") {
65        use read_color::rgb_maybe_a;
66
67        // Color.
68        *read = read.consume(range.length);
69        let (range, _) = read.until_any_or_whitespace(SEPS);
70        let val = read.raw_string(range.length);
71        if let Some((rgb, a)) = rgb_maybe_a(&mut val.chars()) {
72            let v = [
73                f32::from(rgb[0]) / 255.0,
74                f32::from(rgb[1]) / 255.0,
75                f32::from(rgb[2]) / 255.0,
76                f32::from(a.unwrap_or(255)) / 255.0,
77            ];
78            return Ok(Variable::Vec4(v));
79        } else {
80            return Err(error(
81                range,
82                "Expected hex color in format `FFFFFF`or `FFFFFFFF`",
83                data,
84            ));
85        }
86    }
87    if let Some(range) = read.tag("link") {
88        // Link.
89        *read = read.consume(range.length);
90        return link(read, strings, data);
91    }
92    // Text.
93    if let Some(range) = read.string() {
94        match read.parse_string(range.length) {
95            Ok(s) => {
96                *read = read.consume(range.length);
97                return Ok(Variable::Str(if let Some(s) = strings.get(&s) {
98                    s.clone()
99                } else {
100                    Arc::new(s)
101                }));
102            }
103            Err(err_range) => {
104                let (range, err) = err_range.decouple();
105                return Err(error(range, &format!("{}", err), data));
106            }
107        }
108    }
109    // Number.
110    if let Some(range) = read.number(&NUMBER_SETTINGS) {
111        match read.parse_number(&NUMBER_SETTINGS, range.length) {
112            Ok(val) => {
113                *read = read.consume(range.length);
114                return Ok(Variable::f64(val));
115            }
116            Err(err) => return Err(error(range, &format!("{}", err), data)),
117        }
118    }
119    // Boolean.
120    if let Some(range) = read.tag("false") {
121        *read = read.consume(range.length);
122        return Ok(Variable::bool(false));
123    }
124    if let Some(range) = read.tag("true") {
125        *read = read.consume(range.length);
126        return Ok(Variable::bool(true));
127    }
128    // Option.
129    if let Some(range) = read.tag("none()") {
130        *read = read.consume(range.length);
131        return Ok(Variable::Option(None));
132    }
133    if let Some(range) = read.tag("some(") {
134        *read = read.consume(range.length);
135        opt_w(read);
136        let res = expr(read, strings, data)?;
137        opt_w(read);
138        return if let Some(range) = read.tag(")") {
139            *read = read.consume(range.length);
140            Ok(Variable::Option(Some(Box::new(res))))
141        } else {
142            Err(error(read.start(), "Expected `)`", data))
143        };
144    }
145    Err(error(read.start(), "Reached end of file", data))
146}
147
148fn object(read: &mut ReadToken, strings: &mut Strings, data: &str) -> Result<Variable, String> {
149    use std::collections::HashMap;
150
151    let mut res: HashMap<Arc<String>, Variable> = HashMap::new();
152    let mut was_comma = false;
153    loop {
154        opt_w(read);
155
156        if let Some(range) = read.tag("}") {
157            *read = read.consume(range.length);
158            break;
159        }
160
161        if !res.is_empty() && !was_comma {
162            return Err(error(read.start(), "Expected `,`", data));
163        }
164
165        let key: Arc<String>;
166        if let Some(range) = read.string() {
167            match read.parse_string(range.length) {
168                Ok(s) => {
169                    // Use reference to existing string to reduce memory.
170                    key = if let Some(s) = strings.get(&s) {
171                        s.clone()
172                    } else {
173                        Arc::new(s)
174                    };
175                    *read = read.consume(range.length);
176                }
177                Err(err_range) => {
178                    let (range, err) = err_range.decouple();
179                    return Err(error(range, &format!("{}", err), data));
180                }
181            }
182        } else {
183            let (range, _) = read.until_any_or_whitespace(SEPS);
184            if range.length == 0 {
185                return Err(error(range, "Expected key", data));
186            } else {
187                let k = read.raw_string(range.length);
188                // Use reference to existing string to reduce memory.
189                key = if let Some(s) = strings.get(&k) {
190                    s.clone()
191                } else {
192                    Arc::new(k)
193                };
194                *read = read.consume(range.length);
195            };
196        }
197
198        opt_w(read);
199
200        if let Some(range) = read.tag(":") {
201            *read = read.consume(range.length);
202        } else {
203            return Err(error(read.start(), "Expected `:`", data));
204        }
205
206        opt_w(read);
207
208        res.insert(key, expr(read, strings, data)?);
209
210        was_comma = comma(read);
211    }
212    Ok(Variable::Object(Arc::new(res)))
213}
214
215fn array(read: &mut ReadToken, strings: &mut Strings, data: &str) -> Result<Variable, String> {
216    let mut res = vec![];
217    let mut was_comma = false;
218    loop {
219        opt_w(read);
220
221        if let Some(range) = read.tag("]") {
222            *read = read.consume(range.length);
223            break;
224        }
225
226        if !res.is_empty() && !was_comma {
227            return Err(error(read.start(), "Expected `,`", data));
228        }
229
230        res.push(expr(read, strings, data)?);
231        was_comma = comma(read);
232    }
233    Ok(Variable::Array(Arc::new(res)))
234}
235
236fn link(read: &mut ReadToken, strings: &mut Strings, data: &str) -> Result<Variable, String> {
237    use crate::Link;
238
239    opt_w(read);
240
241    if let Some(range) = read.tag("{") {
242        *read = read.consume(range.length);
243    } else {
244        return Err(error(read.start(), "Expected `{`", data));
245    }
246
247    let mut link = Link::new();
248
249    opt_w(read);
250
251    loop {
252        opt_w(read);
253
254        if let Some(range) = read.tag("}") {
255            *read = read.consume(range.length);
256            break;
257        }
258
259        match link.push(&expr(read, strings, data)?) {
260            Ok(()) => {}
261            Err(err) => return Err(err),
262        };
263    }
264    Ok(Variable::Link(Box::new(link)))
265}
266
267fn vec4(read: &mut ReadToken, data: &str) -> Result<Variable, String> {
268    let x = if let Some(range) = read.number(&NUMBER_SETTINGS) {
269        match read.parse_number(&NUMBER_SETTINGS, range.length) {
270            Ok(x) => {
271                *read = read.consume(range.length);
272                x
273            }
274            Err(err) => return Err(error(range, &format!("{}", err), data)),
275        }
276    } else {
277        return Err(error(read.start(), "Expected x component", data));
278    };
279    comma(read);
280    let y = if let Some(range) = read.number(&NUMBER_SETTINGS) {
281        match read.parse_number(&NUMBER_SETTINGS, range.length) {
282            Ok(y) => {
283                *read = read.consume(range.length);
284                y
285            }
286            Err(err) => return Err(error(range, &format!("{}", err), data)),
287        }
288    } else {
289        return Err(error(read.start(), "Expected y component", data));
290    };
291    let (z, w) = if comma(read) {
292        if let Some(range) = read.number(&NUMBER_SETTINGS) {
293            match read.parse_number(&NUMBER_SETTINGS, range.length) {
294                Ok(z) => {
295                    *read = read.consume(range.length);
296                    comma(read);
297                    if let Some(range) = read.number(&NUMBER_SETTINGS) {
298                        match read.parse_number(&NUMBER_SETTINGS, range.length) {
299                            Ok(w) => {
300                                *read = read.consume(range.length);
301                                (z, w)
302                            }
303                            Err(err) => return Err(error(range, &format!("{}", err), data)),
304                        }
305                    } else {
306                        (z, 0.0)
307                    }
308                }
309                Err(err) => return Err(error(range, &format!("{}", err), data)),
310            }
311        } else {
312            (0.0, 0.0)
313        }
314    } else {
315        (0.0, 0.0)
316    };
317    opt_w(read);
318    if let Some(range) = read.tag(")") {
319        *read = read.consume(range.length);
320    } else {
321        return Err(error(read.start(), "Expected `)`", data));
322    }
323    Ok(Variable::Vec4([x as f32, y as f32, z as f32, w as f32]))
324}
325
326/// Reads optional whitespace including comments.
327fn opt_w(read: &mut ReadToken) {
328    loop {
329        let start = *read;
330        let range = read.whitespace();
331        *read = read.consume(range.length);
332
333        // Single line comment.
334        if let Some(range) = read.tag("//") {
335            *read = read.consume(range.length);
336            let (range, _) = read.until_any("\n");
337            *read = read.consume(range.length);
338        }
339
340        multi_line_comment(read);
341
342        if read.subtract(&start).length == 0 {
343            break;
344        }
345    }
346}
347
348fn multi_line_comment(read: &mut ReadToken) {
349    // Multi-line comment.
350    if let Some(range) = read.tag("/*") {
351        *read = read.consume(range.length);
352        let (range, _) = read.until_any("*/");
353        *read = read.consume(range.length);
354        loop {
355            let start = *read;
356
357            if read.tag("*/").is_none() {
358                if let Some(range) = read.tag("*") {
359                    *read = read.consume(range.length);
360                    let (range, _) = read.until_any("*/");
361                    *read = read.consume(range.length);
362                }
363            }
364
365            let start_multi_line = *read;
366            multi_line_comment(read);
367            if read.subtract(&start_multi_line).length > 0 {
368                let (range, _) = read.until_any("*/");
369                *read = read.consume(range.length);
370            } else {
371                *read = start_multi_line;
372                if let Some(range) = read.tag("/") {
373                    *read = read.consume(range.length);
374                    let (range, _) = read.until_any("*/");
375                    *read = read.consume(range.length);
376                }
377            }
378
379            if read.subtract(&start).length == 0 {
380                break;
381            }
382        }
383
384        if let Some(range) = read.tag("*/") {
385            *read = read.consume(range.length);
386        }
387    }
388}
389
390/// Reads comma.
391fn comma(read: &mut ReadToken) -> bool {
392    let mut res = false;
393    opt_w(read);
394    if let Some(range) = read.tag(",") {
395        *read = read.consume(range.length);
396        res = true;
397    }
398    opt_w(read);
399    res
400}
401
402/// Generates error message using Piston-Meta's error handler.
403fn error(range: Range, msg: &str, data: &str) -> String {
404    use piston_meta::ParseErrorHandler;
405
406    let mut handler = ParseErrorHandler::new(data);
407    let mut buf: Vec<u8> = vec![];
408    handler.write_msg(&mut buf, range, msg).unwrap();
409    String::from_utf8(buf).unwrap()
410}