aldrin_parser/
schema.rs

1use crate::ast::{Definition, ImportStmt, SchemaName};
2use crate::error::{DuplicateDefinition, InvalidSchemaName, InvalidSyntax, IoError};
3use crate::grammar::{Grammar, Rule};
4use crate::issues::Issues;
5use crate::validate::Validate;
6use crate::warning::{DuplicateImport, NonSnakeCaseSchemaName};
7use pest::Parser;
8use std::fs::File;
9use std::io::Read;
10use std::path::{Path, PathBuf};
11
12#[derive(Debug, Clone)]
13pub struct Schema {
14    name: String,
15    path: PathBuf,
16    source: Option<String>,
17    imports: Vec<ImportStmt>,
18    defs: Vec<Definition>,
19}
20
21impl Schema {
22    pub(crate) fn parse<P>(schema_path: P, issues: &mut Issues) -> Self
23    where
24        P: AsRef<Path>,
25    {
26        let schema_path = schema_path.as_ref();
27
28        let mut schema = Self {
29            name: Self::parse_file_name(schema_path, issues),
30            path: schema_path.to_owned(),
31            source: None,
32            imports: Vec::new(),
33            defs: Vec::new(),
34        };
35
36        let source = {
37            let mut file = match File::open(schema_path) {
38                Ok(file) => file,
39                Err(e) => {
40                    issues.add_error(IoError::new(&schema.name, e));
41                    return schema;
42                }
43            };
44
45            let mut source = String::new();
46            if let Err(e) = file.read_to_string(&mut source) {
47                issues.add_error(IoError::new(&schema.name, e));
48                return schema;
49            }
50
51            source
52        };
53
54        let pairs = match Grammar::parse(Rule::file, &source) {
55            Ok(pairs) => pairs,
56            Err(e) => {
57                schema.source = Some(source);
58                issues.add_error(InvalidSyntax::new(&schema.name, e));
59                return schema;
60            }
61        };
62
63        for pair in pairs {
64            match pair.as_rule() {
65                Rule::import_stmt => schema.imports.push(ImportStmt::parse(pair)),
66                Rule::def => schema.defs.push(Definition::parse(pair)),
67                Rule::EOI => break,
68                _ => unreachable!(),
69            }
70        }
71
72        schema.source = Some(source);
73        schema
74    }
75
76    fn parse_file_name<P>(path: P, issues: &mut Issues) -> String
77    where
78        P: AsRef<Path>,
79    {
80        let path = path.as_ref();
81
82        let file_stem = match path.file_stem() {
83            Some(file_stem) => file_stem,
84            None => {
85                let schema_name = path.to_string_lossy().into_owned();
86                issues.add_error(InvalidSchemaName::new(&schema_name));
87                return schema_name;
88            }
89        };
90
91        let file_stem_str = match file_stem.to_str() {
92            Some(file_stem_str) => file_stem_str,
93            None => {
94                let schema_name = file_stem.to_string_lossy().into_owned();
95                issues.add_error(InvalidSchemaName::new(&schema_name));
96                return schema_name;
97            }
98        };
99
100        let mut schema_name_pairs = match Grammar::parse(Rule::schema_name, file_stem_str) {
101            Ok(schema_name_pairs) => schema_name_pairs,
102            Err(_) => {
103                issues.add_error(InvalidSchemaName::new(file_stem_str));
104                return file_stem_str.to_owned();
105            }
106        };
107
108        if schema_name_pairs.as_str() != file_stem_str {
109            issues.add_error(InvalidSchemaName::new(file_stem_str));
110            return file_stem_str.to_owned();
111        }
112
113        let schema_name = SchemaName::parse(schema_name_pairs.next().unwrap());
114        schema_name.value().to_owned()
115    }
116
117    pub(crate) fn validate(&self, validate: &mut Validate) {
118        if self.source.is_none() {
119            return;
120        }
121
122        DuplicateDefinition::validate(self, validate);
123        NonSnakeCaseSchemaName::validate(&self.name, validate);
124        DuplicateImport::validate(self, validate);
125
126        for import in &self.imports {
127            import.validate(validate);
128        }
129
130        for def in &self.defs {
131            def.validate(validate);
132        }
133    }
134
135    pub fn name(&self) -> &str {
136        &self.name
137    }
138
139    pub fn path(&self) -> &Path {
140        &self.path
141    }
142
143    pub fn source(&self) -> Option<&str> {
144        self.source.as_deref()
145    }
146
147    pub fn imports(&self) -> &[ImportStmt] {
148        &self.imports
149    }
150
151    pub fn definitions(&self) -> &[Definition] {
152        &self.defs
153    }
154}