jss_core/from/from_text/
mod.rs1use 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 _ => 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}