1use std::{io::Write, num::ParseIntError, path::Path};
2
3use once_cell::sync::Lazy;
4use pest::{
5 iterators::{Pair, Pairs},
6 Parser, Span,
7};
8use pest_derive::Parser;
9use regex::Regex;
10use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
11
12use crate::ast::{
13 Attribute, DataType, Dependency, Enum, EnumValue, Event, Function, Import, Namespace,
14 OrderedMap, Service, SsdModule, TypeName,
15};
16
17use crate::ast::{AstElement, ServiceAstElement};
18
19fn parse_attribute_arg(node: Pair<Rule>) -> Result<(String, Option<String>), ParseError> {
20 let span = node.as_span();
21 let mut p = node.into_inner();
22 let name = p
23 .next()
24 .ok_or_else(|| ParseError::new(ParseErrorType::IncompleteAttributeArg, span))?
25 .as_str()
26 .to_string();
27 let value = p.next().map(|p| p.into_inner().as_str().to_string());
28 Ok((name, value))
29}
30
31fn parse_attribute(node: Pair<Rule>) -> Result<Attribute, ParseError> {
32 let span = node.as_span();
33 let mut p = node.into_inner();
34 let name = p.next();
35 let mut args = Vec::new();
36 for p in p {
37 args.push(parse_attribute_arg(p)?);
38 }
39 Ok(Attribute::new(
40 Namespace::new(
41 name.ok_or_else(|| ParseError::new(ParseErrorType::IncompleteAttribute, span))?
42 .as_str(),
43 ),
44 args,
45 ))
46}
47
48fn parse_attributes(node: Pair<Rule>) -> Result<Vec<Attribute>, ParseError> {
49 node.into_inner().map(parse_attribute).collect()
50}
51
52fn parse_name(p: &mut Pairs<Rule>, n: Pair<Rule>) -> Result<(String, Vec<Attribute>), ParseError> {
53 let span = n.as_span();
54 if n.as_rule() == Rule::attributes {
55 let attributes = parse_attributes(n)?;
56 let name = p
57 .next()
58 .ok_or_else(|| ParseError::new(ParseErrorType::IncompleteName, span))?
59 .as_str()
60 .to_string();
61 Ok((name, attributes))
62 } else {
63 let name = n.as_str().to_string();
64 Ok((name, Vec::new()))
65 }
66}
67
68#[derive(Parser)]
69#[grammar = "grammar.pest"]
70pub(crate) struct FileParser;
71
72#[derive(Debug)]
73pub struct ParseError {
74 pub error_type: ParseErrorType,
75 pub span: String,
76}
77
78impl ParseError {
79 fn new(error_type: ParseErrorType, span: Span) -> Self {
80 Self {
81 error_type,
82 span: format!("{span:?}"),
83 }
84 }
85}
86
87#[derive(Debug)]
88pub enum ParseErrorType {
89 IncompleteImport,
90 IncompleteDatatype,
91 IncompleteProperty,
92 MissingType(String),
93 IncompleteEnum,
94 IncompleteEnumValue,
95 InvalidEnumValue(String),
96 IncompleteService,
97 IncompleteDepends,
98 IncompleteCall,
99 IncompleteEvent,
100 IncompleteArgumentIdent,
101 IncompleteAttributeArg,
102 IncompleteAttribute,
103 IncompleteName,
104 UnexpectedElement(String),
105 OtherError(String),
106}
107
108impl std::fmt::Display for ParseError {
109 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
110 match &self.error_type {
111 ParseErrorType::IncompleteImport => write!(f, "Import incomplete. ({})", self.span),
112 ParseErrorType::IncompleteDatatype => write!(f, "Datatype incomplete. ({})", self.span),
113 ParseErrorType::IncompleteProperty => write!(f, "Property incomplete. ({})", self.span),
114 ParseErrorType::MissingType(name) => {
115 write!(f, "Type missing after {}. ({:?})", name, self.span)
116 }
117 ParseErrorType::IncompleteService => write!(f, "Service incomplete. ({})", self.span),
118 ParseErrorType::IncompleteDepends => write!(f, "Depends incomplete. ({})", self.span),
119 ParseErrorType::IncompleteCall => write!(f, "Call incomplete. ({})", self.span),
120 ParseErrorType::IncompleteEvent => write!(f, "Event incomplete. ({})", self.span),
121 ParseErrorType::IncompleteArgumentIdent => {
122 write!(f, "Argument ident incomplete. ({})", self.span)
123 }
124 ParseErrorType::IncompleteAttributeArg => {
125 write!(f, "Attribute argument incomplete. ({})", self.span)
126 }
127 ParseErrorType::IncompleteAttribute => {
128 write!(f, "Attribute incomplete. ({})", self.span)
129 }
130 ParseErrorType::IncompleteName => {
131 write!(f, "Name incomplete. ({})", self.span)
132 }
133 ParseErrorType::UnexpectedElement(info) => {
134 write!(f, "Unexpected element {} ({})", info, self.span)
135 }
136 ParseErrorType::IncompleteEnum => write!(f, "Incomplete enum. ({})", self.span),
137 ParseErrorType::IncompleteEnumValue => {
138 write!(f, "Incomplete enum value. ({})", self.span)
139 }
140 ParseErrorType::InvalidEnumValue(info) => {
141 write!(f, "Invalid enum value. {} ({})", info, self.span)
142 }
143 ParseErrorType::OtherError(inner) => {
144 write!(f, "Other({inner})")
145 }
146 }
147 }
148}
149
150impl ParseError {
151 fn from_dyn_error<T: std::error::Error>(err: T) -> Self {
152 ParseError {
153 error_type: ParseErrorType::OtherError(format!("{err}")),
154 span: String::new(),
155 }
156 }
157}
158
159impl std::error::Error for ParseError {
160 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
161 None
162 }
163
164 fn cause(&self) -> Option<&dyn std::error::Error> {
165 self.source()
166 }
167}
168
169fn parse_type(typ: &str) -> (&str, bool, Option<usize>) {
170 static RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"(\d+)\s+of").unwrap());
171 if let Some(stripped) = typ.strip_prefix("list of") {
172 (stripped.trim(), true, None)
173 } else if let Some(cap) = RE.captures(typ) {
174 let count_str = cap.get(1).unwrap().as_str();
175 let count = count_str.parse::<usize>().unwrap();
176 (typ[count_str.len() + 3..].trim(), true, Some(count))
177 } else {
178 (typ, false, None)
179 }
180}
181
182#[allow(clippy::too_many_lines)]
183pub fn parse_raw(content: &str) -> Result<Vec<AstElement>, ParseError> {
184 use ParseErrorType::{
185 IncompleteArgumentIdent, IncompleteCall, IncompleteDatatype, IncompleteDepends,
186 IncompleteEnum, IncompleteEnumValue, IncompleteEvent, IncompleteImport, IncompleteProperty,
187 IncompleteService, InvalidEnumValue, MissingType, UnexpectedElement,
188 };
189 let pairs = FileParser::parse(Rule::file, content).map_err(ParseError::from_dyn_error)?;
190 let mut result = Vec::new();
191
192 for p in pairs {
193 match p.as_rule() {
194 Rule::import => {
195 let span = p.as_span();
196 let mut p = p.into_inner();
197 let n = p
198 .next()
199 .ok_or_else(|| ParseError::new(IncompleteImport, span))?;
200 let (name, attributes) = parse_name(&mut p, n)?;
201 result.push(AstElement::Import(Import::new(
202 Namespace::new(&name),
203 attributes,
204 )));
205 }
206 Rule::data => {
207 let span = p.as_span();
208 let mut p = p.into_inner();
209 let n = p
210 .next()
211 .ok_or_else(|| ParseError::new(IncompleteDatatype, span))?;
212 let (name, attributes) = parse_name(&mut p, n)?;
213
214 let mut properties = OrderedMap::new();
215 let mut comments = Vec::new();
216
217 for p in p {
218 if let Rule::COMMENT = p.as_rule() {
219 comments.push(p.as_span().as_str()[3..].trim().to_string());
220 continue;
221 }
222 let span = p.as_span();
223 let mut p = p.into_inner();
224 let n = p
225 .next()
226 .ok_or_else(|| ParseError::new(IncompleteProperty, span))?;
227 let (name, attributes) = parse_name(&mut p, n)?;
228 let typ = p
229 .next()
230 .ok_or_else(|| ParseError::new(MissingType(name.clone()), span))?
231 .as_str()
232 .to_string();
233 let (typ, is_list, count) = parse_type(typ.as_str());
234 properties.push((
235 name,
236 TypeName::new(Namespace::new(typ), is_list, count, attributes)
237 .with_comments(&mut comments),
238 ));
239 }
245
246 result.push(AstElement::DataType((
247 name,
248 DataType::new(properties, attributes),
249 )));
250 }
251 Rule::enum_ => {
252 let span = p.as_span();
253 let mut p = p.into_inner();
254 let n = p
255 .next()
256 .ok_or_else(|| ParseError::new(IncompleteEnum, span))?;
257 let (name, attributes) = parse_name(&mut p, n)?;
258
259 let mut values = OrderedMap::new();
260
261 let mut comments = Vec::new();
262 for p in p {
263 if let Rule::COMMENT = p.as_rule() {
264 comments.push(p.as_span().as_str()[3..].trim().to_string());
265 continue;
266 }
267 let span = p.as_span();
268 let mut p = p.into_inner();
269 let n = p
270 .next()
271 .ok_or_else(|| ParseError::new(IncompleteEnumValue, span))?;
272 let (name, attributes) = parse_name(&mut p, n)?;
273 let value = if let Some(v) = p.next() {
274 Some(v.as_str().parse().map_err(|err: ParseIntError| {
275 ParseError::new(InvalidEnumValue(err.to_string()), span)
276 })?)
277 } else {
278 None
279 };
280 values.push((
281 name,
282 EnumValue::new(value, attributes).with_comments(&mut comments),
283 ));
284 }
289
290 result.push(AstElement::Enum((name, Enum::new(values, attributes))));
291 }
292 Rule::service => {
293 let span = p.as_span();
294 let mut p = p.into_inner();
295 let n = p
296 .next()
297 .ok_or_else(|| ParseError::new(IncompleteService, span))?;
298 let (service_name, attributes) = parse_name(&mut p, n)?;
299
300 let mut service_parts = Vec::new();
301
302 for p in p {
303 let rule = p.as_rule();
304 match rule {
305 Rule::depends => {
306 let span = p.as_span();
307 let mut p = p.into_inner();
308 let n = p
309 .next()
310 .ok_or_else(|| ParseError::new(IncompleteDepends, span))?;
311 let (name, attributes) = parse_name(&mut p, n)?;
312 service_parts.push(ServiceAstElement::Dependency(Dependency::new(
313 Namespace::new(&name),
314 attributes,
315 )));
316 }
317 Rule::function | Rule::handler => {
318 if rule == Rule::handler {
319 const DEPRECATED: &str = "Using 'handlers' is deprecated and will be removed in future versions. Use 'fn' instead.";
320 let mut stderr = StandardStream::stderr(ColorChoice::Always);
321 if stderr
322 .set_color(ColorSpec::new().set_fg(Some(Color::Yellow)))
323 .is_ok()
324 {
325 writeln!(&mut stderr, "{DEPRECATED}").unwrap();
326
327 let _ = stderr.set_color(&ColorSpec::default());
328 } else {
329 eprintln!("{DEPRECATED}");
330 }
331 }
332 let span = p.as_span();
333 let mut p = p.into_inner();
334 let n = p
335 .next()
336 .ok_or_else(|| ParseError::new(IncompleteCall, span))?;
337 let (call_name, call_attributes) = parse_name(&mut p, n)?;
338 let mut arguments = OrderedMap::new();
339 let mut return_type = None;
340 let mut attributes = Vec::new();
341 for p in p.by_ref() {
342 match p.as_rule() {
343 Rule::argument => {
344 let span = p.as_span();
345 let mut p = p.clone().into_inner();
346 while let Some(n) = p.next() {
347 match n.as_rule() {
348 Rule::ident => {
349 let name = n.as_str().to_string();
350 let typ = p.next().ok_or_else(|| ParseError::new(IncompleteArgumentIdent, span))?.as_str().to_string();
351 let (typ, is_list, count) = parse_type(typ.as_str());
352 arguments.push((name, TypeName::new(Namespace::new(typ), is_list, count, attributes.clone())));
353 attributes.clear();
355 }
356 Rule::attributes => {
357 attributes = parse_attributes(n)?;
358 }
359 _ => Err(ParseError::new(
360 UnexpectedElement(format!(
361 "while parsing argument for call \"{call_name}\" in service \"{service_name}\"! {p}"
362 )),
363 span,
364 ))?,
365 }
366 }
367 }
368 Rule::typ => {
369 static RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"\s+").unwrap());
370 let typ = RE.replace_all(p.as_str(), " ");
371 let (typ, is_list, count) = parse_type(&typ);
372 return_type = Some(TypeName::new(
373 Namespace::new(typ),
374 is_list,
375 count,
376 Vec::new(),
377 ));
378 }
379 _ => Err(ParseError::new(
380 UnexpectedElement(format!(
381 "while parsing call \"{call_name}\" in service \"{service_name}\"! {p}"
382 )),
383 p.as_span(),
384 ))?,
385 }
386 }
387
388 if let Some(p) = p.next() {
389 if p.as_rule() == Rule::typ {
390 let (typ, is_list, count) = parse_type(p.as_str());
391 return_type = Some(TypeName::new(
392 Namespace::new(typ),
393 is_list,
394 count,
395 Vec::new(),
396 ));
397 } else {
398 Err(ParseError::new(
399 UnexpectedElement(format!(
400 "while parsing return type for call \"{call_name}\" in service \"{service_name}\"! {p}"
401 )),
402 p.as_span(),
403 ))?;
404 }
405 }
406 service_parts.push(ServiceAstElement::Function((
407 call_name,
408 Function::new(arguments, return_type, call_attributes),
409 )));
410 }
411 Rule::event => {
412 let span = p.as_span();
413 let mut p = p.into_inner();
414 let n = p
415 .next()
416 .ok_or_else(|| ParseError::new(IncompleteEvent, span))?;
417 let (event_name, event_attributes) = parse_name(&mut p, n)?;
418 let mut arguments = OrderedMap::new();
419 let mut attributes = Vec::new();
420 for p in p.by_ref() {
421 match p.as_rule() {
422 Rule::argument => {
423 let span = p.as_span();
424 let mut p = p.clone().into_inner();
425 while let Some(n) = p.next() {
426 match n.as_rule() {
427 Rule::ident => {
428 let name = n.as_str().to_string();
429 let typ = p.next().ok_or_else(|| ParseError::new(IncompleteArgumentIdent, span))?.as_str().to_string();
430 let (typ, is_list, count) = parse_type(typ.as_str());
431 arguments.push((name, TypeName::new(Namespace::new(typ), is_list, count, attributes.clone())));
432 attributes.clear();
434 }
435 Rule::attributes => {
436 attributes = parse_attributes(n)?;
437 }
438 _ => Err(ParseError::new(
439 UnexpectedElement(format!(
440 "while parsing argument for event \"{event_name}\" in service \"{service_name}\"! {p}"
441 )),
442 span,
443 ))?,
444 }
445 }
446 }
447 _ => Err(ParseError::new(
448 UnexpectedElement(format!(
449 "while parsing event \"{event_name}\" in service \"{service_name}\"! {p}"
450 )),
451 p.as_span(),
452 ))?,
453 }
454 }
455
456 service_parts.push(ServiceAstElement::Event((
457 event_name,
458 Event::new(arguments, event_attributes),
459 )));
460 }
461 Rule::COMMENT => service_parts.push(ServiceAstElement::Comment(
462 p.as_span().as_str()[3..].trim().to_string(),
463 )),
464 _ => Err(ParseError::new(
465 UnexpectedElement(format!(
466 "while parsing service \"{service_name}\"! {p}"
467 )),
468 p.as_span(),
469 ))?,
470 }
471 }
472
473 result.push(AstElement::Service((
474 service_name,
475 service_parts,
476 attributes,
477 )));
478 }
479 Rule::EOI => {}
480 Rule::COMMENT => {
481 let span = p.as_span();
482 result.push(AstElement::Comment(span.as_str()[3..].trim().to_string()));
483 }
484 _ => Err(ParseError::new(
485 UnexpectedElement(format!("{p}")),
486 p.as_span(),
487 ))?,
488 }
489 }
490
491 Ok(result)
492}
493
494#[allow(unused)]
495pub fn parse(content: &str, namespace: Namespace) -> Result<SsdModule, ParseError> {
496 let raw = parse_raw(content)?;
497 Ok(raw_to_ssd_file(namespace, &raw))
498}
499
500pub(crate) fn raw_service_to_service(
501 raw: &[ServiceAstElement],
502 attributes: &[Attribute],
503) -> Service {
504 let mut dependencies = Vec::new();
505 let mut functions = OrderedMap::new();
506 let mut events = OrderedMap::new();
507
508 let mut comments = Vec::new();
509 for element in raw {
510 match element {
511 ServiceAstElement::Dependency(import) => {
512 dependencies.push(import.clone().with_comments(&mut comments));
513 }
514 ServiceAstElement::Function((key, value)) => {
515 assert!(
516 !functions.iter().any(|(name, _)| name == key),
517 "Duplicate function {key}!"
518 );
519 functions.push((key.clone(), value.clone().with_comments(&mut comments)));
520 }
527 ServiceAstElement::Event((key, value)) => {
528 assert!(
529 !events.iter().any(|(name, _)| name == key),
530 "Duplicate event {key}!"
531 );
532 events.push((key.clone(), value.clone().with_comments(&mut comments)));
533 }
540 ServiceAstElement::Comment(c) => comments.push(c.to_string()),
541 }
542 }
543
544 Service::new(dependencies, functions, events, attributes.into())
545}
546
547pub(crate) fn raw_to_ssd_file(namespace: Namespace, raw: &[AstElement]) -> SsdModule {
548 let mut imports = Vec::new();
549 let mut datatypes = OrderedMap::new();
550 let mut enums = OrderedMap::new();
551 let mut services = OrderedMap::new();
552
553 for element in raw {
554 match element {
555 AstElement::Import(import) => imports.push(import.clone()),
556 AstElement::DataType((key, value)) => {
557 assert!(
558 !datatypes.iter().any(|(name, _)| name == key),
559 "Duplicate datatype {key}!"
560 );
561 datatypes.push((key.clone(), value.clone()));
562 }
567 AstElement::Enum((key, value)) => {
568 assert!(
569 !enums.iter().any(|(name, _)| name == key),
570 "Duplicate enum {key}!"
571 );
572 enums.push((key.clone(), value.clone()));
573 }
578
579 AstElement::Service((key, value, attributes)) => {
580 assert!(
581 !services.iter().any(|(name, _)| name == key),
582 "Duplicate service {key}!"
583 );
584 services.push((key.clone(), raw_service_to_service(value, attributes)));
585 }
590 AstElement::Comment(_) => (),
591 }
592 }
593
594 SsdModule::new(namespace, imports, datatypes, enums, services)
595}
596
597pub fn parse_file_raw<P: AsRef<Path>>(path: P) -> Result<Vec<AstElement>, ParseError> {
598 let content = std::fs::read_to_string(path).map_err(ParseError::from_dyn_error)?;
599
600 parse_raw(&content)
601}
602
603pub fn parse_file<P: AsRef<Path>>(base: &P, path: &P) -> Result<SsdModule, ParseError> {
612 let base = base.as_ref();
613 let path = path.as_ref();
614 let mut components = if path.starts_with(base) {
615 path.strip_prefix(base)
616 .map_err(ParseError::from_dyn_error)?
617 .to_owned()
618 } else {
619 path.to_owned()
620 };
621
622 components.set_extension("");
623 let components = components
624 .components()
625 .map(|c| c.as_os_str().to_string_lossy().to_string())
626 .collect::<Vec<_>>();
627
628 parse_file_with_namespace(path, Namespace::from_vec(components))
629}
630
631#[allow(unused)]
632pub fn parse_file_with_namespace<P: AsRef<Path>>(
633 path: P,
634 namespace: Namespace,
635) -> Result<SsdModule, ParseError> {
636 let raw = parse_file_raw(path)?;
637
638 Ok(raw_to_ssd_file(namespace, &raw))
639}
640
641#[test]
642fn test_simple() {
643 insta::assert_json_snapshot!(parse(
644 include_str!("../../../data/test.svc"),
645 Namespace::new("__test__")
646 )
647 .unwrap());
648}
649
650#[test]
651fn test_raw() {
652 insta::assert_json_snapshot!(parse_raw(include_str!("../../../data/test.svc"),).unwrap());
653}