1use crate::ast::{Comment, Definition, DocString, Ident, ImportStmt, Prelude};
2use crate::error::{DuplicateDefinition, InvalidSchemaName, InvalidSyntax, IoError};
3use crate::grammar::{Grammar, Rule};
4use crate::issues::Issues;
5use crate::validate::Validate;
6use crate::warning::{BrokenDocLink, DuplicateImport, NonSnakeCaseSchemaName, ReservedSchemaName};
7use crate::SchemaFile;
8use pest::Parser;
9
10#[derive(Debug, Clone)]
11pub struct Schema {
12 name: String,
13 path: String,
14 source: Option<String>,
15 comment: Vec<Comment>,
16 doc: Vec<DocString>,
17 imports: Vec<ImportStmt>,
18 defs: Vec<Definition>,
19}
20
21impl Schema {
22 pub(crate) fn parse(file: &SchemaFile, issues: &mut Issues) -> Self {
23 let mut schema = Self {
24 name: file.name().to_owned(),
25 path: file.path().to_owned(),
26 source: None,
27 comment: Vec::new(),
28 doc: Vec::new(),
29 imports: Vec::new(),
30 defs: Vec::new(),
31 };
32
33 let source = match file.source() {
34 Ok(source) => source,
35
36 Err(e) => {
37 issues.add_error(IoError::new(&schema.name, e.to_string()));
38 return schema;
39 }
40 };
41
42 schema.source = Some(source.to_owned());
43
44 let mut pairs = match Grammar::parse(Rule::file, source) {
45 Ok(pairs) => pairs,
46
47 Err(e) => {
48 issues.add_error(InvalidSyntax::new(&schema.name, e));
49 return schema;
50 }
51 };
52
53 let mut prelude = Prelude::schema(&mut pairs);
54
55 for pair in pairs {
56 match pair.as_rule() {
57 Rule::import_stmt => schema.imports.push(ImportStmt::parse(pair)),
58 Rule::def => schema.defs.push(Definition::parse(pair)),
59 Rule::EOI => break,
60 _ => unreachable!(),
61 }
62 }
63
64 schema.comment = prelude.take_comment();
65 schema.doc = prelude.take_doc();
66
67 schema
68 }
69
70 pub(crate) fn validate(&self, validate: &mut Validate) {
71 if !Ident::is_valid(&self.name) {
72 validate.add_error(InvalidSchemaName::new(self.name.clone()));
73 }
74
75 if self.source.is_none() {
76 return;
77 }
78
79 BrokenDocLink::validate(&self.doc, validate);
80 DuplicateDefinition::validate(self, validate);
81 DuplicateImport::validate(self, validate);
82 NonSnakeCaseSchemaName::validate(&self.name, validate);
83 ReservedSchemaName::validate(&self.name, validate);
84
85 for import in &self.imports {
86 import.validate(validate);
87 }
88
89 for def in &self.defs {
90 def.validate(validate);
91 }
92 }
93
94 pub fn name(&self) -> &str {
95 &self.name
96 }
97
98 pub fn path(&self) -> &str {
99 &self.path
100 }
101
102 pub fn source(&self) -> Option<&str> {
103 self.source.as_deref()
104 }
105
106 pub fn comment(&self) -> &[Comment] {
107 &self.comment
108 }
109
110 pub fn doc(&self) -> &[DocString] {
111 &self.doc
112 }
113
114 pub fn imports(&self) -> &[ImportStmt] {
115 &self.imports
116 }
117
118 pub fn definitions(&self) -> &[Definition] {
119 &self.defs
120 }
121}