Skip to main content

aldrin_parser/
schema.rs

1use crate::SchemaFile;
2use crate::ast::{Comment, Definition, DocString, Ident, ImportStmt, Prelude};
3use crate::error::{DuplicateDefinition, InvalidSchemaName, InvalidSyntax, IoError};
4use crate::grammar::{Grammar, Rule};
5use crate::issues::Issues;
6use crate::validate::Validate;
7use crate::warning::{BrokenDocLink, DuplicateImport, NonSnakeCaseSchemaName, ReservedSchemaName};
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            #[expect(clippy::wildcard_enum_match_arm)]
57            match pair.as_rule() {
58                Rule::import_stmt => schema.imports.push(ImportStmt::parse(pair)),
59                Rule::def => schema.defs.push(Definition::parse(pair)),
60                Rule::EOI => break,
61                _ => unreachable!(),
62            }
63        }
64
65        schema.comment = prelude.take_comment();
66        schema.doc = prelude.take_doc();
67
68        schema
69    }
70
71    pub(crate) fn validate(&self, validate: &mut Validate) {
72        if !Ident::is_valid(&self.name) {
73            validate.add_error(InvalidSchemaName::new(self.name.clone()));
74        }
75
76        if self.source.is_none() {
77            return;
78        }
79
80        BrokenDocLink::validate(&self.doc, validate);
81        DuplicateDefinition::validate(self, validate);
82        DuplicateImport::validate(self, validate);
83        NonSnakeCaseSchemaName::validate(&self.name, validate);
84        ReservedSchemaName::validate(&self.name, validate);
85
86        for import in &self.imports {
87            import.validate(validate);
88        }
89
90        for def in &self.defs {
91            def.validate(validate);
92        }
93    }
94
95    pub fn name(&self) -> &str {
96        &self.name
97    }
98
99    pub fn path(&self) -> &str {
100        &self.path
101    }
102
103    pub fn source(&self) -> Option<&str> {
104        self.source.as_deref()
105    }
106
107    pub fn comment(&self) -> &[Comment] {
108        &self.comment
109    }
110
111    pub fn doc(&self) -> &[DocString] {
112        &self.doc
113    }
114
115    pub fn imports(&self) -> &[ImportStmt] {
116        &self.imports
117    }
118
119    pub fn definitions(&self) -> &[Definition] {
120        &self.defs
121    }
122}