aldrin_parser/
schema.rs

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}