ninja_files_data/file/
builder.rs

1use camino::Utf8Path;
2
3use crate::{
4    Build, BuildBuilder, BuildBuilderError, File, FileName, InvalidFileName, InvalidRuleId,
5    InvalidVariable, InvalidVariableId, Rule, RuleBuilder, RuleBuilderError, RuleId, Variable,
6    VariableId,
7};
8
9#[derive(Clone, Debug, Default)]
10pub struct FileBuilder {
11    file: File,
12    errors: Vec<FileBuilderError>,
13}
14
15#[derive(Clone, Debug)]
16pub enum FileBuilderError {
17    InvalidVariableId(InvalidVariableId),
18    InvalidVariable(InvalidVariable),
19    InvaidRuleId(InvalidRuleId),
20    InvalidFileName(InvalidFileName),
21    InvalidRule {
22        id: RuleId,
23        errors: Vec<RuleBuilderError>,
24    },
25    InvalidBuild {
26        id: FileName,
27        errors: Vec<BuildBuilderError>,
28    },
29    RuleAlreadyExists {
30        id: RuleId,
31        previous: Rule,
32        rule: Rule,
33    },
34    BuildAlreadyExists {
35        id: FileName,
36        previous: Build,
37        build: Build,
38    },
39    VariableAlreadyExists(VariableId),
40}
41
42impl FileBuilder {
43    pub fn new() -> FileBuilder {
44        let file = File::default();
45        let errors = vec![];
46        FileBuilder { file, errors }
47    }
48
49    pub fn build(&self) -> Result<File, Vec<FileBuilderError>> {
50        if self.errors.is_empty() {
51            Ok(self.file.clone())
52        } else {
53            Err(self.errors.clone())
54        }
55    }
56
57    pub fn variable<Id, Var>(mut self, id: Id, var: Var) -> FileBuilder
58    where
59        Id: AsRef<str>,
60        Var: AsRef<str>,
61    {
62        let id = VariableId::try_create(id);
63        let var = Variable::try_create(var);
64
65        match (id, var) {
66            (Ok(id), Ok(var)) => match self.file.variables.insert(id.clone(), var.clone()) {
67                Some(exist) if exist == var => (),
68                Some(_) => self
69                    .errors
70                    .push(FileBuilderError::VariableAlreadyExists(id)),
71                None => (),
72            },
73            (Ok(_), Err(e)) => self.errors.push(FileBuilderError::InvalidVariable(e)),
74            (Err(e), Ok(_)) => self.errors.push(FileBuilderError::InvalidVariableId(e)),
75            (Err(e1), Err(e2)) => {
76                self.errors.push(FileBuilderError::InvalidVariableId(e1));
77                self.errors.push(FileBuilderError::InvalidVariable(e2));
78            }
79        }
80        self
81    }
82
83    pub fn rule<Id, Rule>(mut self, id: Id, rule: Rule) -> FileBuilder
84    where
85        Id: AsRef<str>,
86        Rule: Into<RuleBuilder>,
87    {
88        let id = RuleId::try_create(id);
89        let rule = rule.into().build();
90
91        if let Some(err) = id.clone().err() {
92            self.errors.push(FileBuilderError::InvaidRuleId(err));
93            return self;
94        }
95        let id = id.unwrap();
96        if let Some(err) = rule.clone().err() {
97            self.errors
98                .push(FileBuilderError::InvalidRule { id, errors: err });
99            return self;
100        }
101        let rule = rule.unwrap();
102
103        match self.file.rules.get(&id) {
104            Some(previous) if previous == &rule => (),
105            Some(previous) => self.errors.push(FileBuilderError::RuleAlreadyExists {
106                id,
107                previous: previous.clone(),
108                rule,
109            }),
110            None => {
111                let _ = self.file.rules.insert(id, rule);
112            }
113        }
114
115        self
116    }
117
118    pub fn file<File, Build>(mut self, filename: File, build: Build) -> Self
119    where
120        File: AsRef<Utf8Path>,
121        Build: Into<BuildBuilder>,
122    {
123        let filename = FileName::try_create(filename);
124        let build = build.into().build();
125
126        if let Some(err) = filename.clone().err() {
127            self.errors.push(FileBuilderError::InvalidFileName(err));
128            return self;
129        }
130        let filename = filename.unwrap();
131
132        if let Some(errors) = build.clone().err() {
133            self.errors.push(FileBuilderError::InvalidBuild {
134                id: filename.clone(),
135                errors,
136            })
137        }
138        let build = build.unwrap();
139
140        match self.file.statements.get(&filename) {
141            Some(previous) if previous == &build => (),
142            Some(previous) => self.errors.push(FileBuilderError::BuildAlreadyExists {
143                id: filename,
144                previous: previous.clone(),
145                build,
146            }),
147            None => {
148                let _ = self.file.statements.insert(filename, build);
149            }
150        }
151        self
152    }
153
154    pub fn merge(mut self, other: &Self) -> Self {
155        // combine all the errors
156        self.errors.extend(other.errors.clone());
157
158        let mut file = self;
159        for (id, var) in other.file.variables.iter() {
160            file = file.variable(id.clone(), var.clone());
161        }
162
163        for (id, rule) in other.file.rules.iter() {
164            file = file.rule(id.clone(), rule.clone());
165        }
166
167        for (id, rule) in other.file.statements.iter() {
168            file = file.file(id.clone(), rule.clone());
169        }
170
171        file
172    }
173}