1use std::collections::{BTreeMap, BTreeSet};
2
3use camino::Utf8Path;
4
5use crate::{
6 Build, FileName, InvalidFileName, InvalidRuleId, InvalidVariable, InvalidVariableId, RuleId,
7 Variable, VariableId,
8};
9
10#[derive(Debug, Clone)]
11pub struct BuildBuilder {
12 rule: Option<RuleId>,
13 explicits: BTreeSet<FileName>,
14 implicits: BTreeSet<FileName>,
15 order: BTreeSet<FileName>,
16 dyndep: Option<FileName>,
17 shadow: BTreeMap<VariableId, Variable>,
18 errors: Vec<BuildBuilderError>,
19}
20
21#[derive(Debug, Clone)]
22pub enum BuildBuilderError {
23 InvalidRuleId(InvalidRuleId),
24 InvalidFileName(InvalidFileName),
25 InvalidVariable(InvalidVariable),
26 InvalidVariableId(InvalidVariableId),
27 DynDepAlreadyDefined {
28 previous: FileName,
29 dyndep: FileName,
30 },
31 VariableAlreadyDefined {
32 id: VariableId,
33 previous: Variable,
34 var: Variable,
35 },
36}
37
38impl BuildBuilder {
39 pub fn new<Id>(rule_id: Id) -> Self
40 where
41 Id: AsRef<str>,
42 {
43 let rule_id = RuleId::try_create(rule_id);
44 let mut errors = vec![];
45 if let Some(err) = rule_id.clone().err() {
46 errors.push(BuildBuilderError::InvalidRuleId(err));
47 }
48
49 let rule = rule_id.ok();
50 BuildBuilder {
51 rule,
52 errors,
53 explicits: BTreeSet::new(),
54 implicits: BTreeSet::new(),
55 order: BTreeSet::new(),
56 dyndep: None,
57 shadow: BTreeMap::new(),
58 }
59 }
60
61 pub fn build(&self) -> Result<Build, Vec<BuildBuilderError>> {
62 if !self.errors.is_empty() {
63 return Err(self.errors.clone());
64 }
65 let build = Build {
67 rule: self.rule.clone().unwrap(),
68 explicits: self.explicits.clone(),
69 implicits: self.implicits.clone(),
70 order: self.order.clone(),
71 dyndep: self.dyndep.clone(),
72 shadow: self.shadow.clone(),
73 };
74 Ok(build)
75 }
76
77 pub fn explicit<Dep>(mut self, dep: Dep) -> Self
78 where
79 Dep: AsRef<Utf8Path>,
80 {
81 let filename = FileName::try_create(dep);
82 match filename {
83 Ok(file) => {
84 self.explicits.insert(file);
85 }
86 Err(err) => self.errors.push(BuildBuilderError::InvalidFileName(err)),
87 }
88 self
89 }
90
91 pub fn implicit<Dep>(mut self, dep: Dep) -> Self
92 where
93 Dep: AsRef<Utf8Path>,
94 {
95 let filename = FileName::try_create(dep);
96 match filename {
97 Ok(file) => {
98 self.implicits.insert(file);
99 }
100 Err(err) => self.errors.push(BuildBuilderError::InvalidFileName(err)),
101 }
102 self
103 }
104
105 pub fn order<Dep>(mut self, dep: Dep) -> Self
106 where
107 Dep: AsRef<Utf8Path>,
108 {
109 let filename = FileName::try_create(dep);
110 match filename {
111 Ok(file) => {
112 self.order.insert(file);
113 }
114 Err(err) => self.errors.push(BuildBuilderError::InvalidFileName(err)),
115 }
116 self
117 }
118
119 pub fn dyndep<Dep>(mut self, dep: Dep) -> Self
120 where
121 Dep: AsRef<Utf8Path>,
122 {
123 let filename = FileName::try_create(dep);
124 match (filename, self.dyndep.clone()) {
125 (Ok(file), None) => self.dyndep = Some(file),
126 (Ok(dyndep), Some(previous)) => self
127 .errors
128 .push(BuildBuilderError::DynDepAlreadyDefined { previous, dyndep }),
129 (Err(err), _) => self.errors.push(BuildBuilderError::InvalidFileName(err)),
130 }
131 self
132 }
133
134 pub fn variable<Id, Var>(mut self, id: Id, var: Var) -> Self
135 where
136 Id: AsRef<str>,
137 Var: AsRef<str>,
138 {
139 let id = VariableId::try_create(id);
140 let var = Variable::try_create(var);
141 match (id, var) {
142 (Ok(id), Ok(var)) => match self.shadow.insert(id.clone(), var.clone()) {
143 Some(previous) if var == previous => (),
144 Some(previous) => self.errors.push(BuildBuilderError::VariableAlreadyDefined {
145 id,
146 previous,
147 var,
148 }),
149 None => (),
150 },
151 (Ok(_), Err(v)) => self.errors.push(BuildBuilderError::InvalidVariable(v)),
152 (Err(i), Ok(_)) => self.errors.push(BuildBuilderError::InvalidVariableId(i)),
153 (Err(i), Err(v)) => {
154 self.errors.push(BuildBuilderError::InvalidVariable(v));
155 self.errors.push(BuildBuilderError::InvalidVariableId(i));
156 }
157 }
158 self
159 }
160}
161
162impl From<Build> for BuildBuilder {
163 fn from(
164 Build {
165 rule,
166 explicits,
167 implicits,
168 order,
169 dyndep,
170 shadow,
171 }: Build,
172 ) -> Self {
173 BuildBuilder {
174 rule: Some(rule),
175 explicits,
176 implicits,
177 order,
178 dyndep,
179 shadow,
180 errors: vec![],
181 }
182 }
183}