1use std::borrow::Cow;
2use std::cmp::Ordering;
3use std::fmt::Formatter;
4use std::ops::Bound;
5use std::str::FromStr;
6
7use crate::{
8 Operator, OperatorParseError, Version, VersionPattern, VersionPatternParseError, version,
9};
10use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
11#[cfg(feature = "tracing")]
12use tracing::warn;
13
14#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)]
30#[cfg_attr(
31 feature = "rkyv",
32 derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)
33)]
34#[cfg_attr(feature = "rkyv", rkyv(derive(Debug)))]
35pub struct VersionSpecifiers(Box<[VersionSpecifier]>);
36
37impl std::ops::Deref for VersionSpecifiers {
38 type Target = [VersionSpecifier];
39
40 fn deref(&self) -> &Self::Target {
41 &self.0
42 }
43}
44
45impl VersionSpecifiers {
46 pub fn empty() -> Self {
48 Self(Box::new([]))
49 }
50
51 pub fn len(&self) -> usize {
53 self.0.len()
54 }
55
56 pub fn contains(&self, version: &Version) -> bool {
58 self.iter().all(|specifier| specifier.contains(version))
59 }
60
61 pub fn is_empty(&self) -> bool {
63 self.0.is_empty()
64 }
65
66 fn from_unsorted(mut specifiers: Vec<VersionSpecifier>) -> Self {
68 specifiers.sort_by(|a, b| a.version().cmp(b.version()));
71 Self(specifiers.into_boxed_slice())
72 }
73
74 pub fn from_release_only_bounds<'a>(
78 mut bounds: impl Iterator<Item = (&'a Bound<Version>, &'a Bound<Version>)>,
79 ) -> Self {
80 let mut specifiers = Vec::new();
81
82 let Some((start, mut next)) = bounds.next() else {
83 return Self::empty();
84 };
85
86 for (lower, upper) in bounds {
88 let specifier = match (next, lower) {
89 (Bound::Excluded(prev), Bound::Excluded(lower)) if prev == lower => {
91 Some(VersionSpecifier::not_equals_version(prev.clone()))
92 }
93 (Bound::Excluded(prev), Bound::Included(lower)) => {
95 match *prev.only_release_trimmed().release() {
96 [major] if *lower.only_release_trimmed().release() == [major, 1] => {
97 Some(VersionSpecifier::not_equals_star_version(Version::new([
98 major, 0,
99 ])))
100 }
101 [major, minor]
102 if *lower.only_release_trimmed().release() == [major, minor + 1] =>
103 {
104 Some(VersionSpecifier::not_equals_star_version(Version::new([
105 major, minor,
106 ])))
107 }
108 _ => None,
109 }
110 }
111 _ => None,
112 };
113 if let Some(specifier) = specifier {
114 specifiers.push(specifier);
115 } else {
116 #[cfg(feature = "tracing")]
117 warn!(
118 "Ignoring unsupported gap in `requires-python` version: {next:?} -> {lower:?}"
119 );
120 }
121 next = upper;
122 }
123 let end = next;
124
125 specifiers.extend(VersionSpecifier::from_release_only_bounds((start, end)));
127
128 Self::from_unsorted(specifiers)
129 }
130}
131
132impl FromIterator<VersionSpecifier> for VersionSpecifiers {
133 fn from_iter<T: IntoIterator<Item = VersionSpecifier>>(iter: T) -> Self {
134 Self::from_unsorted(iter.into_iter().collect())
135 }
136}
137
138impl IntoIterator for VersionSpecifiers {
139 type Item = VersionSpecifier;
140 type IntoIter = std::vec::IntoIter<VersionSpecifier>;
141
142 fn into_iter(self) -> Self::IntoIter {
143 self.0.into_vec().into_iter()
144 }
145}
146
147impl FromStr for VersionSpecifiers {
148 type Err = VersionSpecifiersParseError;
149
150 fn from_str(s: &str) -> Result<Self, Self::Err> {
151 parse_version_specifiers(s).map(Self::from_unsorted)
152 }
153}
154
155impl From<VersionSpecifier> for VersionSpecifiers {
156 fn from(specifier: VersionSpecifier) -> Self {
157 Self(Box::new([specifier]))
158 }
159}
160
161impl std::fmt::Display for VersionSpecifiers {
162 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163 for (idx, version_specifier) in self.0.iter().enumerate() {
164 if idx == 0 {
167 write!(f, "{version_specifier}")?;
168 } else {
169 write!(f, ", {version_specifier}")?;
170 }
171 }
172 Ok(())
173 }
174}
175
176impl Default for VersionSpecifiers {
177 fn default() -> Self {
178 Self::empty()
179 }
180}
181
182impl<'de> Deserialize<'de> for VersionSpecifiers {
183 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
184 where
185 D: Deserializer<'de>,
186 {
187 struct Visitor;
188
189 impl de::Visitor<'_> for Visitor {
190 type Value = VersionSpecifiers;
191
192 fn expecting(&self, f: &mut Formatter) -> std::fmt::Result {
193 f.write_str("a string")
194 }
195
196 fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
197 VersionSpecifiers::from_str(v).map_err(de::Error::custom)
198 }
199 }
200
201 deserializer.deserialize_str(Visitor)
202 }
203}
204
205impl Serialize for VersionSpecifiers {
206 #[allow(unstable_name_collisions)]
207 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
208 where
209 S: Serializer,
210 {
211 serializer.serialize_str(
212 &self
213 .iter()
214 .map(ToString::to_string)
215 .collect::<Vec<String>>()
216 .join(","),
217 )
218 }
219}
220
221#[derive(Debug, Eq, PartialEq, Clone)]
223pub struct VersionSpecifiersParseError {
224 inner: Box<VersionSpecifiersParseErrorInner>,
227}
228
229#[derive(Debug, Eq, PartialEq, Clone)]
230struct VersionSpecifiersParseErrorInner {
231 err: VersionSpecifierParseError,
233 line: String,
235 start: usize,
238 end: usize,
241}
242
243impl std::fmt::Display for VersionSpecifiersParseError {
244 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
245 use unicode_width::UnicodeWidthStr;
246
247 let VersionSpecifiersParseErrorInner {
248 ref err,
249 ref line,
250 start,
251 end,
252 } = *self.inner;
253 writeln!(f, "Failed to parse version: {err}:")?;
254 writeln!(f, "{line}")?;
255 let indent = line[..start].width();
256 let point = line[start..end].width();
257 writeln!(f, "{}{}", " ".repeat(indent), "^".repeat(point))?;
258 Ok(())
259 }
260}
261
262impl VersionSpecifiersParseError {
263 pub fn line(&self) -> &String {
265 &self.inner.line
266 }
267}
268
269impl std::error::Error for VersionSpecifiersParseError {}
270
271#[derive(Eq, Ord, PartialEq, PartialOrd, Debug, Clone, Hash)]
283#[cfg_attr(
284 feature = "rkyv",
285 derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)
286)]
287#[cfg_attr(feature = "rkyv", rkyv(derive(Debug)))]
288pub struct VersionSpecifier {
289 pub(crate) operator: Operator,
291 pub(crate) version: Version,
293}
294
295impl<'de> Deserialize<'de> for VersionSpecifier {
296 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
297 where
298 D: Deserializer<'de>,
299 {
300 struct Visitor;
301
302 impl de::Visitor<'_> for Visitor {
303 type Value = VersionSpecifier;
304
305 fn expecting(&self, f: &mut Formatter) -> std::fmt::Result {
306 f.write_str("a string")
307 }
308
309 fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
310 VersionSpecifier::from_str(v).map_err(de::Error::custom)
311 }
312 }
313
314 deserializer.deserialize_str(Visitor)
315 }
316}
317
318impl Serialize for VersionSpecifier {
320 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
321 where
322 S: Serializer,
323 {
324 serializer.collect_str(self)
325 }
326}
327
328impl VersionSpecifier {
329 pub fn from_pattern(
332 operator: Operator,
333 version_pattern: VersionPattern,
334 ) -> Result<Self, VersionSpecifierBuildError> {
335 let star = version_pattern.is_wildcard();
336 let version = version_pattern.into_version();
337
338 let operator = if star {
340 match operator.to_star() {
341 Some(starop) => starop,
342 None => {
343 return Err(BuildErrorKind::OperatorWithStar { operator }.into());
344 }
345 }
346 } else {
347 operator
348 };
349
350 Self::from_version(operator, version)
351 }
352
353 pub fn from_version(
355 operator: Operator,
356 version: Version,
357 ) -> Result<Self, VersionSpecifierBuildError> {
358 if version.is_local() && !operator.is_local_compatible() {
360 return Err(BuildErrorKind::OperatorLocalCombo { operator, version }.into());
361 }
362
363 if operator == Operator::TildeEqual && version.release().len() < 2 {
364 return Err(BuildErrorKind::CompatibleRelease.into());
365 }
366
367 Ok(Self { operator, version })
368 }
369
370 #[must_use]
390 pub fn only_release(self) -> Self {
391 Self {
392 operator: self.operator,
393 version: self.version.only_release(),
394 }
395 }
396
397 #[must_use]
399 pub fn only_minor_release(&self) -> Self {
400 Self {
401 operator: self.operator,
402 version: self.version.only_minor_release(),
403 }
404 }
405
406 pub fn equals_version(version: Version) -> Self {
408 Self {
409 operator: Operator::Equal,
410 version,
411 }
412 }
413
414 pub fn equals_star_version(version: Version) -> Self {
416 Self {
417 operator: Operator::EqualStar,
418 version,
419 }
420 }
421
422 pub fn not_equals_star_version(version: Version) -> Self {
424 Self {
425 operator: Operator::NotEqualStar,
426 version,
427 }
428 }
429
430 pub fn not_equals_version(version: Version) -> Self {
432 Self {
433 operator: Operator::NotEqual,
434 version,
435 }
436 }
437
438 pub fn greater_than_equal_version(version: Version) -> Self {
440 Self {
441 operator: Operator::GreaterThanEqual,
442 version,
443 }
444 }
445 pub fn greater_than_version(version: Version) -> Self {
447 Self {
448 operator: Operator::GreaterThan,
449 version,
450 }
451 }
452
453 pub fn less_than_equal_version(version: Version) -> Self {
455 Self {
456 operator: Operator::LessThanEqual,
457 version,
458 }
459 }
460
461 pub fn less_than_version(version: Version) -> Self {
463 Self {
464 operator: Operator::LessThan,
465 version,
466 }
467 }
468
469 pub fn operator(&self) -> &Operator {
471 &self.operator
472 }
473
474 pub fn version(&self) -> &Version {
476 &self.version
477 }
478
479 pub fn into_parts(self) -> (Operator, Version) {
481 (self.operator, self.version)
482 }
483
484 pub fn any_prerelease(&self) -> bool {
486 self.version.any_prerelease()
487 }
488
489 pub fn from_release_only_bounds(
493 bounds: (&Bound<Version>, &Bound<Version>),
494 ) -> impl Iterator<Item = Self> {
495 let (b1, b2) = match bounds {
496 (Bound::Included(v1), Bound::Included(v2)) if v1 == v2 => {
497 (Some(Self::equals_version(v1.clone())), None)
498 }
499 (Bound::Included(v1), Bound::Excluded(v2)) => {
501 match *v1.only_release_trimmed().release() {
502 [major] if *v2.only_release_trimmed().release() == [major, 1] => {
503 let version = Version::new([major, 0]);
504 (Some(Self::equals_star_version(version)), None)
505 }
506 [major, minor]
507 if *v2.only_release_trimmed().release() == [major, minor + 1] =>
508 {
509 let version = Version::new([major, minor]);
510 (Some(Self::equals_star_version(version)), None)
511 }
512 _ => (
513 Self::from_lower_bound(&Bound::Included(v1.clone())),
514 Self::from_upper_bound(&Bound::Excluded(v2.clone())),
515 ),
516 }
517 }
518 (lower, upper) => (Self::from_lower_bound(lower), Self::from_upper_bound(upper)),
519 };
520
521 b1.into_iter().chain(b2)
522 }
523
524 pub fn from_lower_bound(bound: &Bound<Version>) -> Option<Self> {
526 match bound {
527 Bound::Included(version) => {
528 Some(Self::from_version(Operator::GreaterThanEqual, version.clone()).unwrap())
529 }
530 Bound::Excluded(version) => {
531 Some(Self::from_version(Operator::GreaterThan, version.clone()).unwrap())
532 }
533 Bound::Unbounded => None,
534 }
535 }
536
537 pub fn from_upper_bound(bound: &Bound<Version>) -> Option<Self> {
539 match bound {
540 Bound::Included(version) => {
541 Some(Self::from_version(Operator::LessThanEqual, version.clone()).unwrap())
542 }
543 Bound::Excluded(version) => {
544 Some(Self::from_version(Operator::LessThan, version.clone()).unwrap())
545 }
546 Bound::Unbounded => None,
547 }
548 }
549
550 pub fn contains(&self, version: &Version) -> bool {
558 let this = self.version();
562 let other = if this.local().is_empty() && !version.local().is_empty() {
563 Cow::Owned(version.clone().without_local())
564 } else {
565 Cow::Borrowed(version)
566 };
567
568 match self.operator {
569 Operator::Equal => other.as_ref() == this,
570 Operator::EqualStar => {
571 this.epoch() == other.epoch()
572 && self
573 .version
574 .release()
575 .iter()
576 .zip(other.release().iter().chain(std::iter::repeat(&0)))
580 .all(|(this, other)| this == other)
581 }
582 #[allow(deprecated)]
583 Operator::ExactEqual => {
584 #[cfg(feature = "tracing")]
585 {
586 warn!("Using arbitrary equality (`===`) is discouraged");
587 }
588 self.version.to_string() == version.to_string()
589 }
590 Operator::NotEqual => this != other.as_ref(),
591 Operator::NotEqualStar => {
592 this.epoch() != other.epoch()
593 || !this
594 .release()
595 .iter()
596 .zip(other.release().iter().chain(std::iter::repeat(&0)))
600 .all(|(this, other)| this == other)
601 }
602 Operator::TildeEqual => {
603 assert!(this.release().len() > 1);
608 if this.epoch() != other.epoch() {
609 return false;
610 }
611
612 if !this.release()[..this.release().len() - 1]
613 .iter()
614 .zip(&*other.release())
615 .all(|(this, other)| this == other)
616 {
617 return false;
618 }
619
620 other.as_ref() >= this
623 }
624 Operator::GreaterThan => {
625 if other.epoch() > this.epoch() {
626 return true;
627 }
628
629 if version::compare_release(&this.release(), &other.release()) == Ordering::Equal {
630 if !this.is_post() && other.is_post() {
635 return false;
636 }
637
638 if other.is_local() {
640 return false;
641 }
642 }
643
644 other.as_ref() > this
645 }
646 Operator::GreaterThanEqual => other.as_ref() >= this,
647 Operator::LessThan => {
648 if other.epoch() < this.epoch() {
649 return true;
650 }
651
652 if version::compare_release(&this.release(), &other.release()) == Ordering::Equal
657 && !this.any_prerelease()
658 && other.any_prerelease()
659 {
660 return false;
661 }
662
663 other.as_ref() < this
664 }
665 Operator::LessThanEqual => other.as_ref() <= this,
666 }
667 }
668
669 pub fn has_lower_bound(&self) -> bool {
671 match self.operator() {
672 Operator::Equal
673 | Operator::EqualStar
674 | Operator::ExactEqual
675 | Operator::TildeEqual
676 | Operator::GreaterThan
677 | Operator::GreaterThanEqual => true,
678 Operator::LessThanEqual
679 | Operator::LessThan
680 | Operator::NotEqualStar
681 | Operator::NotEqual => false,
682 }
683 }
684}
685
686impl FromStr for VersionSpecifier {
687 type Err = VersionSpecifierParseError;
688
689 fn from_str(spec: &str) -> Result<Self, Self::Err> {
691 let mut s = unscanny::Scanner::new(spec);
692 s.eat_while(|c: char| c.is_whitespace());
693 let operator = s.eat_while(['=', '!', '~', '<', '>']);
695 if operator.is_empty() {
696 s.eat_while(|c: char| c.is_whitespace());
699 let version = s.eat_while(|c: char| !c.is_whitespace());
700 s.eat_while(|c: char| c.is_whitespace());
701 return Err(ParseErrorKind::MissingOperator(VersionOperatorBuildError {
702 version_pattern: VersionPattern::from_str(version).ok(),
703 })
704 .into());
705 }
706 let operator = Operator::from_str(operator).map_err(ParseErrorKind::InvalidOperator)?;
707 s.eat_while(|c: char| c.is_whitespace());
708 let version = s.eat_while(|c: char| !c.is_whitespace());
709 if version.is_empty() {
710 return Err(ParseErrorKind::MissingVersion.into());
711 }
712 let vpat = version.parse().map_err(ParseErrorKind::InvalidVersion)?;
713 let version_specifier =
714 Self::from_pattern(operator, vpat).map_err(ParseErrorKind::InvalidSpecifier)?;
715 s.eat_while(|c: char| c.is_whitespace());
716 if !s.done() {
717 return Err(ParseErrorKind::InvalidTrailing(s.after().to_string()).into());
718 }
719 Ok(version_specifier)
720 }
721}
722
723impl std::fmt::Display for VersionSpecifier {
724 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
725 if self.operator == Operator::EqualStar || self.operator == Operator::NotEqualStar {
726 return write!(f, "{}{}.*", self.operator, self.version);
727 }
728 write!(f, "{}{}", self.operator, self.version)
729 }
730}
731
732#[derive(Clone, Debug, Eq, PartialEq)]
734pub struct VersionSpecifierBuildError {
735 kind: Box<BuildErrorKind>,
738}
739
740impl std::error::Error for VersionSpecifierBuildError {}
741
742impl std::fmt::Display for VersionSpecifierBuildError {
743 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
744 match *self.kind {
745 BuildErrorKind::OperatorLocalCombo {
746 operator: ref op,
747 ref version,
748 } => {
749 let local = version.local();
750 write!(
751 f,
752 "Operator {op} is incompatible with versions \
753 containing non-empty local segments (`+{local}`)",
754 )
755 }
756 BuildErrorKind::OperatorWithStar { operator: ref op } => {
757 write!(
758 f,
759 "Operator {op} cannot be used with a wildcard version specifier",
760 )
761 }
762 BuildErrorKind::CompatibleRelease => {
763 write!(
764 f,
765 "The ~= operator requires at least two segments in the release version"
766 )
767 }
768 }
769 }
770}
771
772#[derive(Clone, Debug, Eq, PartialEq)]
773struct VersionOperatorBuildError {
774 version_pattern: Option<VersionPattern>,
775}
776
777impl std::error::Error for VersionOperatorBuildError {}
778
779impl std::fmt::Display for VersionOperatorBuildError {
780 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
781 write!(f, "Unexpected end of version specifier, expected operator")?;
782 if let Some(version_pattern) = &self.version_pattern {
783 let version_specifier =
784 VersionSpecifier::from_pattern(Operator::Equal, version_pattern.clone()).unwrap();
785 write!(f, ". Did you mean `{version_specifier}`?")?;
786 }
787 Ok(())
788 }
789}
790
791#[derive(Clone, Debug, Eq, PartialEq)]
794enum BuildErrorKind {
795 OperatorLocalCombo {
799 operator: Operator,
801 version: Version,
803 },
804 OperatorWithStar {
807 operator: Operator,
809 },
810 CompatibleRelease,
813}
814
815impl From<BuildErrorKind> for VersionSpecifierBuildError {
816 fn from(kind: BuildErrorKind) -> Self {
817 Self {
818 kind: Box::new(kind),
819 }
820 }
821}
822
823#[derive(Clone, Debug, Eq, PartialEq)]
825pub struct VersionSpecifierParseError {
826 kind: Box<ParseErrorKind>,
829}
830
831impl std::error::Error for VersionSpecifierParseError {}
832
833impl std::fmt::Display for VersionSpecifierParseError {
834 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
835 match *self.kind {
841 ParseErrorKind::InvalidOperator(ref err) => err.fmt(f),
842 ParseErrorKind::InvalidVersion(ref err) => err.fmt(f),
843 ParseErrorKind::InvalidSpecifier(ref err) => err.fmt(f),
844 ParseErrorKind::MissingOperator(ref err) => err.fmt(f),
845 ParseErrorKind::MissingVersion => {
846 write!(f, "Unexpected end of version specifier, expected version")
847 }
848 ParseErrorKind::InvalidTrailing(ref trail) => {
849 write!(f, "Trailing `{trail}` is not allowed")
850 }
851 }
852 }
853}
854
855#[derive(Clone, Debug, Eq, PartialEq)]
858enum ParseErrorKind {
859 InvalidOperator(OperatorParseError),
860 InvalidVersion(VersionPatternParseError),
861 InvalidSpecifier(VersionSpecifierBuildError),
862 MissingOperator(VersionOperatorBuildError),
863 MissingVersion,
864 InvalidTrailing(String),
865}
866
867impl From<ParseErrorKind> for VersionSpecifierParseError {
868 fn from(kind: ParseErrorKind) -> Self {
869 Self {
870 kind: Box::new(kind),
871 }
872 }
873}
874
875pub(crate) fn parse_version_specifiers(
877 spec: &str,
878) -> Result<Vec<VersionSpecifier>, VersionSpecifiersParseError> {
879 let mut version_ranges = Vec::new();
880 if spec.is_empty() {
881 return Ok(version_ranges);
882 }
883 let mut start: usize = 0;
884 let separator = ",";
885 for version_range_spec in spec.split(separator) {
886 match VersionSpecifier::from_str(version_range_spec) {
887 Err(err) => {
888 return Err(VersionSpecifiersParseError {
889 inner: Box::new(VersionSpecifiersParseErrorInner {
890 err,
891 line: spec.to_string(),
892 start,
893 end: start + version_range_spec.len(),
894 }),
895 });
896 }
897 Ok(version_range) => {
898 version_ranges.push(version_range);
899 }
900 }
901 start += version_range_spec.len();
902 start += separator.len();
903 }
904 Ok(version_ranges)
905}
906
907#[derive(Clone, Debug)]
910pub struct TildeVersionSpecifier<'a> {
911 inner: Cow<'a, VersionSpecifier>,
912}
913
914impl<'a> TildeVersionSpecifier<'a> {
915 pub fn from_specifier(specifier: VersionSpecifier) -> Option<Self> {
920 TildeVersionSpecifier::new(Cow::Owned(specifier))
921 }
922
923 pub fn from_specifier_ref(specifier: &'a VersionSpecifier) -> Option<Self> {
927 TildeVersionSpecifier::new(Cow::Borrowed(specifier))
928 }
929
930 fn new(specifier: Cow<'a, VersionSpecifier>) -> Option<Self> {
931 if specifier.operator != Operator::TildeEqual {
932 return None;
933 }
934 if specifier.version().release().len() < 2 || specifier.version().release().len() > 3 {
935 return None;
936 }
937 if specifier.version().any_prerelease()
938 || specifier.version().is_local()
939 || specifier.version().is_post()
940 {
941 return None;
942 }
943 Some(Self { inner: specifier })
944 }
945
946 pub fn has_patch(&self) -> bool {
948 self.inner.version.release().len() == 3
949 }
950
951 pub fn bounding_specifiers(&self) -> (VersionSpecifier, VersionSpecifier) {
955 let release = self.inner.version().release();
956 let lower = self.inner.version.clone();
957 let upper = if self.has_patch() {
958 Version::new([release[0], release[1] + 1])
959 } else {
960 Version::new([release[0] + 1])
961 };
962 (
963 VersionSpecifier::greater_than_equal_version(lower),
964 VersionSpecifier::less_than_version(upper),
965 )
966 }
967
968 pub fn with_patch_version(&self, patch: u64) -> TildeVersionSpecifier<'_> {
970 let mut release = self.inner.version.release().to_vec();
971 if self.has_patch() {
972 release.pop();
973 }
974 release.push(patch);
975 TildeVersionSpecifier::from_specifier(
976 VersionSpecifier::from_version(Operator::TildeEqual, Version::new(release))
977 .expect("We should always derive a valid new version specifier"),
978 )
979 .expect("We should always derive a new tilde version specifier")
980 }
981}
982
983impl std::fmt::Display for TildeVersionSpecifier<'_> {
984 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
985 write!(f, "{}", self.inner)
986 }
987}
988
989#[cfg(test)]
990mod tests {
991 use std::{cmp::Ordering, str::FromStr};
992
993 use indoc::indoc;
994
995 use crate::LocalSegment;
996
997 use super::*;
998
999 #[test]
1001 fn test_equal() {
1002 let version = Version::from_str("1.1.post1").unwrap();
1003
1004 assert!(
1005 !VersionSpecifier::from_str("== 1.1")
1006 .unwrap()
1007 .contains(&version)
1008 );
1009 assert!(
1010 VersionSpecifier::from_str("== 1.1.post1")
1011 .unwrap()
1012 .contains(&version)
1013 );
1014 assert!(
1015 VersionSpecifier::from_str("== 1.1.*")
1016 .unwrap()
1017 .contains(&version)
1018 );
1019 }
1020
1021 const VERSIONS_ALL: &[&str] = &[
1022 "1.0.dev456",
1024 "1.0a1",
1025 "1.0a2.dev456",
1026 "1.0a12.dev456",
1027 "1.0a12",
1028 "1.0b1.dev456",
1029 "1.0b2",
1030 "1.0b2.post345.dev456",
1031 "1.0b2.post345",
1032 "1.0b2-346",
1033 "1.0c1.dev456",
1034 "1.0c1",
1035 "1.0rc2",
1036 "1.0c3",
1037 "1.0",
1038 "1.0.post456.dev34",
1039 "1.0.post456",
1040 "1.1.dev1",
1041 "1.2+123abc",
1042 "1.2+123abc456",
1043 "1.2+abc",
1044 "1.2+abc123",
1045 "1.2+abc123def",
1046 "1.2+1234.abc",
1047 "1.2+123456",
1048 "1.2.r32+123456",
1049 "1.2.rev33+123456",
1050 "1!1.0.dev456",
1052 "1!1.0a1",
1053 "1!1.0a2.dev456",
1054 "1!1.0a12.dev456",
1055 "1!1.0a12",
1056 "1!1.0b1.dev456",
1057 "1!1.0b2",
1058 "1!1.0b2.post345.dev456",
1059 "1!1.0b2.post345",
1060 "1!1.0b2-346",
1061 "1!1.0c1.dev456",
1062 "1!1.0c1",
1063 "1!1.0rc2",
1064 "1!1.0c3",
1065 "1!1.0",
1066 "1!1.0.post456.dev34",
1067 "1!1.0.post456",
1068 "1!1.1.dev1",
1069 "1!1.2+123abc",
1070 "1!1.2+123abc456",
1071 "1!1.2+abc",
1072 "1!1.2+abc123",
1073 "1!1.2+abc123def",
1074 "1!1.2+1234.abc",
1075 "1!1.2+123456",
1076 "1!1.2.r32+123456",
1077 "1!1.2.rev33+123456",
1078 ];
1079
1080 #[test]
1086 fn test_operators_true() {
1087 let versions: Vec<Version> = VERSIONS_ALL
1088 .iter()
1089 .map(|version| Version::from_str(version).unwrap())
1090 .collect();
1091
1092 let operations = [
1095 versions
1097 .iter()
1098 .enumerate()
1099 .flat_map(|(i, x)| {
1100 versions[i + 1..]
1101 .iter()
1102 .map(move |y| (x, y, Ordering::Less))
1103 })
1104 .collect::<Vec<_>>(),
1105 versions
1107 .iter()
1108 .map(move |x| (x, x, Ordering::Equal))
1109 .collect::<Vec<_>>(),
1110 versions
1112 .iter()
1113 .enumerate()
1114 .flat_map(|(i, x)| versions[..i].iter().map(move |y| (x, y, Ordering::Greater)))
1115 .collect::<Vec<_>>(),
1116 ]
1117 .into_iter()
1118 .flatten();
1119
1120 for (a, b, ordering) in operations {
1121 assert_eq!(a.cmp(b), ordering, "{a} {ordering:?} {b}");
1122 }
1123 }
1124
1125 const VERSIONS_0: &[&str] = &[
1126 "1.0.dev456",
1127 "1.0a1",
1128 "1.0a2.dev456",
1129 "1.0a12.dev456",
1130 "1.0a12",
1131 "1.0b1.dev456",
1132 "1.0b2",
1133 "1.0b2.post345.dev456",
1134 "1.0b2.post345",
1135 "1.0b2-346",
1136 "1.0c1.dev456",
1137 "1.0c1",
1138 "1.0rc2",
1139 "1.0c3",
1140 "1.0",
1141 "1.0.post456.dev34",
1142 "1.0.post456",
1143 "1.1.dev1",
1144 "1.2+123abc",
1145 "1.2+123abc456",
1146 "1.2+abc",
1147 "1.2+abc123",
1148 "1.2+abc123def",
1149 "1.2+1234.abc",
1150 "1.2+123456",
1151 "1.2.r32+123456",
1152 "1.2.rev33+123456",
1153 ];
1154
1155 const SPECIFIERS_OTHER: &[&str] = &[
1156 "== 1.*", "== 1.0.*", "== 1.1.*", "== 1.2.*", "== 2.*", "~= 1.0", "~= 1.0b1", "~= 1.1",
1157 "~= 1.2", "~= 2.0",
1158 ];
1159
1160 const EXPECTED_OTHER: &[[bool; 10]] = &[
1161 [
1162 true, true, false, false, false, false, false, false, false, false,
1163 ],
1164 [
1165 true, true, false, false, false, false, false, false, false, false,
1166 ],
1167 [
1168 true, true, false, false, false, false, false, false, false, false,
1169 ],
1170 [
1171 true, true, false, false, false, false, false, false, false, false,
1172 ],
1173 [
1174 true, true, false, false, false, false, false, false, false, false,
1175 ],
1176 [
1177 true, true, false, false, false, false, false, false, false, false,
1178 ],
1179 [
1180 true, true, false, false, false, false, true, false, false, false,
1181 ],
1182 [
1183 true, true, false, false, false, false, true, false, false, false,
1184 ],
1185 [
1186 true, true, false, false, false, false, true, false, false, false,
1187 ],
1188 [
1189 true, true, false, false, false, false, true, false, false, false,
1190 ],
1191 [
1192 true, true, false, false, false, false, true, false, false, false,
1193 ],
1194 [
1195 true, true, false, false, false, false, true, false, false, false,
1196 ],
1197 [
1198 true, true, false, false, false, false, true, false, false, false,
1199 ],
1200 [
1201 true, true, false, false, false, false, true, false, false, false,
1202 ],
1203 [
1204 true, true, false, false, false, true, true, false, false, false,
1205 ],
1206 [
1207 true, true, false, false, false, true, true, false, false, false,
1208 ],
1209 [
1210 true, true, false, false, false, true, true, false, false, false,
1211 ],
1212 [
1213 true, false, true, false, false, true, true, false, false, false,
1214 ],
1215 [
1216 true, false, false, true, false, true, true, true, true, false,
1217 ],
1218 [
1219 true, false, false, true, false, true, true, true, true, false,
1220 ],
1221 [
1222 true, false, false, true, false, true, true, true, true, false,
1223 ],
1224 [
1225 true, false, false, true, false, true, true, true, true, false,
1226 ],
1227 [
1228 true, false, false, true, false, true, true, true, true, false,
1229 ],
1230 [
1231 true, false, false, true, false, true, true, true, true, false,
1232 ],
1233 [
1234 true, false, false, true, false, true, true, true, true, false,
1235 ],
1236 [
1237 true, false, false, true, false, true, true, true, true, false,
1238 ],
1239 [
1240 true, false, false, true, false, true, true, true, true, false,
1241 ],
1242 ];
1243
1244 #[test]
1248 fn test_operators_other() {
1249 let versions = VERSIONS_0
1250 .iter()
1251 .map(|version| Version::from_str(version).unwrap());
1252 let specifiers: Vec<_> = SPECIFIERS_OTHER
1253 .iter()
1254 .map(|specifier| VersionSpecifier::from_str(specifier).unwrap())
1255 .collect();
1256
1257 for (version, expected) in versions.zip(EXPECTED_OTHER) {
1258 let actual = specifiers
1259 .iter()
1260 .map(|specifier| specifier.contains(&version));
1261 for ((actual, expected), _specifier) in actual.zip(expected).zip(SPECIFIERS_OTHER) {
1262 assert_eq!(actual, *expected);
1263 }
1264 }
1265 }
1266
1267 #[test]
1268 fn test_arbitrary_equality() {
1269 assert!(
1270 VersionSpecifier::from_str("=== 1.2a1")
1271 .unwrap()
1272 .contains(&Version::from_str("1.2a1").unwrap())
1273 );
1274 assert!(
1275 !VersionSpecifier::from_str("=== 1.2a1")
1276 .unwrap()
1277 .contains(&Version::from_str("1.2a1+local").unwrap())
1278 );
1279 }
1280
1281 #[test]
1282 fn test_equal_star_short_version_bug() {
1283 let specifier = VersionSpecifier::from_str("==2.1.*").unwrap();
1285 let version = Version::from_str("2").unwrap();
1286 assert!(
1287 !specifier.contains(&version),
1288 "Bug: version '2' incorrectly matches '==2.1.*'"
1289 );
1290
1291 let specifier = VersionSpecifier::from_str("!=2.1.*").unwrap();
1293 let version = Version::from_str("2").unwrap();
1294 assert!(
1295 specifier.contains(&version),
1296 "Bug: version '2' should match '!=2.1.*' (2.0 is not in 2.1 family)"
1297 );
1298
1299 let specifier = VersionSpecifier::from_str("==2.0.*").unwrap();
1301 let version = Version::from_str("2").unwrap();
1302 assert!(
1303 specifier.contains(&version),
1304 "version '2' should match '==2.0.*'"
1305 );
1306
1307 let specifier = VersionSpecifier::from_str("!=2.0.*").unwrap();
1309 let version = Version::from_str("2").unwrap();
1310 assert!(
1311 !specifier.contains(&version),
1312 "version '2' should not match '!=2.0.*'"
1313 );
1314
1315 let specifier = VersionSpecifier::from_str("==2.1.*").unwrap();
1318 let version = Version::from_str("2+local").unwrap();
1319 assert!(
1320 !specifier.contains(&version),
1321 "version '2+local' should not match '==2.1.*'"
1322 );
1323
1324 let specifier = VersionSpecifier::from_str("!=2.1.*").unwrap();
1326 let version = Version::from_str("2+local").unwrap();
1327 assert!(
1328 specifier.contains(&version),
1329 "version '2+local' should match '!=2.1.*'"
1330 );
1331 }
1332
1333 #[test]
1334 fn test_specifiers_true() {
1335 let pairs = [
1336 ("2.0", "==2"),
1338 ("2.0", "==2.0"),
1339 ("2.0", "==2.0.0"),
1340 ("2.0+deadbeef", "==2"),
1341 ("2.0+deadbeef", "==2.0"),
1342 ("2.0+deadbeef", "==2.0.0"),
1343 ("2.0+deadbeef", "==2+deadbeef"),
1344 ("2.0+deadbeef", "==2.0+deadbeef"),
1345 ("2.0+deadbeef", "==2.0.0+deadbeef"),
1346 ("2.0+deadbeef.0", "==2.0.0+deadbeef.00"),
1347 ("2.dev1", "==2.*"),
1349 ("2a1", "==2.*"),
1350 ("2a1.post1", "==2.*"),
1351 ("2b1", "==2.*"),
1352 ("2b1.dev1", "==2.*"),
1353 ("2c1", "==2.*"),
1354 ("2c1.post1.dev1", "==2.*"),
1355 ("2c1.post1.dev1", "==2.0.*"),
1356 ("2rc1", "==2.*"),
1357 ("2rc1", "==2.0.*"),
1358 ("2", "==2.*"),
1359 ("2", "==2.0.*"),
1360 ("2", "==0!2.*"),
1361 ("0!2", "==2.*"),
1362 ("2.0", "==2.*"),
1363 ("2.0.0", "==2.*"),
1364 ("2.1+local.version", "==2.1.*"),
1365 ("2.1", "!=2"),
1367 ("2.1", "!=2.0"),
1368 ("2.0.1", "!=2"),
1369 ("2.0.1", "!=2.0"),
1370 ("2.0.1", "!=2.0.0"),
1371 ("2.0", "!=2.0+deadbeef"),
1372 ("2.0", "!=3.*"),
1374 ("2.1", "!=2.0.*"),
1375 ("2.0", ">=2"),
1377 ("2.0", ">=2.0"),
1378 ("2.0", ">=2.0.0"),
1379 ("2.0.post1", ">=2"),
1380 ("2.0.post1.dev1", ">=2"),
1381 ("3", ">=2"),
1382 ("2.0", "<=2"),
1384 ("2.0", "<=2.0"),
1385 ("2.0", "<=2.0.0"),
1386 ("2.0.dev1", "<=2"),
1387 ("2.0a1", "<=2"),
1388 ("2.0a1.dev1", "<=2"),
1389 ("2.0b1", "<=2"),
1390 ("2.0b1.post1", "<=2"),
1391 ("2.0c1", "<=2"),
1392 ("2.0c1.post1.dev1", "<=2"),
1393 ("2.0rc1", "<=2"),
1394 ("1", "<=2"),
1395 ("3", ">2"),
1397 ("2.1", ">2.0"),
1398 ("2.0.1", ">2"),
1399 ("2.1.post1", ">2"),
1400 ("2.1+local.version", ">2"),
1401 ("2.post2", ">2.post1"),
1402 ("1", "<2"),
1404 ("2.0", "<2.1"),
1405 ("2.0.dev0", "<2.1"),
1406 ("0.1a1", "<0.1a2"),
1408 ("0.1dev1", "<0.1dev2"),
1409 ("0.1dev1", "<0.1a1"),
1410 ("1", "~=1.0"),
1412 ("1.0.1", "~=1.0"),
1413 ("1.1", "~=1.0"),
1414 ("1.9999999", "~=1.0"),
1415 ("1.1", "~=1.0a1"),
1416 ("2022.01.01", "~=2022.01.01"),
1417 ("2!1.0", "~=2!1.0"),
1419 ("2!1.0", "==2!1.*"),
1420 ("2!1.0", "==2!1.0"),
1421 ("2!1.0", "!=1.0"),
1422 ("1.0", "!=2!1.0"),
1423 ("1.0", "<=2!0.1"),
1424 ("2!1.0", ">=2.0"),
1425 ("1.0", "<2!0.1"),
1426 ("2!1.0", ">2.0"),
1427 ("2.0.5", ">2.0dev"),
1429 ];
1430
1431 for (s_version, s_spec) in pairs {
1432 let version = s_version.parse::<Version>().unwrap();
1433 let spec = s_spec.parse::<VersionSpecifier>().unwrap();
1434 assert!(
1435 spec.contains(&version),
1436 "{s_version} {s_spec}\nversion repr: {:?}\nspec version repr: {:?}",
1437 version.as_bloated_debug(),
1438 spec.version.as_bloated_debug(),
1439 );
1440 }
1441 }
1442
1443 #[test]
1444 fn test_specifier_false() {
1445 let pairs = [
1446 ("2.1", "==2"),
1448 ("2.1", "==2.0"),
1449 ("2.1", "==2.0.0"),
1450 ("2.0", "==2.0+deadbeef"),
1451 ("2.0", "==3.*"),
1453 ("2.1", "==2.0.*"),
1454 ("2.0", "!=2"),
1456 ("2.0", "!=2.0"),
1457 ("2.0", "!=2.0.0"),
1458 ("2.0+deadbeef", "!=2"),
1459 ("2.0+deadbeef", "!=2.0"),
1460 ("2.0+deadbeef", "!=2.0.0"),
1461 ("2.0+deadbeef", "!=2+deadbeef"),
1462 ("2.0+deadbeef", "!=2.0+deadbeef"),
1463 ("2.0+deadbeef", "!=2.0.0+deadbeef"),
1464 ("2.0+deadbeef.0", "!=2.0.0+deadbeef.00"),
1465 ("2.dev1", "!=2.*"),
1467 ("2a1", "!=2.*"),
1468 ("2a1.post1", "!=2.*"),
1469 ("2b1", "!=2.*"),
1470 ("2b1.dev1", "!=2.*"),
1471 ("2c1", "!=2.*"),
1472 ("2c1.post1.dev1", "!=2.*"),
1473 ("2c1.post1.dev1", "!=2.0.*"),
1474 ("2rc1", "!=2.*"),
1475 ("2rc1", "!=2.0.*"),
1476 ("2", "!=2.*"),
1477 ("2", "!=2.0.*"),
1478 ("2.0", "!=2.*"),
1479 ("2.0.0", "!=2.*"),
1480 ("2.0.dev1", ">=2"),
1482 ("2.0a1", ">=2"),
1483 ("2.0a1.dev1", ">=2"),
1484 ("2.0b1", ">=2"),
1485 ("2.0b1.post1", ">=2"),
1486 ("2.0c1", ">=2"),
1487 ("2.0c1.post1.dev1", ">=2"),
1488 ("2.0rc1", ">=2"),
1489 ("1", ">=2"),
1490 ("2.0.post1", "<=2"),
1492 ("2.0.post1.dev1", "<=2"),
1493 ("3", "<=2"),
1494 ("1", ">2"),
1496 ("2.0.dev1", ">2"),
1497 ("2.0a1", ">2"),
1498 ("2.0a1.post1", ">2"),
1499 ("2.0b1", ">2"),
1500 ("2.0b1.dev1", ">2"),
1501 ("2.0c1", ">2"),
1502 ("2.0c1.post1.dev1", ">2"),
1503 ("2.0rc1", ">2"),
1504 ("2.0", ">2"),
1505 ("2.post2", ">2"),
1506 ("2.0.post1", ">2"),
1507 ("2.0.post1.dev1", ">2"),
1508 ("2.0+local.version", ">2"),
1509 ("2.0.dev1", "<2"),
1511 ("2.0a1", "<2"),
1512 ("2.0a1.post1", "<2"),
1513 ("2.0b1", "<2"),
1514 ("2.0b2.dev1", "<2"),
1515 ("2.0c1", "<2"),
1516 ("2.0c1.post1.dev1", "<2"),
1517 ("2.0rc1", "<2"),
1518 ("2.0", "<2"),
1519 ("2.post1", "<2"),
1520 ("2.post1.dev1", "<2"),
1521 ("3", "<2"),
1522 ("2.0", "~=1.0"),
1524 ("1.1.0", "~=1.0.0"),
1525 ("1.1.post1", "~=1.0.0"),
1526 ("1.0", "~=2!1.0"),
1528 ("2!1.0", "~=1.0"),
1529 ("2!1.0", "==1.0"),
1530 ("1.0", "==2!1.0"),
1531 ("2!1.0", "==1.*"),
1532 ("1.0", "==2!1.*"),
1533 ("2!1.0", "!=2!1.0"),
1534 ];
1535 for (version, specifier) in pairs {
1536 assert!(
1537 !VersionSpecifier::from_str(specifier)
1538 .unwrap()
1539 .contains(&Version::from_str(version).unwrap()),
1540 "{version} {specifier}"
1541 );
1542 }
1543 }
1544
1545 #[test]
1546 fn test_parse_version_specifiers() {
1547 let result = VersionSpecifiers::from_str("~= 0.9, >= 1.0, != 1.3.4.*, < 2.0").unwrap();
1548 assert_eq!(
1549 result.0.as_ref(),
1550 [
1551 VersionSpecifier {
1552 operator: Operator::TildeEqual,
1553 version: Version::new([0, 9]),
1554 },
1555 VersionSpecifier {
1556 operator: Operator::GreaterThanEqual,
1557 version: Version::new([1, 0]),
1558 },
1559 VersionSpecifier {
1560 operator: Operator::NotEqualStar,
1561 version: Version::new([1, 3, 4]),
1562 },
1563 VersionSpecifier {
1564 operator: Operator::LessThan,
1565 version: Version::new([2, 0]),
1566 }
1567 ]
1568 );
1569 }
1570
1571 #[test]
1572 fn test_parse_error() {
1573 let result = VersionSpecifiers::from_str("~= 0.9, %= 1.0, != 1.3.4.*");
1574 assert_eq!(
1575 result.unwrap_err().to_string(),
1576 indoc! {r"
1577 Failed to parse version: Unexpected end of version specifier, expected operator:
1578 ~= 0.9, %= 1.0, != 1.3.4.*
1579 ^^^^^^^
1580 "}
1581 );
1582 }
1583
1584 #[test]
1585 fn test_parse_specifier_missing_operator_error() {
1586 let result = VersionSpecifiers::from_str("3.12");
1587 assert_eq!(
1588 result.unwrap_err().to_string(),
1589 indoc! {"
1590 Failed to parse version: Unexpected end of version specifier, expected operator. Did you mean `==3.12`?:
1591 3.12
1592 ^^^^
1593 "}
1594 );
1595 }
1596
1597 #[test]
1598 fn test_parse_specifier_missing_operator_invalid_version_error() {
1599 let result = VersionSpecifiers::from_str("blergh");
1600 assert_eq!(
1601 result.unwrap_err().to_string(),
1602 indoc! {r"
1603 Failed to parse version: Unexpected end of version specifier, expected operator:
1604 blergh
1605 ^^^^^^
1606 "}
1607 );
1608 }
1609
1610 #[test]
1611 fn test_non_star_after_star() {
1612 let result = VersionSpecifiers::from_str("== 0.9.*.1");
1613 assert_eq!(
1614 result.unwrap_err().inner.err,
1615 ParseErrorKind::InvalidVersion(version::PatternErrorKind::WildcardNotTrailing.into())
1616 .into(),
1617 );
1618 }
1619
1620 #[test]
1621 fn test_star_wrong_operator() {
1622 let result = VersionSpecifiers::from_str(">= 0.9.1.*");
1623 assert_eq!(
1624 result.unwrap_err().inner.err,
1625 ParseErrorKind::InvalidSpecifier(
1626 BuildErrorKind::OperatorWithStar {
1627 operator: Operator::GreaterThanEqual,
1628 }
1629 .into()
1630 )
1631 .into(),
1632 );
1633 }
1634
1635 #[test]
1636 fn test_invalid_word() {
1637 let result = VersionSpecifiers::from_str("blergh");
1638 assert_eq!(
1639 result.unwrap_err().inner.err,
1640 ParseErrorKind::MissingOperator(VersionOperatorBuildError {
1641 version_pattern: None
1642 })
1643 .into(),
1644 );
1645 }
1646
1647 #[test]
1649 fn test_invalid_specifier() {
1650 let specifiers = [
1651 (
1653 "2.0",
1654 ParseErrorKind::MissingOperator(VersionOperatorBuildError {
1655 version_pattern: VersionPattern::from_str("2.0").ok(),
1656 })
1657 .into(),
1658 ),
1659 (
1661 "=>2.0",
1662 ParseErrorKind::InvalidOperator(OperatorParseError {
1663 got: "=>".to_string(),
1664 })
1665 .into(),
1666 ),
1667 ("==", ParseErrorKind::MissingVersion.into()),
1669 (
1671 "~=1.0+5",
1672 ParseErrorKind::InvalidSpecifier(
1673 BuildErrorKind::OperatorLocalCombo {
1674 operator: Operator::TildeEqual,
1675 version: Version::new([1, 0])
1676 .with_local_segments(vec![LocalSegment::Number(5)]),
1677 }
1678 .into(),
1679 )
1680 .into(),
1681 ),
1682 (
1683 ">=1.0+deadbeef",
1684 ParseErrorKind::InvalidSpecifier(
1685 BuildErrorKind::OperatorLocalCombo {
1686 operator: Operator::GreaterThanEqual,
1687 version: Version::new([1, 0]).with_local_segments(vec![
1688 LocalSegment::String("deadbeef".to_string()),
1689 ]),
1690 }
1691 .into(),
1692 )
1693 .into(),
1694 ),
1695 (
1696 "<=1.0+abc123",
1697 ParseErrorKind::InvalidSpecifier(
1698 BuildErrorKind::OperatorLocalCombo {
1699 operator: Operator::LessThanEqual,
1700 version: Version::new([1, 0])
1701 .with_local_segments(vec![LocalSegment::String("abc123".to_string())]),
1702 }
1703 .into(),
1704 )
1705 .into(),
1706 ),
1707 (
1708 ">1.0+watwat",
1709 ParseErrorKind::InvalidSpecifier(
1710 BuildErrorKind::OperatorLocalCombo {
1711 operator: Operator::GreaterThan,
1712 version: Version::new([1, 0])
1713 .with_local_segments(vec![LocalSegment::String("watwat".to_string())]),
1714 }
1715 .into(),
1716 )
1717 .into(),
1718 ),
1719 (
1720 "<1.0+1.0",
1721 ParseErrorKind::InvalidSpecifier(
1722 BuildErrorKind::OperatorLocalCombo {
1723 operator: Operator::LessThan,
1724 version: Version::new([1, 0]).with_local_segments(vec![
1725 LocalSegment::Number(1),
1726 LocalSegment::Number(0),
1727 ]),
1728 }
1729 .into(),
1730 )
1731 .into(),
1732 ),
1733 (
1735 "~=1.0.*",
1736 ParseErrorKind::InvalidSpecifier(
1737 BuildErrorKind::OperatorWithStar {
1738 operator: Operator::TildeEqual,
1739 }
1740 .into(),
1741 )
1742 .into(),
1743 ),
1744 (
1745 ">=1.0.*",
1746 ParseErrorKind::InvalidSpecifier(
1747 BuildErrorKind::OperatorWithStar {
1748 operator: Operator::GreaterThanEqual,
1749 }
1750 .into(),
1751 )
1752 .into(),
1753 ),
1754 (
1755 "<=1.0.*",
1756 ParseErrorKind::InvalidSpecifier(
1757 BuildErrorKind::OperatorWithStar {
1758 operator: Operator::LessThanEqual,
1759 }
1760 .into(),
1761 )
1762 .into(),
1763 ),
1764 (
1765 ">1.0.*",
1766 ParseErrorKind::InvalidSpecifier(
1767 BuildErrorKind::OperatorWithStar {
1768 operator: Operator::GreaterThan,
1769 }
1770 .into(),
1771 )
1772 .into(),
1773 ),
1774 (
1775 "<1.0.*",
1776 ParseErrorKind::InvalidSpecifier(
1777 BuildErrorKind::OperatorWithStar {
1778 operator: Operator::LessThan,
1779 }
1780 .into(),
1781 )
1782 .into(),
1783 ),
1784 (
1787 "==1.0.*+5",
1788 ParseErrorKind::InvalidVersion(
1789 version::PatternErrorKind::WildcardNotTrailing.into(),
1790 )
1791 .into(),
1792 ),
1793 (
1794 "!=1.0.*+deadbeef",
1795 ParseErrorKind::InvalidVersion(
1796 version::PatternErrorKind::WildcardNotTrailing.into(),
1797 )
1798 .into(),
1799 ),
1800 (
1803 "==2.0a1.*",
1804 ParseErrorKind::InvalidVersion(
1805 version::ErrorKind::UnexpectedEnd {
1806 version: "2.0a1".to_string(),
1807 remaining: ".*".to_string(),
1808 }
1809 .into(),
1810 )
1811 .into(),
1812 ),
1813 (
1814 "!=2.0a1.*",
1815 ParseErrorKind::InvalidVersion(
1816 version::ErrorKind::UnexpectedEnd {
1817 version: "2.0a1".to_string(),
1818 remaining: ".*".to_string(),
1819 }
1820 .into(),
1821 )
1822 .into(),
1823 ),
1824 (
1825 "==2.0.post1.*",
1826 ParseErrorKind::InvalidVersion(
1827 version::ErrorKind::UnexpectedEnd {
1828 version: "2.0.post1".to_string(),
1829 remaining: ".*".to_string(),
1830 }
1831 .into(),
1832 )
1833 .into(),
1834 ),
1835 (
1836 "!=2.0.post1.*",
1837 ParseErrorKind::InvalidVersion(
1838 version::ErrorKind::UnexpectedEnd {
1839 version: "2.0.post1".to_string(),
1840 remaining: ".*".to_string(),
1841 }
1842 .into(),
1843 )
1844 .into(),
1845 ),
1846 (
1847 "==2.0.dev1.*",
1848 ParseErrorKind::InvalidVersion(
1849 version::ErrorKind::UnexpectedEnd {
1850 version: "2.0.dev1".to_string(),
1851 remaining: ".*".to_string(),
1852 }
1853 .into(),
1854 )
1855 .into(),
1856 ),
1857 (
1858 "!=2.0.dev1.*",
1859 ParseErrorKind::InvalidVersion(
1860 version::ErrorKind::UnexpectedEnd {
1861 version: "2.0.dev1".to_string(),
1862 remaining: ".*".to_string(),
1863 }
1864 .into(),
1865 )
1866 .into(),
1867 ),
1868 (
1869 "==1.0+5.*",
1870 ParseErrorKind::InvalidVersion(
1871 version::ErrorKind::LocalEmpty { precursor: '.' }.into(),
1872 )
1873 .into(),
1874 ),
1875 (
1876 "!=1.0+deadbeef.*",
1877 ParseErrorKind::InvalidVersion(
1878 version::ErrorKind::LocalEmpty { precursor: '.' }.into(),
1879 )
1880 .into(),
1881 ),
1882 (
1884 "==1.0.*.5",
1885 ParseErrorKind::InvalidVersion(
1886 version::PatternErrorKind::WildcardNotTrailing.into(),
1887 )
1888 .into(),
1889 ),
1890 (
1892 "~=1",
1893 ParseErrorKind::InvalidSpecifier(BuildErrorKind::CompatibleRelease.into()).into(),
1894 ),
1895 (
1897 "==1.0.dev1.*",
1898 ParseErrorKind::InvalidVersion(
1899 version::ErrorKind::UnexpectedEnd {
1900 version: "1.0.dev1".to_string(),
1901 remaining: ".*".to_string(),
1902 }
1903 .into(),
1904 )
1905 .into(),
1906 ),
1907 (
1908 "!=1.0.dev1.*",
1909 ParseErrorKind::InvalidVersion(
1910 version::ErrorKind::UnexpectedEnd {
1911 version: "1.0.dev1".to_string(),
1912 remaining: ".*".to_string(),
1913 }
1914 .into(),
1915 )
1916 .into(),
1917 ),
1918 ];
1919 for (specifier, error) in specifiers {
1920 assert_eq!(VersionSpecifier::from_str(specifier).unwrap_err(), error);
1921 }
1922 }
1923
1924 #[test]
1925 fn test_display_start() {
1926 assert_eq!(
1927 VersionSpecifier::from_str("== 1.1.*")
1928 .unwrap()
1929 .to_string(),
1930 "==1.1.*"
1931 );
1932 assert_eq!(
1933 VersionSpecifier::from_str("!= 1.1.*")
1934 .unwrap()
1935 .to_string(),
1936 "!=1.1.*"
1937 );
1938 }
1939
1940 #[test]
1941 fn test_version_specifiers_str() {
1942 assert_eq!(
1943 VersionSpecifiers::from_str(">= 3.7").unwrap().to_string(),
1944 ">=3.7"
1945 );
1946 assert_eq!(
1947 VersionSpecifiers::from_str(">=3.7, < 4.0, != 3.9.0")
1948 .unwrap()
1949 .to_string(),
1950 ">=3.7, !=3.9.0, <4.0"
1951 );
1952 }
1953
1954 #[test]
1957 fn test_version_specifiers_empty() {
1958 assert_eq!(VersionSpecifiers::from_str("").unwrap().to_string(), "");
1959 }
1960
1961 #[test]
1965 fn non_ascii_version_specifier() {
1966 let s = "💩";
1967 let err = s.parse::<VersionSpecifiers>().unwrap_err();
1968 assert_eq!(err.inner.start, 0);
1969 assert_eq!(err.inner.end, 4);
1970
1971 let s = ">=3.7, <4.0,>5.%";
1975 let err = s.parse::<VersionSpecifiers>().unwrap_err();
1976 assert_eq!(err.inner.start, 12);
1977 assert_eq!(err.inner.end, 16);
1978 let s = ">=3.7,\u{3000}<4.0,>5.%";
1987 let err = s.parse::<VersionSpecifiers>().unwrap_err();
1988 assert_eq!(err.inner.start, 14);
1989 assert_eq!(err.inner.end, 18);
1990 }
1991
1992 #[test]
1995 fn error_message_version_specifiers_parse_error() {
1996 let specs = ">=1.2.3, 5.4.3, >=3.4.5";
1997 let err = VersionSpecifierParseError {
1998 kind: Box::new(ParseErrorKind::MissingOperator(VersionOperatorBuildError {
1999 version_pattern: VersionPattern::from_str("5.4.3").ok(),
2000 })),
2001 };
2002 let inner = Box::new(VersionSpecifiersParseErrorInner {
2003 err,
2004 line: specs.to_string(),
2005 start: 8,
2006 end: 14,
2007 });
2008 let err = VersionSpecifiersParseError { inner };
2009 assert_eq!(err, VersionSpecifiers::from_str(specs).unwrap_err());
2010 assert_eq!(
2011 err.to_string(),
2012 "\
2013Failed to parse version: Unexpected end of version specifier, expected operator. Did you mean `==5.4.3`?:
2014>=1.2.3, 5.4.3, >=3.4.5
2015 ^^^^^^
2016"
2017 );
2018 }
2019
2020 #[test]
2023 fn error_message_version_specifier_build_error() {
2024 let err = VersionSpecifierBuildError {
2025 kind: Box::new(BuildErrorKind::CompatibleRelease),
2026 };
2027 let op = Operator::TildeEqual;
2028 let v = Version::new([5]);
2029 let vpat = VersionPattern::verbatim(v);
2030 assert_eq!(err, VersionSpecifier::from_pattern(op, vpat).unwrap_err());
2031 assert_eq!(
2032 err.to_string(),
2033 "The ~= operator requires at least two segments in the release version"
2034 );
2035 }
2036
2037 #[test]
2040 fn error_message_version_specifier_parse_error() {
2041 let err = VersionSpecifierParseError {
2042 kind: Box::new(ParseErrorKind::InvalidSpecifier(
2043 VersionSpecifierBuildError {
2044 kind: Box::new(BuildErrorKind::CompatibleRelease),
2045 },
2046 )),
2047 };
2048 assert_eq!(err, VersionSpecifier::from_str("~=5").unwrap_err());
2049 assert_eq!(
2050 err.to_string(),
2051 "The ~= operator requires at least two segments in the release version"
2052 );
2053 }
2054}