jss_core/from/from_text/
mod.rs

1use std::{mem::take, str::FromStr};
2
3use indexmap::IndexMap;
4
5use json_value::Number;
6use jss_pest::{JssParser, Pair, Parser, Rule};
7
8use crate::{JssError, JssSchema, JssValue, Result};
9
10impl FromStr for JssSchema {
11    type Err = JssError;
12    fn from_str(s: &str) -> Result<Self> {
13        let mut ctx = ParseContext::default();
14        ctx.parse(s)
15    }
16}
17
18macro_rules! debug_cases {
19    ($i:ident) => {{
20        println!("Rule::{:?}=>continue,", $i.as_rule());
21        println!("Span: {:?}", $i.as_span());
22        println!("Text: {}", $i.as_str());
23        unreachable!();
24    }};
25}
26
27struct ParseContext {
28    errors: Vec<JssError>,
29    documents: String,
30}
31
32impl Default for ParseContext {
33    fn default() -> Self {
34        Self { errors: vec![], documents: "".to_string() }
35    }
36}
37
38impl ParseContext {
39    pub fn parse(&mut self, input: &str) -> Result<JssSchema> {
40        let parsed = JssParser::parse(Rule::program, input)?;
41        let mut node = JssSchema::top();
42        for pair in parsed {
43            match pair.as_rule() {
44                Rule::EOI => continue,
45                Rule::schema_statement => {
46                    node.set_description(take(&mut self.documents));
47                    self.parse_schema_statement(pair, &mut node)?
48                }
49                Rule::property_statement => {
50                    let mut property = self.parse_property_statement(pair)?;
51                    property.set_description(take(&mut self.documents));
52                    let name = property.get_name().to_string();
53                    node.insert_property(name, property);
54                }
55                Rule::define_statement => {
56                    let mut define = self.parse_define_statement(pair)?;
57                    define.set_description(take(&mut self.documents));
58                    let name = define.get_name().to_string();
59                    node.insert_definition(name, define);
60                }
61                Rule::DOCUMENTATION => self.push_comment(pair),
62                _ => debug_cases!(pair),
63            }
64        }
65        Ok(node)
66    }
67
68    pub fn parse_schema_statement(&mut self, pairs: Pair<Rule>, node: &mut JssSchema) -> Result<()> {
69        for pair in pairs.into_inner() {
70            match pair.as_rule() {
71                Rule::SYMBOL => node.set_name(pair.as_str()),
72                Rule::block => self.parse_block(pair, node)?,
73                _ => debug_cases!(pair),
74            }
75        }
76        Ok(())
77    }
78    pub fn parse_define_statement(&mut self, pairs: Pair<Rule>) -> Result<JssSchema> {
79        let mut out = JssSchema::definition();
80        for pair in pairs.into_inner() {
81            match pair.as_rule() {
82                Rule::SYMBOL => out.set_name(pair.as_str()),
83                Rule::block => self.parse_block(pair, &mut out)?,
84                _ => debug_cases!(pair),
85            }
86        }
87        Ok(out)
88    }
89    pub fn parse_property_statement(&mut self, pairs: Pair<Rule>) -> Result<JssSchema> {
90        let mut out = JssSchema::property();
91        for pair in pairs.into_inner() {
92            match pair.as_rule() {
93                Rule::SYMBOL => out.set_name(pair.as_str()),
94                Rule::block => self.parse_block(pair, &mut out)?,
95                _ => debug_cases!(pair),
96            }
97        }
98        Ok(out)
99    }
100    pub fn parse_object_statement(&mut self, pairs: Pair<Rule>, node: &mut JssSchema) -> Result<()> {
101        for pair in pairs.into_inner() {
102            match pair.as_rule() {
103                Rule::attitude_statement => {
104                    let mut inner = pair.into_inner();
105                    let key = self.parse_key(inner.next().unwrap())?;
106                    let value = self.parse_data(inner.next().unwrap())?;
107                    node.insert_attribute(key, value);
108                }
109                Rule::property_statement => {
110                    let property = self.parse_property_statement(pair)?;
111                    let name = property.get_name().to_string();
112                    node.insert_property(name, property);
113                }
114                _ => debug_cases!(pair),
115            }
116        }
117        Ok(())
118    }
119    pub fn parse_object_simple(&mut self, pairs: Pair<Rule>) -> Result<JssValue> {
120        let mut out = IndexMap::default();
121        for pair in pairs.into_inner() {
122            match pair.as_rule() {
123                Rule::attitude_statement => {
124                    let mut inner = pair.into_inner();
125                    let key = self.parse_key(inner.next().unwrap())?;
126                    let value = self.parse_data(inner.next().unwrap())?;
127                    out.insert(key, value);
128                }
129                _ => debug_cases!(pair),
130            }
131        }
132        Ok(JssValue::Object(out))
133    }
134    pub fn push_comment(&mut self, pair: Pair<Rule>) {
135        if !self.documents.is_empty() {
136            self.documents.push('\n');
137        }
138        let comment = pair.as_str().trim_start_matches("///").trim();
139        self.documents.push_str(comment);
140    }
141}
142
143impl ParseContext {
144    pub fn parse_block(&mut self, pairs: Pair<Rule>, node: &mut JssSchema) -> Result<()> {
145        for pair in pairs.into_inner() {
146            match pair.as_rule() {
147                Rule::key => node.set_type(self.parse_key(pair)?),
148                Rule::object => self.parse_object_statement(pair, node)?,
149                _ => debug_cases!(pair),
150            }
151        }
152        Ok(())
153    }
154    pub fn parse_key(&mut self, pairs: Pair<Rule>) -> Result<String> {
155        let pair = pairs.into_inner().next().unwrap();
156        match pair.as_rule() {
157            Rule::SYMBOL => return Ok(pair.as_str().to_string()),
158            Rule::STRING_INLINE => return self.parse_string_inline(pair),
159            _ => debug_cases!(pair),
160        }
161    }
162    pub fn parse_data(&mut self, pairs: Pair<Rule>) -> Result<JssValue> {
163        let pair = pairs.into_inner().next().unwrap();
164        let value = match pair.as_rule() {
165            Rule::URL => JssValue::Url(pair.as_str().to_string()),
166            Rule::array => self.parse_array(pair)?,
167            Rule::STRING_INLINE => JssValue::String(self.parse_string_inline(pair)?),
168            Rule::Float => {
169                let n = match f64::from_str(pair.as_str()) {
170                    Ok(o) => Number::from_f64(o),
171                    Err(e) => {
172                        self.errors.push(JssError::from(e));
173                        Number::from_f64(0.0)
174                    }
175                };
176                JssValue::Number(n.unwrap_or(Number::from(0)))
177            }
178            Rule::Special => match pair.as_str() {
179                "true" => JssValue::Boolean(true),
180                "false" => JssValue::Boolean(false),
181                "null" => JssValue::Null,
182                _ => unreachable!(),
183            },
184            Rule::object => self.parse_object_simple(pair)?,
185            // Rule::Number => JssValue::Number(Number::from()),
186            _ => debug_cases!(pair),
187        };
188        Ok(value)
189    }
190    pub fn parse_array(&mut self, pairs: Pair<Rule>) -> Result<JssValue> {
191        let mut out = vec![];
192        for pair in pairs.into_inner() {
193            match pair.as_rule() {
194                Rule::data => out.push(self.parse_data(pair)?),
195                _ => debug_cases!(pair),
196            }
197        }
198        return Ok(JssValue::Array(out));
199    }
200    pub fn parse_string_inline(&mut self, pairs: Pair<Rule>) -> Result<String> {
201        let mut out = String::with_capacity(pairs.as_str().len());
202        for pair in pairs.into_inner() {
203            match pair.as_rule() {
204                Rule::NS2 => out.push_str(pair.as_str()),
205                _ => debug_cases!(pair),
206            }
207        }
208        return Ok(out);
209    }
210}