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 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}