version_spec/
unresolved_parser.rs1use crate::spec_error::SpecError;
2use human_sort::compare;
3
4#[derive(Debug, Default, PartialEq)]
5pub enum ParseKind {
6 #[default]
7 Unknown,
8 Req,
9 Cal,
10 Sem,
11}
12
13#[derive(Debug, Default, PartialEq)]
14pub enum ParsePart {
15 #[default]
16 Start,
17 ReqPrefix,
18 MajorYear,
19 MinorMonth,
20 PatchDay,
21 PreId,
22 BuildSuffix,
23}
24
25impl ParsePart {
26 pub fn is_prefix(&self) -> bool {
27 matches!(self, Self::Start | Self::ReqPrefix)
28 }
29
30 pub fn is_suffix(&self) -> bool {
31 matches!(self, Self::PreId | Self::BuildSuffix)
32 }
33}
34
35#[derive(Debug, Default)]
36pub struct UnresolvedParser {
37 kind: ParseKind,
39 in_part: ParsePart,
40 is_and: bool,
41 req_op: String,
42 major_year: String,
43 minor_month: String,
44 patch_day: String,
45 pre_id: String,
46 build_id: String,
47
48 results: Vec<String>,
50}
51
52impl UnresolvedParser {
53 pub fn parse(mut self, input: impl AsRef<str>) -> Result<(String, ParseKind), SpecError> {
54 let input = input.as_ref().trim();
55
56 if input.is_empty() || input == "*" {
57 return Ok(("*".to_owned(), ParseKind::Req));
58 }
59
60 for ch in input.chars() {
61 match ch {
62 '=' | '~' | '^' | '>' | '<' => {
64 if self.in_part != ParsePart::Start && self.in_part != ParsePart::ReqPrefix {
65 return Err(SpecError::InvalidParseRequirement);
66 }
67
68 self.in_part = ParsePart::ReqPrefix;
69 self.req_op.push(ch);
70 }
71 '*' => {
73 }
75 '0'..='9' => {
77 let part_str = match self.in_part {
78 ParsePart::Start | ParsePart::ReqPrefix | ParsePart::MajorYear => {
79 self.in_part = ParsePart::MajorYear;
80 &mut self.major_year
81 }
82 ParsePart::MinorMonth => &mut self.minor_month,
83 ParsePart::PatchDay => &mut self.patch_day,
84 ParsePart::PreId => &mut self.pre_id,
85 ParsePart::BuildSuffix => &mut self.build_id,
86 };
87
88 part_str.push(ch);
89 }
90 'a'..='z' | 'A'..='Z' => match self.in_part {
92 ParsePart::PreId => {
93 self.pre_id.push(ch);
94 }
95 ParsePart::BuildSuffix => {
96 self.build_id.push(ch);
97 }
98 _ => {
99 if ch == 'v' || ch == 'V' {
101 continue;
102 } else {
103 return Err(SpecError::UnknownParseChar(ch));
104 }
105 }
106 },
107 '.' | '-' => {
109 if self.kind == ParseKind::Unknown {
111 if ch == '-' {
112 self.kind = ParseKind::Cal;
113 } else {
114 self.kind = ParseKind::Sem;
115 }
116 }
117
118 if ch == '-' {
120 if self.kind == ParseKind::Sem {
121 match self.in_part {
122 ParsePart::MajorYear
123 | ParsePart::MinorMonth
124 | ParsePart::PatchDay => {
125 self.in_part = ParsePart::PreId;
126 }
127 ParsePart::PreId => {
128 self.pre_id.push('-');
129 }
130 ParsePart::BuildSuffix => {
131 self.build_id.push('-');
132 }
133 _ => continue,
134 };
135 } else if self.kind == ParseKind::Cal {
136 match self.in_part {
137 ParsePart::MajorYear => {
138 self.in_part = ParsePart::MinorMonth;
139 }
140 ParsePart::MinorMonth => {
141 self.in_part = ParsePart::PatchDay;
142 }
143 ParsePart::PatchDay | ParsePart::BuildSuffix => {
144 self.in_part = ParsePart::PreId;
145 }
146 ParsePart::PreId => {
147 self.pre_id.push('-');
148 }
149 _ => continue,
150 };
151 }
152 } else if ch == '.' {
153 if self.kind == ParseKind::Sem {
154 match self.in_part {
155 ParsePart::MajorYear => {
156 self.in_part = ParsePart::MinorMonth;
157 }
158 ParsePart::MinorMonth => {
159 self.in_part = ParsePart::PatchDay;
160 }
161 ParsePart::PatchDay => {
162 self.in_part = ParsePart::PreId;
163 }
164 ParsePart::PreId => {
165 self.pre_id.push('.');
166 }
167 ParsePart::BuildSuffix => {
168 self.build_id.push('.');
169 }
170 _ => continue,
171 };
172 } else if self.kind == ParseKind::Cal {
173 match self.in_part {
174 ParsePart::MajorYear
175 | ParsePart::MinorMonth
176 | ParsePart::PatchDay => {
177 self.in_part = ParsePart::BuildSuffix;
178 }
179 ParsePart::PreId => {
180 self.pre_id.push('.');
181 }
182 ParsePart::BuildSuffix => {
183 self.build_id.push('.');
184 }
185 _ => continue,
186 };
187 }
188 }
189 }
190 '_' | '+' => {
192 if ch == '+' {
193 if self.kind == ParseKind::Sem {
194 self.in_part = ParsePart::BuildSuffix;
195 } else {
196 continue;
197 }
198 } else if self.kind == ParseKind::Cal {
199 self.in_part = ParsePart::BuildSuffix;
200 } else {
201 continue;
202 }
203 }
204 ',' => {
206 self.is_and = true;
207 self.build_result()?;
208 self.reset_state();
209 }
210 ' ' => {
212 if self.in_part.is_prefix() {
213 } else {
215 self.is_and = true;
217 self.build_result()?;
218 self.reset_state();
219 }
220 }
221 _ => {
222 return Err(SpecError::UnknownParseChar(ch));
223 }
224 }
225 }
226
227 self.build_result()?;
228
229 let result = self.get_result();
230 let is_req = result.contains(',');
231
232 Ok((result, if is_req { ParseKind::Req } else { self.kind }))
233 }
234
235 fn get_result(&self) -> String {
236 self.results.join(",")
237 }
238
239 fn get_part<'p>(&self, value: &'p str) -> &'p str {
240 let value = value.trim_start_matches('0');
241
242 if value.is_empty() {
243 return "0";
244 }
245
246 value
247 }
248
249 fn build_result(&mut self) -> Result<(), SpecError> {
250 if self.in_part.is_prefix() {
251 return Ok(());
252 }
253
254 let mut output = String::new();
255 let was_calver = self.kind == ParseKind::Cal;
256
257 if self.req_op.is_empty() {
258 if self.minor_month.is_empty() || self.patch_day.is_empty() {
259 self.kind = ParseKind::Req;
260
261 if !self.is_and {
262 output.push('~');
263 }
264 }
265 } else {
266 self.kind = ParseKind::Req;
267 output.push_str(&self.req_op);
268 }
269
270 let separator = if self.kind == ParseKind::Cal && !self.is_and {
271 '-'
272 } else {
273 '.'
274 };
275
276 if was_calver {
278 let year = self.get_part(&self.major_year);
279
280 if year.len() < 4 {
281 let mut year: usize = year.parse().unwrap();
282 year += 2000;
283
284 output.push_str(&year.to_string());
285 } else {
286 output.push_str(year);
287 }
288 } else if self.major_year.is_empty() {
289 return Err(SpecError::MissingParseMajorPart);
290 } else {
291 output.push_str(self.get_part(&self.major_year));
292 }
293
294 if !self.minor_month.is_empty() {
296 output.push(separator);
297 output.push_str(self.get_part(&self.minor_month));
298 }
299
300 if !self.patch_day.is_empty() {
302 output.push(separator);
303 output.push_str(self.get_part(&self.patch_day));
304 }
305
306 if !self.pre_id.is_empty() {
308 output.push('-');
309 output.push_str(&self.pre_id);
310 }
311
312 if !self.build_id.is_empty() {
314 output.push('+');
315 output.push_str(&self.build_id);
316 }
317
318 self.results.push(output);
319
320 Ok(())
321 }
322
323 fn reset_state(&mut self) {
324 self.kind = ParseKind::Unknown;
325 self.in_part = ParsePart::Start;
326 self.req_op.truncate(0);
327 self.major_year.truncate(0);
328 self.minor_month.truncate(0);
329 self.patch_day.truncate(0);
330 self.pre_id.truncate(0);
331 self.build_id.truncate(0);
332 }
333}
334
335pub fn parse_multi(input: impl AsRef<str>) -> Result<Vec<String>, SpecError> {
339 let input = input.as_ref();
340 let mut results = vec![];
341
342 if input.contains("||") {
343 let mut parts = input.split("||").map(|p| p.trim()).collect::<Vec<_>>();
344
345 parts.sort_by(|a, d| compare(d, a));
347
348 for part in parts {
349 results.push(parse(part)?.0);
350 }
351 } else {
352 results.push(parse(input)?.0);
353 }
354
355 Ok(results)
356}
357
358pub fn parse(input: impl AsRef<str>) -> Result<(String, ParseKind), SpecError> {
363 UnresolvedParser::default().parse(input)
364}