1#[cfg(not(feature = "std"))]
2use alloc::{vec, vec::Vec};
3
4use core::fmt;
5use core::str::FromStr;
6
7use crate::error::SemverErrorKind;
8use crate::identifier::{BuildMetadata, PreRelease, validate_build_metadata};
9use crate::number::{MAX_SAFE_INTEGER, parse_nr};
10use crate::version::{Version, compare_core_and_prerelease};
11use crate::{MAX_LENGTH, SemverError};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub(crate) enum Operator {
20 LessThan,
22 LessThanOrEqual,
24 GreaterThan,
26 GreaterThanOrEqual,
28 Equal,
30}
31
32impl fmt::Display for Operator {
33 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34 f.write_str(match self {
35 Self::LessThan => "<",
36 Self::LessThanOrEqual => "<=",
37 Self::GreaterThan => ">",
38 Self::GreaterThanOrEqual => ">=",
39 Self::Equal => "=",
40 })
41 }
42}
43
44#[derive(Debug, Clone, PartialEq, Eq)]
45struct Comparator {
46 op: Operator,
47 version: Version,
48}
49
50impl Comparator {
51 fn test(&self, v: &Version) -> bool {
52 let cmp = compare_core_and_prerelease(v, &self.version);
53 match self.op {
54 Operator::Equal => cmp == core::cmp::Ordering::Equal,
55 Operator::GreaterThan => cmp == core::cmp::Ordering::Greater,
56 Operator::GreaterThanOrEqual => cmp != core::cmp::Ordering::Less,
57 Operator::LessThan => cmp == core::cmp::Ordering::Less,
58 Operator::LessThanOrEqual => cmp != core::cmp::Ordering::Greater,
59 }
60 }
61}
62
63impl fmt::Display for Comparator {
64 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65 match self.op {
66 Operator::Equal => write!(f, "{}", self.version),
67 Operator::LessThan
68 | Operator::LessThanOrEqual
69 | Operator::GreaterThan
70 | Operator::GreaterThanOrEqual => write!(f, "{}{}", self.op, self.version),
71 }
72 }
73}
74
75#[derive(Debug, Clone, PartialEq, Eq)]
76struct ComparatorSet {
77 comparators: Vec<Comparator>,
78}
79
80impl ComparatorSet {
81 fn test(&self, v: &Version) -> bool {
82 if self.comparators.is_empty() {
83 return v.pre_release.is_empty();
84 }
85
86 if v.pre_release.is_empty() {
87 for comparator in &self.comparators {
88 if !comparator.test(v) {
89 return false;
90 }
91 }
92 return true;
93 }
94
95 let mut has_matching_prerelease_tuple = false;
96 for comparator in &self.comparators {
97 if !comparator.test(v) {
98 return false;
99 }
100 let comparator_version = &comparator.version;
101 if !comparator_version.pre_release.is_empty()
102 && comparator_version.major == v.major
103 && comparator_version.minor == v.minor
104 && comparator_version.patch == v.patch
105 {
106 has_matching_prerelease_tuple = true;
107 }
108 }
109 has_matching_prerelease_tuple
110 }
111}
112
113#[derive(Debug, Clone)]
129pub struct Range {
130 set: ComparatorSets,
131}
132
133#[derive(Debug, Clone)]
134enum ComparatorSets {
135 One(ComparatorSet),
136 Many(Vec<ComparatorSet>),
137}
138
139impl ComparatorSets {
140 fn iter(&self) -> core::slice::Iter<'_, ComparatorSet> {
141 match self {
142 Self::One(set) => core::slice::from_ref(set).iter(),
143 Self::Many(sets) => sets.iter(),
144 }
145 }
146
147 #[cfg(test)]
148 fn len(&self) -> usize {
149 match self {
150 Self::One(_) => 1,
151 Self::Many(sets) => sets.len(),
152 }
153 }
154}
155
156impl Range {
157 pub fn parse(s: &str) -> Result<Self, SemverError> {
174 parse_range(s)
175 }
176
177 #[must_use]
194 pub fn satisfies(&self, version: &Version) -> bool {
195 for comparator_set in self.set.iter() {
196 if comparator_set.test(version) {
197 return true;
198 }
199 }
200 false
201 }
202}
203
204impl fmt::Display for Range {
205 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
206 for (i, cs) in self.set.iter().enumerate() {
207 if i > 0 {
208 f.write_str("||")?;
209 }
210 if cs.comparators.is_empty() {
211 f.write_str("*")?;
212 } else {
213 for (j, c) in cs.comparators.iter().enumerate() {
214 if j > 0 {
215 f.write_str(" ")?;
216 }
217 write!(f, "{c}")?;
218 }
219 }
220 }
221 Ok(())
222 }
223}
224
225impl FromStr for Range {
226 type Err = SemverError;
227 fn from_str(s: &str) -> Result<Self, Self::Err> {
228 parse_range(s)
229 }
230}
231
232#[derive(Debug, Clone)]
237struct Partial {
238 major: Option<u64>,
239 minor: Option<u64>,
240 patch: Option<u64>,
241 pre_release: PreRelease,
242}
243
244impl Partial {
245 fn floor(self) -> Version {
246 Version {
247 major: self.major.unwrap_or(0),
248 minor: self.minor.unwrap_or(0),
249 patch: self.patch.unwrap_or(0),
250 pre_release: self.pre_release,
251 build: BuildMetadata::default(),
252 }
253 }
254}
255
256fn parse_partial(s: &str) -> Result<Partial, SemverError> {
257 let s = s.trim();
258 let s = if has_fully_qualified_numeric_core_after_full_strip(s) {
259 s.strip_prefix(['v', '=']).unwrap_or(s)
260 } else {
261 s.trim_start_matches(['v', '='])
262 };
263 if let Some(partial) = parse_simple_partial(s) {
264 return Ok(partial);
265 }
266 let original_len = s.len();
267 let (s, pre_separator) = strip_build_metadata_and_find_prerelease(s)?;
268 if s.is_empty() || s.starts_with('.') {
269 return Err(SemverErrorKind::MissingVersionSegment.into());
270 }
271 if s.len() != original_len {
272 if let Some(partial) = parse_simple_partial(s) {
273 return Ok(partial);
274 }
275 }
276 let Some(pre_separator) = pre_separator else {
277 return Err(SemverErrorKind::InvalidNumber.into());
278 };
279 let bytes = s.as_bytes();
280 let version_end = pre_separator;
281 let version_core = &s[..version_end];
282 let pre_part = &s[pre_separator + 1..];
283
284 if version_core.is_empty() {
285 return Err(SemverErrorKind::MissingVersionSegment.into());
286 }
287 if bytes.get(version_end.wrapping_sub(1)) == Some(&b'.') {
288 return Err(SemverErrorKind::TrailingDot.into());
289 }
290
291 let (dot1, dot2) = find_component_dots(bytes, version_end, s)?;
292
293 let major_part = if let Some(end) = dot1 {
294 &s[..end]
295 } else {
296 version_core
297 };
298 let major = parse_xr(major_part)?;
299 let minor = match (dot1, dot2) {
300 (Some(start), Some(end)) => parse_xr(&s[start + 1..end])?,
301 (Some(start), None) => parse_xr(&s[start + 1..version_end])?,
302 (None, _) => None,
303 };
304 let patch = match dot2 {
305 Some(start) => parse_xr(&s[start + 1..version_end])?,
306 None => None,
307 };
308
309 let pre_release = if pre_part.is_empty() || pre_part.ends_with('.') {
310 return Err(SemverErrorKind::EmptySegment.into());
311 } else if dot1.is_some()
312 && dot2.is_some()
313 && (major.is_none() || minor.is_none() || patch.is_none())
314 {
315 PreRelease::new(pre_part)?;
316 PreRelease::default()
317 } else {
318 if minor.is_none() || patch.is_none() {
319 return Err(SemverErrorKind::MissingVersionSegment.into());
320 }
321 PreRelease::new(pre_part)?
322 };
323
324 Ok(Partial {
325 major,
326 minor,
327 patch,
328 pre_release,
329 })
330}
331
332fn strip_build_metadata(s: &str) -> Result<&str, SemverError> {
333 let Some(plus) = s.find('+') else {
334 return Ok(s);
335 };
336 let build = &s[plus + 1..];
337 if build.is_empty() {
338 return Err(SemverErrorKind::EmptySegment.into());
339 }
340 validate_build_metadata(build)?;
341 Ok(&s[..plus])
342}
343
344fn strip_build_metadata_and_find_prerelease(s: &str) -> Result<(&str, Option<usize>), SemverError> {
345 let mut pre_separator = None;
346 for (pos, byte) in s.bytes().enumerate() {
347 match byte {
348 b'-' if pre_separator.is_none() => pre_separator = Some(pos),
349 b'+' => {
350 let build = &s[pos + 1..];
351 if build.is_empty() {
352 return Err(SemverErrorKind::EmptySegment.into());
353 }
354 validate_build_metadata(build)?;
355 return Ok((&s[..pos], pre_separator));
356 }
357 _ => {}
358 }
359 }
360 Ok((s, pre_separator))
361}
362
363fn parse_simple_partial(s: &str) -> Option<Partial> {
364 let bytes = s.as_bytes();
365 let (major, mut pos) = parse_simple_component(bytes, 0)?;
366 let mut minor = None;
367 let mut patch = None;
368
369 if pos < bytes.len() {
370 if bytes[pos] != b'.' {
371 return None;
372 }
373 (minor, pos) = parse_simple_component(bytes, pos + 1)?;
374 }
375
376 if pos < bytes.len() {
377 if bytes[pos] != b'.' {
378 return None;
379 }
380 (patch, pos) = parse_simple_component(bytes, pos + 1)?;
381 }
382
383 if pos != bytes.len() {
384 return None;
385 }
386
387 Some(Partial {
388 major,
389 minor,
390 patch,
391 pre_release: PreRelease::default(),
392 })
393}
394
395fn parse_simple_component(bytes: &[u8], start: usize) -> Option<(Option<u64>, usize)> {
396 match bytes.get(start).copied()? {
397 b'x' | b'X' | b'*' => Some((None, start + 1)),
398 first @ b'0'..=b'9' => {
399 if first == b'0' && bytes.get(start + 1).is_some_and(u8::is_ascii_digit) {
400 return None;
401 }
402
403 let mut pos = start;
404 let mut value = 0u64;
405 while let Some(digit @ b'0'..=b'9') = bytes.get(pos).copied() {
406 if pos - start == 16 {
407 return None;
408 }
409 value = value * 10 + u64::from(digit - b'0');
410 pos += 1;
411 }
412 (value <= MAX_SAFE_INTEGER).then_some((Some(value), pos))
413 }
414 _ => None,
415 }
416}
417
418fn parse_xr(s: &str) -> Result<Option<u64>, SemverError> {
419 match s {
420 "" | "*" | "x" | "X" => Ok(None),
421 _ => Ok(Some(parse_nr(s)?)),
422 }
423}
424
425fn has_fully_qualified_numeric_core_after_full_strip(s: &str) -> bool {
426 if s.strip_prefix(['v', '=']).is_none() {
427 return false;
428 }
429 let fully_stripped = s.trim_start_matches(['v', '=']);
430 let core_end = fully_stripped
431 .find(['-', '+'])
432 .unwrap_or(fully_stripped.len());
433 let core = &fully_stripped[..core_end];
434 let mut parts = core.split('.');
435 let (Some(major), Some(minor), Some(patch), None) =
436 (parts.next(), parts.next(), parts.next(), parts.next())
437 else {
438 return false;
439 };
440 [major, minor, patch]
441 .into_iter()
442 .all(|part| !part.is_empty() && part.bytes().all(|b| b.is_ascii_digit()))
443}
444
445fn find_component_dots(
450 bytes: &[u8],
451 version_end: usize,
452 _raw: &str,
453) -> Result<(Option<usize>, Option<usize>), SemverError> {
454 let mut first = None;
455 let mut second = None;
456 let mut pos = 0;
457 while pos < version_end {
458 if bytes[pos] == b'.' {
459 if pos == 0 || bytes[pos - 1] == b'.' {
460 return Err(SemverErrorKind::EmptySegment.into());
461 }
462 if first.is_none() {
463 first = Some(pos);
464 } else if second.is_none() {
465 second = Some(pos);
466 } else {
467 return Err(SemverErrorKind::UnexpectedDot.into());
468 }
469 }
470 pos += 1;
471 }
472 Ok((first, second))
473}
474
475fn version_with_pre_release(
476 major: u64,
477 minor: u64,
478 patch: u64,
479 pre_release: PreRelease,
480) -> Version {
481 if pre_release.is_empty() {
482 return Version::new(major, minor, patch);
483 }
484
485 Version {
486 major,
487 minor,
488 patch,
489 pre_release,
490 build: BuildMetadata::default(),
491 }
492}
493
494const fn comparator_gte(ver: Version) -> Comparator {
495 Comparator {
496 op: Operator::GreaterThanOrEqual,
497 version: ver,
498 }
499}
500const fn comparator_gt(ver: Version) -> Comparator {
501 Comparator {
502 op: Operator::GreaterThan,
503 version: ver,
504 }
505}
506const fn comparator_lte(ver: Version) -> Comparator {
507 Comparator {
508 op: Operator::LessThanOrEqual,
509 version: ver,
510 }
511}
512const fn comparator_lt(ver: Version) -> Comparator {
513 Comparator {
514 op: Operator::LessThan,
515 version: ver,
516 }
517}
518const fn comparator_eq(ver: Version) -> Comparator {
519 Comparator {
520 op: Operator::Equal,
521 version: ver,
522 }
523}
524
525fn comparator_lt_upper_bound(major: u64, minor: u64, patch: u64) -> Comparator {
526 comparator_lt(version_with_pre_release(
527 major,
528 minor,
529 patch,
530 PreRelease::zero(),
531 ))
532}
533
534fn next_component(value: u64) -> Result<u64, SemverError> {
535 if value >= MAX_SAFE_INTEGER {
536 return Err(SemverErrorKind::MaxSafeIntegerExceeded.into());
537 }
538 Ok(value + 1)
539}
540
541fn expand_tilde_into(out: &mut Vec<Comparator>, p: Partial) -> Result<(), SemverError> {
547 match (p.major, p.minor, p.patch) {
548 (None, _, _) => {}
549 (Some(0), None, _) => push_canonical_comparator(out, comparator_lt_upper_bound(1, 0, 0)),
550 (Some(maj), None, _) => {
551 push_canonical_comparator(
552 out,
553 comparator_gte(version_with_pre_release(maj, 0, 0, PreRelease::default())),
554 );
555 push_canonical_comparator(out, comparator_lt_upper_bound(next_component(maj)?, 0, 0));
556 }
557 (Some(maj), Some(mnr), None) => {
558 push_canonical_comparator(
559 out,
560 comparator_gte(version_with_pre_release(maj, mnr, 0, PreRelease::default())),
561 );
562 push_canonical_comparator(out, comparator_lt_upper_bound(maj, next_component(mnr)?, 0));
563 }
564 (Some(maj), Some(mnr), Some(patch)) => {
565 let floor = version_with_pre_release(maj, mnr, patch, p.pre_release);
566 push_canonical_comparator(out, comparator_gte(floor));
567 push_canonical_comparator(out, comparator_lt_upper_bound(maj, next_component(mnr)?, 0));
568 }
569 }
570 Ok(())
571}
572
573#[cfg(test)]
574fn expand_tilde(p: Partial) -> Result<Vec<Comparator>, SemverError> {
575 let mut out = Vec::with_capacity(2);
576 expand_tilde_into(&mut out, p)?;
577 Ok(out)
578}
579
580fn expand_caret_into(out: &mut Vec<Comparator>, p: Partial) -> Result<(), SemverError> {
582 match (p.major, p.minor, p.patch) {
583 (None, _, _) => {}
584 (Some(0), None, _) => push_canonical_comparator(out, comparator_lt_upper_bound(1, 0, 0)),
585 (Some(maj), None, _) => {
586 push_canonical_comparator(
587 out,
588 comparator_gte(version_with_pre_release(maj, 0, 0, PreRelease::default())),
589 );
590 push_canonical_comparator(out, comparator_lt_upper_bound(next_component(maj)?, 0, 0));
591 }
592 (Some(maj), Some(mnr), None) => {
593 if maj > 0 {
594 push_canonical_comparator(
595 out,
596 comparator_gte(version_with_pre_release(maj, mnr, 0, PreRelease::default())),
597 );
598 push_canonical_comparator(
599 out,
600 comparator_lt_upper_bound(next_component(maj)?, 0, 0),
601 );
602 } else if mnr > 0 {
603 push_canonical_comparator(
604 out,
605 comparator_gte(version_with_pre_release(0, mnr, 0, PreRelease::default())),
606 );
607 push_canonical_comparator(
608 out,
609 comparator_lt_upper_bound(0, next_component(mnr)?, 0),
610 );
611 } else {
612 push_canonical_comparator(
613 out,
614 comparator_gte(version_with_pre_release(0, 0, 0, PreRelease::default())),
615 );
616 push_canonical_comparator(out, comparator_lt_upper_bound(0, 1, 0));
617 }
618 }
619 (Some(maj), Some(mnr), Some(patch)) => {
620 let floor = version_with_pre_release(maj, mnr, patch, p.pre_release);
621 push_canonical_comparator(out, comparator_gte(floor));
622 if maj > 0 {
623 push_canonical_comparator(
624 out,
625 comparator_lt_upper_bound(next_component(maj)?, 0, 0),
626 );
627 } else if mnr > 0 {
628 push_canonical_comparator(
629 out,
630 comparator_lt_upper_bound(0, next_component(mnr)?, 0),
631 );
632 } else {
633 push_canonical_comparator(
634 out,
635 comparator_lt_upper_bound(0, 0, next_component(patch)?),
636 );
637 }
638 }
639 }
640 Ok(())
641}
642
643#[cfg(test)]
644fn expand_caret(p: Partial) -> Result<Vec<Comparator>, SemverError> {
645 let mut out = Vec::with_capacity(2);
646 expand_caret_into(&mut out, p)?;
647 Ok(out)
648}
649
650fn expand_primitive_into(
652 out: &mut Vec<Comparator>,
653 op: Option<Operator>,
654 p: Partial,
655) -> Result<(), SemverError> {
656 if (p.major.is_none() && p.minor.is_some()) || (p.minor.is_none() && p.patch.is_some()) {
657 return Err(SemverErrorKind::MissingVersionSegment.into());
658 }
659 match op {
660 None | Some(Operator::Equal) => expand_equal_primitive(out, p)?,
661 Some(Operator::GreaterThan) => expand_greater_than_primitive(out, p)?,
662 Some(Operator::GreaterThanOrEqual) => expand_greater_than_or_equal_primitive(out, p),
663 Some(Operator::LessThan) => expand_less_than_primitive(out, p),
664 Some(Operator::LessThanOrEqual) => expand_less_than_or_equal_primitive(out, p)?,
665 }
666 Ok(())
667}
668
669fn version_from_partial(p: Partial, major: u64, minor: u64, patch: u64) -> Version {
670 version_with_pre_release(major, minor, patch, p.pre_release)
671}
672
673fn expand_equal_primitive(out: &mut Vec<Comparator>, p: Partial) -> Result<(), SemverError> {
674 match (p.major, p.minor, p.patch) {
675 (None, _, _) => {}
676 (Some(0), None, _) => {
677 push_canonical_comparator(out, comparator_lt_upper_bound(1, 0, 0));
678 }
679 (Some(maj), None, _) => {
680 push_canonical_comparator(
681 out,
682 comparator_gte(version_with_pre_release(maj, 0, 0, PreRelease::default())),
683 );
684 push_canonical_comparator(out, comparator_lt_upper_bound(next_component(maj)?, 0, 0));
685 }
686 (Some(maj), Some(mnr), None) => {
687 push_canonical_comparator(
688 out,
689 comparator_gte(version_with_pre_release(maj, mnr, 0, PreRelease::default())),
690 );
691 push_canonical_comparator(out, comparator_lt_upper_bound(maj, next_component(mnr)?, 0));
692 }
693 (Some(maj), Some(mnr), Some(patch)) => {
694 push_canonical_comparator(out, comparator_eq(version_from_partial(p, maj, mnr, patch)));
695 }
696 }
697 Ok(())
698}
699
700fn expand_greater_than_primitive(out: &mut Vec<Comparator>, p: Partial) -> Result<(), SemverError> {
701 match (p.major, p.minor, p.patch) {
702 (None, _, _) => push_canonical_comparator(out, comparator_lt_upper_bound(0, 0, 0)),
703 (Some(maj), None, _) => {
704 push_canonical_comparator(
705 out,
706 comparator_gte(version_with_pre_release(
707 next_component(maj)?,
708 0,
709 0,
710 PreRelease::default(),
711 )),
712 );
713 }
714 (Some(maj), Some(mnr), None) => {
715 push_canonical_comparator(
716 out,
717 comparator_gte(version_with_pre_release(
718 maj,
719 next_component(mnr)?,
720 0,
721 PreRelease::default(),
722 )),
723 );
724 }
725 (Some(maj), Some(mnr), Some(patch)) => {
726 push_canonical_comparator(out, comparator_gt(version_from_partial(p, maj, mnr, patch)));
727 }
728 }
729 Ok(())
730}
731
732fn expand_greater_than_or_equal_primitive(out: &mut Vec<Comparator>, p: Partial) {
733 match (p.major, p.minor, p.patch) {
734 (None, _, _) | (Some(0), None, _) => {}
735 (Some(maj), None, _) => {
736 push_canonical_comparator(
737 out,
738 comparator_gte(version_with_pre_release(maj, 0, 0, PreRelease::default())),
739 );
740 }
741 (Some(maj), Some(mnr), None) => {
742 push_canonical_comparator(
743 out,
744 comparator_gte(version_with_pre_release(maj, mnr, 0, PreRelease::default())),
745 );
746 }
747 (Some(maj), Some(mnr), Some(patch)) => {
748 push_canonical_comparator(
749 out,
750 comparator_gte(version_from_partial(p, maj, mnr, patch)),
751 );
752 }
753 }
754}
755
756fn expand_less_than_primitive(out: &mut Vec<Comparator>, p: Partial) {
757 match (p.major, p.minor, p.patch) {
758 (None, _, _) => push_canonical_comparator(out, comparator_lt_upper_bound(0, 0, 0)),
759 (Some(maj), None, _) => {
760 push_canonical_comparator(out, comparator_lt_upper_bound(maj, 0, 0));
761 }
762 (Some(maj), Some(mnr), None) => {
763 push_canonical_comparator(out, comparator_lt_upper_bound(maj, mnr, 0));
764 }
765 (Some(maj), Some(mnr), Some(patch)) => {
766 push_canonical_comparator(out, comparator_lt(version_from_partial(p, maj, mnr, patch)));
767 }
768 }
769}
770
771fn expand_less_than_or_equal_primitive(
772 out: &mut Vec<Comparator>,
773 p: Partial,
774) -> Result<(), SemverError> {
775 match (p.major, p.minor, p.patch) {
776 (None, _, _) => {}
777 (Some(maj), None, _) => {
778 push_canonical_comparator(out, comparator_lt_upper_bound(next_component(maj)?, 0, 0));
779 }
780 (Some(maj), Some(mnr), None) => {
781 push_canonical_comparator(out, comparator_lt_upper_bound(maj, next_component(mnr)?, 0));
782 }
783 (Some(maj), Some(mnr), Some(patch)) => {
784 push_canonical_comparator(
785 out,
786 comparator_lte(version_from_partial(p, maj, mnr, patch)),
787 );
788 }
789 }
790 Ok(())
791}
792
793#[cfg(test)]
794fn expand_primitive(op: Option<Operator>, p: Partial) -> Result<Vec<Comparator>, SemverError> {
795 let mut out = Vec::with_capacity(2);
796 expand_primitive_into(&mut out, op, p)?;
797 Ok(out)
798}
799
800fn expand_hyphen_into(
802 out: &mut Vec<Comparator>,
803 a: Partial,
804 b: Partial,
805) -> Result<(), SemverError> {
806 push_canonical_comparator(out, comparator_gte(a.floor()));
807 match (b.major, b.minor, b.patch) {
808 (None, _, _) => {}
809 (Some(maj), None, _) => {
810 push_canonical_comparator(out, comparator_lt_upper_bound(next_component(maj)?, 0, 0));
811 }
812 (Some(maj), Some(mnr), None) => {
813 push_canonical_comparator(out, comparator_lt_upper_bound(maj, next_component(mnr)?, 0));
814 }
815 (Some(maj), Some(mnr), Some(patch)) => {
816 let ver = version_with_pre_release(maj, mnr, patch, b.pre_release);
817 push_canonical_comparator(out, comparator_lte(ver));
818 }
819 }
820 Ok(())
821}
822
823fn expand_hyphen(a: Partial, b: Partial) -> Result<Vec<Comparator>, SemverError> {
824 let mut out = Vec::with_capacity(2);
825 expand_hyphen_into(&mut out, a, b)?;
826 Ok(out)
827}
828
829fn parse_range(s: &str) -> Result<Range, SemverError> {
834 let s = s.trim();
835 let exceeds_max_length =
836 s.len() > MAX_LENGTH && range_len_without_build_metadata(s) > MAX_LENGTH && {
837 let trimmed_prefix = s.trim_start_matches(['v', '=', '^', '~', '>', '<']);
838 trimmed_prefix.len() > MAX_LENGTH
839 && range_len_without_build_metadata(trimmed_prefix) > MAX_LENGTH
840 };
841
842 let bytes = s.as_bytes();
843 let group_count = count_or_groups(bytes);
844 if group_count == 1 {
845 let comparator_set = parse_comparator_set(s)?;
846 if !comparator_set.comparators.is_empty() && exceeds_max_length {
847 return Err(SemverErrorKind::MaxLengthExceeded.into());
848 }
849 return Ok(Range {
850 set: ComparatorSets::One(comparator_set),
851 });
852 }
853
854 let mut set = Vec::with_capacity(group_count);
855 let mut start = 0;
856 let mut i = 0;
857 while i < bytes.len() {
858 if i + 1 < bytes.len() && bytes[i] == b'|' && bytes[i + 1] == b'|' {
859 let comparator_set = parse_comparator_set(s[start..i].trim())?;
860 set.push(comparator_set);
861 i += 2;
862 start = i;
863 } else {
864 i += 1;
865 }
866 }
867 set.push(parse_comparator_set(s[start..].trim())?);
868
869 let has_unbounded_set = set
870 .iter()
871 .any(|comparator_set| comparator_set.comparators.is_empty());
872
873 if has_unbounded_set && set.len() > 1 && exceeds_max_length {
874 return Err(SemverErrorKind::MaxLengthExceeded.into());
875 }
876
877 if has_unbounded_set {
878 return Ok(Range {
879 set: ComparatorSets::One(ComparatorSet {
880 comparators: vec![],
881 }),
882 });
883 }
884
885 if exceeds_max_length {
886 return Err(SemverErrorKind::MaxLengthExceeded.into());
887 }
888
889 set.dedup();
890
891 if set.len() == 1 {
892 let comparator_set = set.remove(0);
893 return Ok(Range {
894 set: ComparatorSets::One(comparator_set),
895 });
896 }
897
898 Ok(Range {
899 set: ComparatorSets::Many(set),
900 })
901}
902
903fn range_len_without_build_metadata(s: &str) -> usize {
904 let bytes = s.as_bytes();
905 let mut len = 0;
906 let mut pos = 0;
907 while pos < bytes.len() {
908 if bytes[pos] == b'+' {
909 pos += 1;
910 while pos < bytes.len()
911 && matches!(bytes[pos], b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z' | b'-' | b'.')
912 {
913 pos += 1;
914 }
915 } else {
916 len += 1;
917 pos += 1;
918 }
919 }
920 len
921}
922
923fn parse_comparator_set(s: &str) -> Result<ComparatorSet, SemverError> {
924 if s.is_empty() || s == "*" {
925 return Ok(ComparatorSet {
926 comparators: vec![],
927 });
928 }
929
930 let bytes = s.as_bytes();
931 if !bytes.iter().any(u8::is_ascii_whitespace) {
932 let mut comparators = Vec::with_capacity(2);
933 parse_token_into(&mut comparators, s)?;
934 return Ok(ComparatorSet { comparators });
935 }
936
937 if let Some(comps) = try_hyphen(s)? {
938 return Ok(ComparatorSet { comparators: comps });
939 }
940
941 let mut all = Vec::with_capacity(count_whitespace_tokens(bytes).saturating_mul(2));
942 let mut pos = 0;
943 while let Some(t) = next_whitespace_token(s, bytes, &mut pos) {
944 let is_op_only = matches!(t, ">" | ">=" | "<" | "<=" | "=" | "^" | "~" | "~=" | "~>");
945 if is_op_only {
946 if let Some(next) = next_whitespace_token(s, bytes, &mut pos) {
947 let mut buf = [0u8; 258];
948 let op = t.as_bytes();
949 let ver = strip_build_metadata(next)?.as_bytes();
950 let len = op.len() + ver.len();
951 if len > buf.len() {
952 return Err(SemverErrorKind::MaxLengthExceeded.into());
953 }
954 buf[..op.len()].copy_from_slice(op);
955 buf[op.len()..len].copy_from_slice(ver);
956 let merged = unsafe { core::str::from_utf8_unchecked(&buf[..len]) };
959 parse_token_into(&mut all, merged)?;
960 } else {
961 parse_token_into(&mut all, t)?;
962 }
963 } else {
964 parse_token_into(&mut all, t)?;
965 }
966 }
967 Ok(ComparatorSet { comparators: all })
968}
969
970fn next_whitespace_token<'a>(s: &'a str, bytes: &[u8], pos: &mut usize) -> Option<&'a str> {
971 while *pos < bytes.len() && bytes[*pos].is_ascii_whitespace() {
972 *pos += 1;
973 }
974 if *pos >= bytes.len() {
975 return None;
976 }
977 let start = *pos;
978 while *pos < bytes.len() && !bytes[*pos].is_ascii_whitespace() {
979 *pos += 1;
980 }
981 Some(&s[start..*pos])
982}
983
984fn count_or_groups(bytes: &[u8]) -> usize {
985 let mut count = 1;
986 let mut pos = 0;
987 while pos + 1 < bytes.len() {
988 if bytes[pos] == b'|' && bytes[pos + 1] == b'|' {
989 count += 1;
990 pos += 2;
991 } else {
992 pos += 1;
993 }
994 }
995 count
996}
997
998fn count_whitespace_tokens(bytes: &[u8]) -> usize {
999 let mut count = 0;
1000 let mut pos = 0;
1001 while pos < bytes.len() {
1002 while pos < bytes.len() && bytes[pos].is_ascii_whitespace() {
1003 pos += 1;
1004 }
1005 if pos >= bytes.len() {
1006 break;
1007 }
1008 count += 1;
1009 while pos < bytes.len() && !bytes[pos].is_ascii_whitespace() {
1010 pos += 1;
1011 }
1012 }
1013 count
1014}
1015
1016fn try_hyphen(s: &str) -> Result<Option<Vec<Comparator>>, SemverError> {
1018 let bytes = s.as_bytes();
1019 let mut i = 0;
1020 while i + 2 < bytes.len() {
1021 if bytes[i] == b' ' && bytes[i + 1] == b'-' && bytes[i + 2] == b' ' {
1022 let left = s[..i].trim();
1023 let right = s[i + 3..].trim();
1024 let is_op = |c: char| matches!(c, '>' | '<' | '=' | '~' | '^');
1025 if !left.starts_with(is_op) && !right.starts_with(is_op) {
1026 let a = parse_partial(left)?;
1027 let b = parse_partial(right)?;
1028 return Ok(Some(expand_hyphen(a, b)?));
1029 }
1030 }
1031 i += 1;
1032 }
1033 Ok(None)
1034}
1035
1036fn parse_token_into(all: &mut Vec<Comparator>, s: &str) -> Result<(), SemverError> {
1037 let s = s.trim();
1038 if s.is_empty() || s == "*" {
1039 return Ok(());
1040 }
1041 if s.starts_with('+') {
1042 strip_build_metadata(s)?;
1043 return Ok(());
1044 }
1045
1046 if let Some(rest) = s.strip_prefix('~') {
1047 let rest = rest.trim_start_matches(['=', '>']); return expand_tilde_into(all, parse_required_partial(rest, "~")?);
1049 }
1050 if let Some(rest) = s.strip_prefix('^') {
1051 return expand_caret_into(all, parse_required_partial(rest, "^")?);
1052 }
1053 if let Some(rest) = s.strip_prefix(">=") {
1054 return expand_primitive_into(
1055 all,
1056 Some(Operator::GreaterThanOrEqual),
1057 parse_required_partial(rest, ">=")?,
1058 );
1059 }
1060 if let Some(rest) = s.strip_prefix("<=") {
1061 return expand_primitive_into(
1062 all,
1063 Some(Operator::LessThanOrEqual),
1064 parse_required_partial(rest, "<=")?,
1065 );
1066 }
1067 if let Some(rest) = s.strip_prefix('>') {
1068 return expand_primitive_into(
1069 all,
1070 Some(Operator::GreaterThan),
1071 parse_required_partial(rest, ">")?,
1072 );
1073 }
1074 if let Some(rest) = s.strip_prefix('<') {
1075 return expand_primitive_into(
1076 all,
1077 Some(Operator::LessThan),
1078 parse_required_partial(rest, "<")?,
1079 );
1080 }
1081 if let Some(rest) = s.strip_prefix('=') {
1082 return expand_primitive_into(
1083 all,
1084 Some(Operator::Equal),
1085 parse_required_partial(rest, "=")?,
1086 );
1087 }
1088
1089 expand_primitive_into(all, None, parse_partial(s)?)
1090}
1091
1092fn parse_required_partial(s: &str, operator: &'static str) -> Result<Partial, SemverError> {
1093 let s = s.trim();
1094 if s.is_empty() {
1095 return Err(SemverErrorKind::MissingVersionAfterOperator(operator).into());
1096 }
1097 parse_partial(s)
1098}
1099
1100fn push_canonical_comparator(all: &mut Vec<Comparator>, new: Comparator) {
1101 if is_impossible_comparator(&new) {
1102 all.clear();
1103 all.push(new);
1104 return;
1105 }
1106 for existing in &mut *all {
1107 if is_impossible_comparator(existing) {
1108 return;
1109 }
1110 match (existing.op, new.op) {
1111 (
1112 Operator::LessThan | Operator::LessThanOrEqual,
1113 Operator::LessThan | Operator::LessThanOrEqual,
1114 ) if existing.version.major == new.version.major
1115 && existing.version.minor == new.version.minor
1116 && existing.version.patch == new.version.patch =>
1117 {
1118 let ordering = compare_core_and_prerelease(&existing.version, &new.version);
1119 if ordering == core::cmp::Ordering::Greater
1120 || (ordering == core::cmp::Ordering::Equal
1121 && existing.op == Operator::LessThanOrEqual
1122 && new.op == Operator::LessThan)
1123 {
1124 *existing = new;
1125 }
1126 return;
1127 }
1128 (Operator::Equal, Operator::Equal)
1129 if compare_core_and_prerelease(&existing.version, &new.version)
1130 == core::cmp::Ordering::Equal =>
1131 {
1132 return;
1133 }
1134 _ => {}
1135 }
1136 }
1137 all.push(new);
1138}
1139
1140fn is_impossible_comparator(comparator: &Comparator) -> bool {
1141 comparator.op == Operator::LessThan
1142 && comparator.version.major == 0
1143 && comparator.version.minor == 0
1144 && comparator.version.patch == 0
1145 && comparator.version.pre_release == PreRelease::zero()
1146}
1147
1148#[cfg(test)]
1153mod tests {
1154 #[cfg(not(feature = "std"))]
1155 use alloc::string::ToString;
1156
1157 use super::*;
1158
1159 #[test]
1160 fn try_hyphen_rejects_non_hyphen_forms() {
1161 assert!(try_hyphen(">=1.0.0 - 2.0.0").unwrap().is_none());
1162 assert!(try_hyphen("1.0.0 - <=2.0.0").unwrap().is_none());
1163 assert!(try_hyphen("1.2.3").unwrap().is_none());
1164 assert!(try_hyphen("1.2.3 -").unwrap().is_none());
1165 assert!(try_hyphen("- 1.2.3").unwrap().is_none());
1166 }
1167
1168 #[test]
1171 fn operator_display() {
1172 assert_eq!(Operator::LessThan.to_string(), "<");
1173 assert_eq!(Operator::LessThanOrEqual.to_string(), "<=");
1174 assert_eq!(Operator::GreaterThan.to_string(), ">");
1175 assert_eq!(Operator::GreaterThanOrEqual.to_string(), ">=");
1176 assert_eq!(Operator::Equal.to_string(), "=");
1177 }
1178
1179 #[test]
1180 fn helper_count_and_expand_tilde_caret_coverage() {
1181 assert_eq!(parse_partial("1.2").unwrap().minor, Some(2));
1182 assert_eq!(count_whitespace_tokens(b""), 0);
1183 assert_eq!(count_whitespace_tokens(b">=1.0.0 <2.0.0"), 2);
1184 assert_eq!(count_whitespace_tokens(b" >=1.0.0 <2.0.0 "), 2);
1185
1186 assert_eq!(expand_tilde(parse_partial("1").unwrap()).unwrap().len(), 2);
1187 assert_eq!(
1188 expand_tilde(parse_partial("1.2").unwrap()).unwrap().len(),
1189 2
1190 );
1191
1192 assert_eq!(expand_caret(parse_partial("1").unwrap()).unwrap().len(), 2);
1193 assert_eq!(
1194 expand_caret(parse_partial("1.2").unwrap()).unwrap().len(),
1195 2
1196 );
1197 assert_eq!(
1198 expand_caret(parse_partial("1.2.3").unwrap()).unwrap().len(),
1199 2
1200 );
1201 assert_eq!(
1202 expand_caret(parse_partial("0.2.3").unwrap()).unwrap().len(),
1203 2
1204 );
1205 }
1206
1207 #[test]
1208 fn helper_build_metadata_stripping_and_length_coverage() {
1209 assert!(has_fully_qualified_numeric_core_after_full_strip("v1.2.3"));
1210 assert!(!has_fully_qualified_numeric_core_after_full_strip("v1.2.x"));
1211 assert!(!has_fully_qualified_numeric_core_after_full_strip("v1..3"));
1212 assert!(!has_fully_qualified_numeric_core_after_full_strip("v1.2"));
1213 assert_eq!(strip_build_metadata("1.2.3+A0-z.9").unwrap(), "1.2.3");
1214 assert_eq!(strip_build_metadata("1.2.3").unwrap(), "1.2.3");
1215 assert!(strip_build_metadata("1.2.3+").is_err());
1216 assert!(strip_build_metadata("1.2.3+bad!").is_err());
1217 assert_eq!(
1218 strip_build_metadata_and_find_prerelease("1.2.3-alpha-1+build").unwrap(),
1219 ("1.2.3-alpha-1", Some(5))
1220 );
1221 assert_eq!(
1222 strip_build_metadata_and_find_prerelease("1.2.3").unwrap(),
1223 ("1.2.3", None)
1224 );
1225 assert!(strip_build_metadata_and_find_prerelease("1.2.3+").is_err());
1226 assert!(strip_build_metadata_and_find_prerelease("1.2.3+bad!").is_err());
1227 assert_eq!(range_len_without_build_metadata("1.2.3+A0-z.9"), 5);
1228 assert_eq!(range_len_without_build_metadata("1.2.3+!"), 6);
1229 }
1230
1231 #[test]
1232 fn helper_expand_primitive_equal_coverage() {
1233 assert_eq!(
1234 expand_primitive(None, parse_partial("1").unwrap())
1235 .unwrap()
1236 .len(),
1237 2
1238 );
1239 assert_eq!(
1240 expand_primitive(None, parse_partial("1.2").unwrap())
1241 .unwrap()
1242 .len(),
1243 2
1244 );
1245 assert_eq!(
1246 expand_primitive(None, parse_partial("0").unwrap())
1247 .unwrap()
1248 .len(),
1249 1
1250 );
1251 assert_eq!(
1252 expand_primitive(None, parse_partial("1.2.3-alpha").unwrap())
1253 .unwrap()
1254 .len(),
1255 1
1256 );
1257 }
1258
1259 #[test]
1260 fn helper_expand_primitive_greater_coverage() {
1261 assert_eq!(
1262 expand_primitive(Some(Operator::GreaterThan), parse_partial("1").unwrap())
1263 .unwrap()
1264 .len(),
1265 1
1266 );
1267 assert_eq!(
1268 expand_primitive(Some(Operator::GreaterThan), parse_partial("1.2").unwrap())
1269 .unwrap()
1270 .len(),
1271 1
1272 );
1273 assert_eq!(
1274 expand_primitive(Some(Operator::GreaterThan), parse_partial("*").unwrap())
1275 .unwrap()
1276 .len(),
1277 1
1278 );
1279 assert_eq!(
1280 expand_primitive(
1281 Some(Operator::GreaterThan),
1282 parse_partial("1.2.3-alpha").unwrap()
1283 )
1284 .unwrap()
1285 .len(),
1286 1
1287 );
1288 }
1289
1290 #[test]
1291 fn helper_expand_primitive_greater_equal_coverage() {
1292 assert_eq!(
1293 expand_primitive(
1294 Some(Operator::GreaterThanOrEqual),
1295 parse_partial("1").unwrap()
1296 )
1297 .unwrap()
1298 .len(),
1299 1
1300 );
1301 assert_eq!(
1302 expand_primitive(
1303 Some(Operator::GreaterThanOrEqual),
1304 parse_partial("1.2").unwrap()
1305 )
1306 .unwrap()
1307 .len(),
1308 1
1309 );
1310 assert_eq!(
1311 expand_primitive(
1312 Some(Operator::GreaterThanOrEqual),
1313 parse_partial("*").unwrap()
1314 )
1315 .unwrap()
1316 .len(),
1317 0
1318 );
1319 assert_eq!(
1320 expand_primitive(
1321 Some(Operator::GreaterThanOrEqual),
1322 parse_partial("1.2.3-alpha").unwrap()
1323 )
1324 .unwrap()
1325 .len(),
1326 1
1327 );
1328 }
1329
1330 #[test]
1331 fn helper_expand_primitive_less_coverage() {
1332 assert_eq!(
1333 expand_primitive(Some(Operator::LessThan), parse_partial("1").unwrap())
1334 .unwrap()
1335 .len(),
1336 1
1337 );
1338 assert_eq!(
1339 expand_primitive(
1340 Some(Operator::LessThan),
1341 parse_partial("1.2.3-alpha").unwrap()
1342 )
1343 .unwrap()
1344 .len(),
1345 1
1346 );
1347 }
1348
1349 #[test]
1350 fn helper_expand_primitive_less_equal_coverage() {
1351 assert_eq!(
1352 expand_primitive(Some(Operator::LessThanOrEqual), parse_partial("1").unwrap())
1353 .unwrap()
1354 .len(),
1355 1
1356 );
1357 assert_eq!(
1358 expand_primitive(
1359 Some(Operator::LessThanOrEqual),
1360 parse_partial("1.2").unwrap()
1361 )
1362 .unwrap()
1363 .len(),
1364 1
1365 );
1366 assert_eq!(
1367 expand_primitive(
1368 Some(Operator::LessThanOrEqual),
1369 parse_partial("1.2.3-alpha").unwrap()
1370 )
1371 .unwrap()
1372 .len(),
1373 1
1374 );
1375 }
1376
1377 #[test]
1378 fn helper_expand_hyphen_and_parse_range_coverage() {
1379 assert_eq!(
1380 expand_hyphen(parse_partial("1.0.0").unwrap(), parse_partial("2").unwrap())
1381 .unwrap()
1382 .len(),
1383 2
1384 );
1385 assert_eq!(
1386 expand_hyphen(
1387 parse_partial("1.0.0").unwrap(),
1388 parse_partial("2.5").unwrap()
1389 )
1390 .unwrap()
1391 .len(),
1392 2
1393 );
1394 assert!(try_hyphen("1.0.0 - 2.0.0").unwrap().is_some());
1395 }
1396
1397 #[test]
1398 fn helper_expand_error_paths() {
1399 let partial = parse_partial("1.2").unwrap();
1400 assert_eq!(partial.major, Some(1));
1401 assert_eq!(partial.minor, Some(2));
1402 assert_eq!(partial.patch, None);
1403 assert!(expand_tilde(parse_partial("9007199254740991").unwrap()).is_err());
1404 assert!(expand_tilde(parse_partial("1.9007199254740991").unwrap()).is_err());
1405 assert!(expand_tilde(parse_partial("1.9007199254740991.0").unwrap()).is_err());
1406
1407 assert!(expand_caret(parse_partial("9007199254740991").unwrap()).is_err());
1408 assert!(expand_caret(parse_partial("9007199254740991.1").unwrap()).is_err());
1409 assert!(expand_caret(parse_partial("0.9007199254740991").unwrap()).is_err());
1410 assert!(expand_caret(parse_partial("0.9007199254740991.1").unwrap()).is_err());
1411 assert!(expand_caret(parse_partial("0.0.9007199254740991").unwrap()).is_err());
1412 assert_eq!(
1413 expand_caret(parse_partial("1.2").unwrap()).unwrap().len(),
1414 2
1415 );
1416 assert_eq!(
1417 expand_caret(parse_partial("0.2").unwrap()).unwrap().len(),
1418 2
1419 );
1420 let mut out = Vec::new();
1421 expand_caret_into(&mut out, parse_partial("0.2").unwrap()).unwrap();
1422 assert_eq!(out.len(), 2);
1423
1424 assert!(expand_primitive(None, parse_partial("9007199254740991").unwrap()).is_err());
1425 assert!(expand_primitive(None, parse_partial("1.9007199254740991").unwrap()).is_err());
1426 assert!(
1427 expand_primitive(
1428 Some(Operator::GreaterThan),
1429 parse_partial("9007199254740991").unwrap()
1430 )
1431 .is_err()
1432 );
1433 assert!(
1434 expand_primitive(
1435 Some(Operator::GreaterThan),
1436 parse_partial("1.9007199254740991").unwrap()
1437 )
1438 .is_err()
1439 );
1440 assert!(
1441 expand_primitive(
1442 Some(Operator::LessThanOrEqual),
1443 parse_partial("9007199254740991").unwrap()
1444 )
1445 .is_err()
1446 );
1447 assert!(
1448 expand_primitive(
1449 Some(Operator::LessThanOrEqual),
1450 parse_partial("1.9007199254740991").unwrap()
1451 )
1452 .is_err()
1453 );
1454
1455 assert!(
1456 expand_hyphen(
1457 parse_partial("1.0.0").unwrap(),
1458 parse_partial("9007199254740991").unwrap()
1459 )
1460 .is_err()
1461 );
1462 assert!(
1463 expand_hyphen(
1464 parse_partial("1.0.0").unwrap(),
1465 parse_partial("1.9007199254740991").unwrap()
1466 )
1467 .is_err()
1468 );
1469 assert!(parse_partial("1.bad").is_err());
1470 assert!(parse_partial("1.bad-alpha").is_err());
1471 assert!(parse_partial("1.bad.3-alpha").is_err());
1472 assert!(parse_partial("1-alpha").is_err());
1473 assert!(parse_partial("bad-alpha").is_err());
1474 assert!(parse_partial("-alpha").is_err());
1475 assert!(parse_partial("1.-alpha").is_err());
1476 assert!(parse_partial("1..2-alpha").is_err());
1477 assert!(parse_partial("1.2.3.4-alpha").is_err());
1478 assert!(parse_partial("1.2-rc.0").is_err());
1479 assert!(parse_partial("2.x-rc.0").is_err());
1480 assert!(parse_partial("1.2.3+").is_err());
1481 assert!(parse_partial("10000000000000000").is_err());
1482 assert_eq!(parse_range("^1.0.0").unwrap().set.len(), 1);
1483 assert_eq!(parse_range("1.0.0 || 2.0.0").unwrap().set.len(), 2);
1484 assert_eq!(parse_range("1.0.0 || 2.0.0 || 3.0.0").unwrap().set.len(), 3);
1485 assert!(parse_range(">= || 1.0.0").is_err());
1486 assert!(parse_range("1.0.0 || >=").is_err());
1487 assert!(parse_comparator_set(">= ").is_err());
1488 let mut long_bounded_range = "1.0.0 || ".repeat(29);
1489 long_bounded_range.push_str("1.0.0");
1490 assert!(parse_range(&long_bounded_range).is_err());
1491 assert_eq!(try_hyphen("1.0.0 - 2.0.0").unwrap().unwrap().len(), 2);
1492 assert!(try_hyphen("1.0.0 - 9007199254740991").is_err());
1493 }
1494
1495 #[test]
1496 fn public_and_comparator_helpers_are_used_in_crate_tests() {
1497 let version = Version::parse("1.2.3").unwrap();
1498 let prerelease = Version::parse("1.2.3-alpha.1").unwrap();
1499
1500 let eq = Comparator {
1501 op: Operator::Equal,
1502 version: version.clone(),
1503 };
1504 let lt = Comparator {
1505 op: Operator::LessThan,
1506 version: Version::parse("2.0.0").unwrap(),
1507 };
1508 let set = ComparatorSet {
1509 comparators: vec![eq.clone(), lt.clone()],
1510 };
1511 let range = Range::parse("1.2.3").unwrap();
1512
1513 assert!(eq.test(&version));
1514 assert!(
1515 Comparator {
1516 op: Operator::GreaterThan,
1517 version: Version::parse("1.2.2").unwrap(),
1518 }
1519 .test(&version)
1520 );
1521 assert!(
1522 Comparator {
1523 op: Operator::LessThanOrEqual,
1524 version: version.clone(),
1525 }
1526 .test(&version)
1527 );
1528 assert_eq!(eq.to_string(), "1.2.3");
1529 assert!(set.test(&version));
1530 assert!(!set.test(&prerelease));
1531 assert_eq!(
1532 Range::parse("^1.2.3").unwrap().to_string(),
1533 ">=1.2.3 <2.0.0-0"
1534 );
1535 assert!(range.satisfies(&version));
1536 assert!(!Range::parse("2.x || 3.x").unwrap().satisfies(&version));
1537 assert_eq!(
1538 compare_core_and_prerelease(&version, &Version::parse("1.2.4").unwrap()),
1539 core::cmp::Ordering::Less
1540 );
1541 }
1542
1543 #[test]
1544 fn comparator_set_test_covers_release_and_prerelease_paths() {
1545 let release = Version::parse("1.2.3").unwrap();
1546 let prerelease = Version::parse("1.2.3-alpha.1").unwrap();
1547 let next_release = Version::parse("1.2.4").unwrap();
1548 let matching_pre = Version::parse("1.2.3-alpha.0").unwrap();
1549
1550 let empty = ComparatorSet {
1551 comparators: Vec::new(),
1552 };
1553 assert!(empty.test(&release));
1554 assert!(!empty.test(&prerelease));
1555
1556 let release_ok = ComparatorSet {
1557 comparators: vec![Comparator {
1558 op: Operator::Equal,
1559 version: release.clone(),
1560 }],
1561 };
1562 assert!(release_ok.test(&release));
1563 assert!(!release_ok.test(&next_release));
1564
1565 let prerelease_without_match = ComparatorSet {
1566 comparators: vec![Comparator {
1567 op: Operator::GreaterThanOrEqual,
1568 version: release.clone(),
1569 }],
1570 };
1571 assert!(!prerelease_without_match.test(&prerelease));
1572
1573 let prerelease_passes_but_tuple_does_not_match = ComparatorSet {
1574 comparators: vec![
1575 Comparator {
1576 op: Operator::GreaterThan,
1577 version: Version::parse("1.0.0").unwrap(),
1578 },
1579 Comparator {
1580 op: Operator::LessThanOrEqual,
1581 version: Version::parse("2.0.0").unwrap(),
1582 },
1583 ],
1584 };
1585 assert!(!prerelease_passes_but_tuple_does_not_match.test(&prerelease));
1586
1587 let prerelease_with_match = ComparatorSet {
1588 comparators: vec![Comparator {
1589 op: Operator::GreaterThanOrEqual,
1590 version: matching_pre,
1591 }],
1592 };
1593 assert!(prerelease_with_match.test(&prerelease));
1594 }
1595
1596 #[test]
1597 fn compare_core_and_prerelease_covers_all_major_paths() {
1598 assert_eq!(
1599 compare_core_and_prerelease(
1600 &Version::parse("2.0.0").unwrap(),
1601 &Version::parse("1.9.9").unwrap()
1602 ),
1603 core::cmp::Ordering::Greater
1604 );
1605 assert_eq!(
1606 compare_core_and_prerelease(
1607 &Version::parse("1.3.0").unwrap(),
1608 &Version::parse("1.2.9").unwrap()
1609 ),
1610 core::cmp::Ordering::Greater
1611 );
1612 assert_eq!(
1613 compare_core_and_prerelease(
1614 &Version::parse("1.2.4").unwrap(),
1615 &Version::parse("1.2.3").unwrap()
1616 ),
1617 core::cmp::Ordering::Greater
1618 );
1619 assert_eq!(
1620 compare_core_and_prerelease(
1621 &Version::parse("1.2.3").unwrap(),
1622 &Version::parse("1.2.3-alpha.1").unwrap()
1623 ),
1624 core::cmp::Ordering::Greater
1625 );
1626 assert_eq!(
1627 compare_core_and_prerelease(
1628 &Version::parse("1.2.3-alpha.1").unwrap(),
1629 &Version::parse("1.2.3-alpha.2").unwrap()
1630 ),
1631 core::cmp::Ordering::Less
1632 );
1633 assert_eq!(
1634 compare_core_and_prerelease(
1635 &Version::parse("1.2.3-alpha.1").unwrap(),
1636 &Version::parse("1.2.3-alpha.1").unwrap()
1637 ),
1638 core::cmp::Ordering::Equal
1639 );
1640 }
1641}