dyon/dyon_std/
meta.rs

1#[cfg(feature = "file")]
2use super::io::io_error;
3use piston_meta::{parse_errstr, MetaData, Syntax};
4#[cfg(feature = "file")]
5use piston_meta::syntax_errstr;
6#[cfg(feature = "file")]
7use std::fs::File;
8use std::io;
9#[cfg(feature = "file")]
10use std::io::Read;
11use std::sync::Arc;
12
13use crate::Variable;
14
15pub fn parse_syntax_data(rules: &Syntax, file: &str, d: &str) -> Result<Vec<Variable>, String> {
16    let mut tokens = vec![];
17    parse_errstr(rules, d, &mut tokens)
18        .map_err(|err| format!("When parsing data in `{}`:\n{}", file, err))?;
19    let mut res = vec![];
20    let b: Arc<String> = Arc::new("bool".into());
21    let s: Arc<String> = Arc::new("str".into());
22    let n: Arc<String> = Arc::new("f64".into());
23    let start: Arc<String> = Arc::new("start".into());
24    let end: Arc<String> = Arc::new("end".into());
25    for range_token in &tokens {
26        let mut data = vec![
27            Variable::f64(range_token.offset as f64),
28            Variable::f64(range_token.length as f64),
29        ];
30        match range_token.data {
31            MetaData::Bool(ref name, val) => {
32                data.push(Variable::Str(b.clone()));
33                data.push(Variable::Str(name.clone()));
34                data.push(Variable::bool(val));
35            }
36            MetaData::String(ref name, ref val) => {
37                data.push(Variable::Str(s.clone()));
38                data.push(Variable::Str(name.clone()));
39                data.push(Variable::Str(val.clone()));
40            }
41            MetaData::F64(ref name, val) => {
42                data.push(Variable::Str(n.clone()));
43                data.push(Variable::Str(name.clone()));
44                data.push(Variable::f64(val));
45            }
46            MetaData::StartNode(ref name) => {
47                data.push(Variable::Str(start.clone()));
48                data.push(Variable::Str(name.clone()));
49            }
50            MetaData::EndNode(ref name) => {
51                data.push(Variable::Str(end.clone()));
52                data.push(Variable::Str(name.clone()));
53            }
54        }
55        res.push(Variable::Array(Arc::new(data)));
56    }
57    Ok(res)
58}
59
60#[cfg(feature = "file")]
61fn load_metarules_data(meta: &str, s: &str, file: &str, d: &str) -> Result<Vec<Variable>, String> {
62    let rules = syntax_errstr(s)
63        .map_err(|err| format!("When parsing meta syntax in `{}`:\n{}", meta, err))?;
64    parse_syntax_data(&rules, file, d)
65}
66
67/// Loads a file using a meta file as syntax.
68#[cfg(feature = "file")]
69pub fn load_meta_file(meta: &str, file: &str) -> Result<Vec<Variable>, String> {
70    let mut syntax_file = File::open(meta).map_err(|err| io_error("open", meta, &err))?;
71    let mut s = String::new();
72    syntax_file
73        .read_to_string(&mut s)
74        .map_err(|err| io_error("read", meta, &err))?;
75    let mut data_file = File::open(file).map_err(|err| io_error("open", file, &err))?;
76    let mut d = String::new();
77    data_file
78        .read_to_string(&mut d)
79        .map_err(|err| io_error("read", file, &err))?;
80    load_metarules_data(meta, &s, file, &d)
81}
82
83#[cfg(not(feature = "file"))]
84pub fn load_meta_file(_: &str, _: &str) -> Result<Vec<Variable>, String> {
85    Err(super::FILE_SUPPORT_DISABLED.into())
86}
87
88/// Loads a text file from url.
89#[cfg(all(not(target_family = "wasm"), feature = "http"))]
90pub fn load_text_file_from_url(url: &str) -> Result<String, String> {
91    use reqwest::{Client, StatusCode, Url};
92
93    let url_address = Url::parse(url).map_err(|e| format!("Error parsing url:\n`{}`\n", e))?;
94    let client = Client::new();
95    let request = client.get(url_address);
96    let mut response = request.send().map_err(|e| {
97        format!(
98            "Error fetching file over http `{}`:\n{}\n",
99            url,
100            e.to_string()
101        )
102    })?;
103    if response.status() == StatusCode::OK {
104        let mut data = String::new();
105        response.read_to_string(&mut data).map_err(|e| {
106            format!(
107                "Error fetching file over http `{}`:\n{}\n",
108                url,
109                e.to_string()
110            )
111        })?;
112        Ok(data)
113    } else {
114        Err(format!(
115            "Error fetching file over http `{}:\n{}\n",
116            url,
117            response.status()
118        ))
119    }
120}
121
122#[cfg(not(all(not(target_family = "wasm"), feature = "http")))]
123pub fn load_text_file_from_url(_url: &str) -> Result<String, String> {
124    Err(super::HTTP_SUPPORT_DISABLED.into())
125}
126
127/// Loads an url using a meta file as syntax.
128#[cfg(all(not(target_family = "wasm"), feature = "http"))]
129pub fn load_meta_url(meta: &str, url: &str) -> Result<Vec<Variable>, String> {
130    let mut syntax_file = File::open(meta).map_err(|err| io_error("open", meta, &err))?;
131    let mut s = String::new();
132    syntax_file
133        .read_to_string(&mut s)
134        .map_err(|err| io_error("read", meta, &err))?;
135    let d = load_text_file_from_url(url)?;
136    load_metarules_data(meta, &s, url, &d)
137}
138
139#[cfg(not(all(not(target_family = "wasm"), feature = "http")))]
140pub fn load_meta_url(_meta: &str, _url: &str) -> Result<Vec<Variable>, String> {
141    Err(super::HTTP_SUPPORT_DISABLED.into())
142}
143
144// Downloads a file from url.
145#[cfg(all(not(target_family = "wasm"), feature = "http"))]
146pub fn download_url_to_file(url: &str, file: &str) -> Result<String, String> {
147    use reqwest::{Client, StatusCode, Url};
148    use std::io::copy;
149
150    let url_address = Url::parse(url).map_err(|e| format!("Error parsing url:\n`{}`\n", e))?;
151    let client = Client::new();
152    let request = client.get(url_address);
153    let mut response = request.send().map_err(|e| {
154        format!(
155            "Error fetching file over http `{}`:\n{}\n",
156            url,
157            e.to_string()
158        )
159    })?;
160    if response.status() == StatusCode::OK {
161        let mut f = File::create(file)
162            .map_err(|err| format!("Could not create file `{}`:\n{}", file, err.to_string()))?;
163        copy(&mut response, &mut f).map_err(|e| {
164            format!(
165                "Error fetching file over http `{}`:\n{}\n",
166                url,
167                e.to_string()
168            )
169        })?;
170        Ok(file.into())
171    } else {
172        Err(format!(
173            "Error fetching file over http `{}:\n{}\n",
174            url,
175            response.status()
176        ))
177    }
178}
179
180#[cfg(not(all(not(target_family = "wasm"), feature = "http")))]
181pub fn download_url_to_file(_url: &str, _file: &str) -> Result<String, String> {
182    Err(super::HTTP_SUPPORT_DISABLED.into())
183}
184
185pub fn json_from_meta_data(data: &[Variable]) -> Result<String, io::Error> {
186    fn is_start_node(v: &Variable) -> bool {
187        if let Variable::Array(ref arr) = *v {
188            if let Variable::Str(ref t) = arr[2] {
189                &**t == "start"
190            } else {
191                false
192            }
193        } else {
194            false
195        }
196    }
197
198    fn is_end_node(v: &Variable) -> bool {
199        if let Variable::Array(ref arr) = *v {
200            if let Variable::Str(ref t) = arr[2] {
201                &**t == "end"
202            } else {
203                false
204            }
205        } else {
206            false
207        }
208    }
209
210    use piston_meta::json::write_string;
211    use std::cmp::{max, min};
212    use std::io::Write;
213
214    let indent_offset = 0;
215    let mut w: Vec<u8> = vec![];
216
217    // Start indention such that it balances off to zero.
218    let starts = data.iter().filter(|x| is_start_node(x)).count() as u32;
219    let ends = data.iter().filter(|x| is_end_node(x)).count() as u32;
220    let mut indent: u32 = max(starts, ends) - min(starts, ends);
221    let mut first = true;
222    for (i, d) in data.iter().enumerate() {
223        let is_end = if is_end_node(d) {
224            indent -= 1;
225            true
226        } else {
227            false
228        };
229        let print_comma = !first && !is_end;
230        if print_comma {
231            writeln!(w, ",")?;
232        } else if i != 0 {
233            writeln!(w)?;
234        }
235        first = false;
236        for _ in 0..indent_offset + indent {
237            write!(w, " ")?;
238        }
239        if let Variable::Array(ref arr) = *d {
240            let name = if let Variable::Str(ref t) = arr[3] {
241                t
242            } else {
243                ""
244            };
245            if let Variable::Str(ref t) = arr[2] {
246                match &***t {
247                    "start" => {
248                        first = true;
249                        write_string(&mut w, name)?;
250                        write!(w, ":{}", "{")?;
251                        indent += 1;
252                    }
253                    "end" => {
254                        write!(w, "{}", "}")?;
255                    }
256                    "bool" => {
257                        if let Variable::Bool(val, _) = arr[4] {
258                            write_string(&mut w, name)?;
259                            write!(w, ":{}", val)?;
260                        }
261                    }
262                    "f64" => {
263                        if let Variable::F64(val, _) = arr[4] {
264                            write_string(&mut w, name)?;
265                            write!(w, ":{}", val)?;
266                        }
267                    }
268                    "str" => {
269                        if let Variable::Str(ref val) = arr[4] {
270                            write_string(&mut w, name)?;
271                            write!(w, ":")?;
272                            write_string(&mut w, val)?;
273                        }
274                    }
275                    _ => {}
276                }
277            }
278        }
279    }
280    writeln!(w)?;
281    Ok(String::from_utf8(w).unwrap())
282}