1use std::borrow::Cow;
2use std::cmp::Ordering;
3use std::fmt::Formatter;
4use std::hash::{Hash, Hasher};
5use std::ops::Bound;
6use std::str::FromStr;
7
8use crate::{
9 Operator, OperatorParseError, Version, VersionPattern, VersionPatternParseError, version,
10};
11use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
12#[cfg(feature = "tracing")]
13use tracing::warn;
14
15#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)]
31#[cfg_attr(
32 feature = "rkyv",
33 derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)
34)]
35#[cfg_attr(feature = "rkyv", rkyv(derive(Debug)))]
36pub struct VersionSpecifiers(Box<[VersionSpecifier]>);
37
38impl std::ops::Deref for VersionSpecifiers {
39 type Target = [VersionSpecifier];
40
41 fn deref(&self) -> &Self::Target {
42 &self.0
43 }
44}
45
46impl VersionSpecifiers {
47 pub fn empty() -> Self {
49 Self(Box::new([]))
50 }
51
52 pub fn len(&self) -> usize {
54 self.0.len()
55 }
56
57 pub fn contains(&self, version: &Version) -> bool {
59 self.iter().all(|specifier| specifier.contains(version))
60 }
61
62 pub fn is_empty(&self) -> bool {
64 self.0.is_empty()
65 }
66
67 fn from_unsorted(mut specifiers: Vec<VersionSpecifier>) -> Self {
69 specifiers.sort_by(|a, b| {
75 a.version()
76 .cmp(b.version())
77 .then_with(|| a.operator().cmp(b.operator()))
78 });
79 Self(specifiers.into_boxed_slice())
80 }
81
82 pub fn from_release_only_bounds<'a>(
86 mut bounds: impl Iterator<Item = (&'a Bound<Version>, &'a Bound<Version>)>,
87 ) -> Self {
88 let mut specifiers = Vec::new();
89
90 let Some((start, mut next)) = bounds.next() else {
91 return Self::empty();
92 };
93
94 for (lower, upper) in bounds {
96 let specifier = match (next, lower) {
97 (Bound::Excluded(prev), Bound::Excluded(lower)) if prev == lower => {
99 Some(VersionSpecifier::not_equals_version(prev.clone()))
100 }
101 (Bound::Excluded(prev), Bound::Included(lower)) => {
103 match *prev.only_release_trimmed().release() {
104 [major] if *lower.only_release_trimmed().release() == [major, 1] => {
105 Some(VersionSpecifier::not_equals_star_version(Version::new([
106 major, 0,
107 ])))
108 }
109 [major, minor]
110 if *lower.only_release_trimmed().release() == [major, minor + 1] =>
111 {
112 Some(VersionSpecifier::not_equals_star_version(Version::new([
113 major, minor,
114 ])))
115 }
116 _ => None,
117 }
118 }
119 _ => None,
120 };
121 if let Some(specifier) = specifier {
122 specifiers.push(specifier);
123 } else {
124 #[cfg(feature = "tracing")]
125 warn!(
126 "Ignoring unsupported gap in `requires-python` version: {next:?} -> {lower:?}"
127 );
128 }
129 next = upper;
130 }
131 let end = next;
132
133 specifiers.extend(VersionSpecifier::from_release_only_bounds((start, end)));
135
136 Self::from_unsorted(specifiers)
137 }
138}
139
140impl FromIterator<VersionSpecifier> for VersionSpecifiers {
141 fn from_iter<T: IntoIterator<Item = VersionSpecifier>>(iter: T) -> Self {
142 Self::from_unsorted(iter.into_iter().collect())
143 }
144}
145
146impl IntoIterator for VersionSpecifiers {
147 type Item = VersionSpecifier;
148 type IntoIter = std::vec::IntoIter<VersionSpecifier>;
149
150 fn into_iter(self) -> Self::IntoIter {
151 self.0.into_vec().into_iter()
152 }
153}
154
155impl FromStr for VersionSpecifiers {
156 type Err = VersionSpecifiersParseError;
157
158 fn from_str(s: &str) -> Result<Self, Self::Err> {
159 parse_version_specifiers(s).map(Self::from_unsorted)
160 }
161}
162
163impl From<VersionSpecifier> for VersionSpecifiers {
164 fn from(specifier: VersionSpecifier) -> Self {
165 Self(Box::new([specifier]))
166 }
167}
168
169impl std::fmt::Display for VersionSpecifiers {
170 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
171 for (idx, version_specifier) in self.0.iter().enumerate() {
172 if idx == 0 {
175 write!(f, "{version_specifier}")?;
176 } else {
177 write!(f, ", {version_specifier}")?;
178 }
179 }
180 Ok(())
181 }
182}
183
184impl Default for VersionSpecifiers {
185 fn default() -> Self {
186 Self::empty()
187 }
188}
189
190impl<'de> Deserialize<'de> for VersionSpecifiers {
191 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
192 where
193 D: Deserializer<'de>,
194 {
195 struct Visitor;
196
197 impl de::Visitor<'_> for Visitor {
198 type Value = VersionSpecifiers;
199
200 fn expecting(&self, f: &mut Formatter) -> std::fmt::Result {
201 f.write_str("a string")
202 }
203
204 fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
205 VersionSpecifiers::from_str(v).map_err(de::Error::custom)
206 }
207 }
208
209 deserializer.deserialize_str(Visitor)
210 }
211}
212
213impl Serialize for VersionSpecifiers {
214 #[allow(unstable_name_collisions)]
215 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
216 where
217 S: Serializer,
218 {
219 serializer.serialize_str(
220 &self
221 .iter()
222 .map(ToString::to_string)
223 .collect::<Vec<String>>()
224 .join(","),
225 )
226 }
227}
228
229#[derive(Debug, Eq, PartialEq, Clone)]
231pub struct VersionSpecifiersParseError {
232 inner: Box<VersionSpecifiersParseErrorInner>,
235}
236
237#[derive(Debug, Eq, PartialEq, Clone)]
238struct VersionSpecifiersParseErrorInner {
239 err: VersionSpecifierParseError,
241 line: String,
243 start: usize,
246 end: usize,
249}
250
251impl std::fmt::Display for VersionSpecifiersParseError {
252 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
253 use unicode_width::UnicodeWidthStr;
254
255 let VersionSpecifiersParseErrorInner {
256 ref err,
257 ref line,
258 start,
259 end,
260 } = *self.inner;
261 writeln!(f, "Failed to parse version: {err}:")?;
262 writeln!(f, "{line}")?;
263 let indent = line[..start].width();
264 let point = line[start..end].width();
265 writeln!(f, "{}{}", " ".repeat(indent), "^".repeat(point))?;
266 Ok(())
267 }
268}
269
270impl VersionSpecifiersParseError {
271 pub fn line(&self) -> &String {
273 &self.inner.line
274 }
275}
276
277impl std::error::Error for VersionSpecifiersParseError {}
278
279#[derive(Debug, Clone)]
296#[cfg_attr(
297 feature = "rkyv",
298 derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)
299)]
300#[cfg_attr(feature = "rkyv", rkyv(derive(Debug)))]
301pub struct VersionSpecifier {
302 pub(crate) operator: Operator,
304 pub(crate) version: Version,
306}
307
308impl PartialEq for VersionSpecifier {
309 fn eq(&self, other: &Self) -> bool {
310 if self.operator != other.operator {
311 return false;
312 }
313 if self.operator == Operator::TildeEqual
315 && self.version.release().len() != other.version.release().len()
316 {
317 return false;
318 }
319 self.version == other.version
320 }
321}
322
323impl Eq for VersionSpecifier {}
324
325impl Hash for VersionSpecifier {
326 fn hash<H: Hasher>(&self, state: &mut H) {
327 self.operator.hash(state);
328 if self.operator == Operator::TildeEqual {
331 self.version.release().len().hash(state);
332 }
333 self.version.hash(state);
334 }
335}
336
337impl PartialOrd for VersionSpecifier {
338 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
339 Some(self.cmp(other))
340 }
341}
342
343impl Ord for VersionSpecifier {
344 fn cmp(&self, other: &Self) -> Ordering {
345 self.operator
346 .cmp(&other.operator)
347 .then_with(|| self.version.cmp(&other.version))
348 .then_with(|| {
349 if self.operator == Operator::TildeEqual {
351 self.version
352 .release()
353 .len()
354 .cmp(&other.version.release().len())
355 } else {
356 Ordering::Equal
357 }
358 })
359 }
360}
361
362impl<'de> Deserialize<'de> for VersionSpecifier {
363 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
364 where
365 D: Deserializer<'de>,
366 {
367 struct Visitor;
368
369 impl de::Visitor<'_> for Visitor {
370 type Value = VersionSpecifier;
371
372 fn expecting(&self, f: &mut Formatter) -> std::fmt::Result {
373 f.write_str("a string")
374 }
375
376 fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
377 VersionSpecifier::from_str(v).map_err(de::Error::custom)
378 }
379 }
380
381 deserializer.deserialize_str(Visitor)
382 }
383}
384
385impl Serialize for VersionSpecifier {
387 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
388 where
389 S: Serializer,
390 {
391 serializer.collect_str(self)
392 }
393}
394
395impl VersionSpecifier {
396 pub fn from_pattern(
399 operator: Operator,
400 version_pattern: VersionPattern,
401 ) -> Result<Self, VersionSpecifierBuildError> {
402 let star = version_pattern.is_wildcard();
403 let version = version_pattern.into_version();
404
405 let operator = if star {
407 match operator.to_star() {
408 Some(starop) => starop,
409 None => {
410 return Err(BuildErrorKind::OperatorWithStar { operator }.into());
411 }
412 }
413 } else {
414 operator
415 };
416
417 Self::from_version(operator, version)
418 }
419
420 pub fn from_version(
422 operator: Operator,
423 version: Version,
424 ) -> Result<Self, VersionSpecifierBuildError> {
425 if version.is_local() && !operator.is_local_compatible() {
427 return Err(BuildErrorKind::OperatorLocalCombo { operator, version }.into());
428 }
429
430 if operator == Operator::TildeEqual && version.release().len() < 2 {
431 return Err(BuildErrorKind::CompatibleRelease.into());
432 }
433
434 Ok(Self { operator, version })
435 }
436
437 #[must_use]
457 pub fn only_release(self) -> Self {
458 Self {
459 operator: self.operator,
460 version: self.version.only_release(),
461 }
462 }
463
464 #[must_use]
466 pub fn only_minor_release(&self) -> Self {
467 Self {
468 operator: self.operator,
469 version: self.version.only_minor_release(),
470 }
471 }
472
473 pub fn equals_version(version: Version) -> Self {
475 Self {
476 operator: Operator::Equal,
477 version,
478 }
479 }
480
481 pub fn equals_star_version(version: Version) -> Self {
483 Self {
484 operator: Operator::EqualStar,
485 version,
486 }
487 }
488
489 pub fn not_equals_star_version(version: Version) -> Self {
491 Self {
492 operator: Operator::NotEqualStar,
493 version,
494 }
495 }
496
497 pub fn not_equals_version(version: Version) -> Self {
499 Self {
500 operator: Operator::NotEqual,
501 version,
502 }
503 }
504
505 pub fn greater_than_equal_version(version: Version) -> Self {
507 Self {
508 operator: Operator::GreaterThanEqual,
509 version,
510 }
511 }
512 pub fn greater_than_version(version: Version) -> Self {
514 Self {
515 operator: Operator::GreaterThan,
516 version,
517 }
518 }
519
520 pub fn less_than_equal_version(version: Version) -> Self {
522 Self {
523 operator: Operator::LessThanEqual,
524 version,
525 }
526 }
527
528 pub fn less_than_version(version: Version) -> Self {
530 Self {
531 operator: Operator::LessThan,
532 version,
533 }
534 }
535
536 pub fn operator(&self) -> &Operator {
538 &self.operator
539 }
540
541 pub fn version(&self) -> &Version {
543 &self.version
544 }
545
546 pub fn into_parts(self) -> (Operator, Version) {
548 (self.operator, self.version)
549 }
550
551 pub fn any_prerelease(&self) -> bool {
553 self.version.any_prerelease()
554 }
555
556 pub fn from_release_only_bounds(
560 bounds: (&Bound<Version>, &Bound<Version>),
561 ) -> impl Iterator<Item = Self> {
562 let (b1, b2) = match bounds {
563 (Bound::Included(v1), Bound::Included(v2)) if v1 == v2 => {
564 (Some(Self::equals_version(v1.clone())), None)
565 }
566 (Bound::Included(v1), Bound::Excluded(v2)) => {
568 match *v1.only_release_trimmed().release() {
569 [major] if *v2.only_release_trimmed().release() == [major, 1] => {
570 let version = Version::new([major, 0]);
571 (Some(Self::equals_star_version(version)), None)
572 }
573 [major, minor]
574 if *v2.only_release_trimmed().release() == [major, minor + 1] =>
575 {
576 let version = Version::new([major, minor]);
577 (Some(Self::equals_star_version(version)), None)
578 }
579 _ => (
580 Self::from_lower_bound(&Bound::Included(v1.clone())),
581 Self::from_upper_bound(&Bound::Excluded(v2.clone())),
582 ),
583 }
584 }
585 (lower, upper) => (Self::from_lower_bound(lower), Self::from_upper_bound(upper)),
586 };
587
588 b1.into_iter().chain(b2)
589 }
590
591 pub fn from_lower_bound(bound: &Bound<Version>) -> Option<Self> {
593 match bound {
594 Bound::Included(version) => {
595 Some(Self::from_version(Operator::GreaterThanEqual, version.clone()).unwrap())
596 }
597 Bound::Excluded(version) => {
598 Some(Self::from_version(Operator::GreaterThan, version.clone()).unwrap())
599 }
600 Bound::Unbounded => None,
601 }
602 }
603
604 pub fn from_upper_bound(bound: &Bound<Version>) -> Option<Self> {
606 match bound {
607 Bound::Included(version) => {
608 Some(Self::from_version(Operator::LessThanEqual, version.clone()).unwrap())
609 }
610 Bound::Excluded(version) => {
611 Some(Self::from_version(Operator::LessThan, version.clone()).unwrap())
612 }
613 Bound::Unbounded => None,
614 }
615 }
616
617 pub fn contains(&self, version: &Version) -> bool {
625 let this = self.version();
629 let other = if this.local().is_empty() && !version.local().is_empty() {
630 Cow::Owned(version.clone().without_local())
631 } else {
632 Cow::Borrowed(version)
633 };
634
635 match self.operator {
636 Operator::Equal => other.as_ref() == this,
637 Operator::EqualStar => {
638 this.epoch() == other.epoch()
639 && self
640 .version
641 .release()
642 .iter()
643 .zip(other.release().iter().chain(std::iter::repeat(&0)))
647 .all(|(this, other)| this == other)
648 }
649 #[allow(deprecated)]
650 Operator::ExactEqual => {
651 #[cfg(feature = "tracing")]
652 {
653 warn!("Using arbitrary equality (`===`) is discouraged");
654 }
655 self.version.to_string() == version.to_string()
656 }
657 Operator::NotEqual => this != other.as_ref(),
658 Operator::NotEqualStar => {
659 this.epoch() != other.epoch()
660 || !this
661 .release()
662 .iter()
663 .zip(other.release().iter().chain(std::iter::repeat(&0)))
667 .all(|(this, other)| this == other)
668 }
669 Operator::TildeEqual => {
670 assert!(this.release().len() > 1);
675 if this.epoch() != other.epoch() {
676 return false;
677 }
678
679 if !this.release()[..this.release().len() - 1]
680 .iter()
681 .zip(&*other.release())
682 .all(|(this, other)| this == other)
683 {
684 return false;
685 }
686
687 other.as_ref() >= this
690 }
691 Operator::GreaterThan => {
692 if other.epoch() > this.epoch() {
693 return true;
694 }
695
696 if version::compare_release(&this.release(), &other.release()) == Ordering::Equal {
697 if !this.is_post() && other.is_post() {
702 return false;
703 }
704
705 if other.is_local() {
707 return false;
708 }
709 }
710
711 other.as_ref() > this
712 }
713 Operator::GreaterThanEqual => other.as_ref() >= this,
714 Operator::LessThan => {
715 if other.epoch() < this.epoch() {
716 return true;
717 }
718
719 if version::compare_release(&this.release(), &other.release()) == Ordering::Equal
724 && !this.any_prerelease()
725 && other.any_prerelease()
726 {
727 return false;
728 }
729
730 other.as_ref() < this
731 }
732 Operator::LessThanEqual => other.as_ref() <= this,
733 }
734 }
735
736 pub fn has_lower_bound(&self) -> bool {
738 match self.operator() {
739 Operator::Equal
740 | Operator::EqualStar
741 | Operator::ExactEqual
742 | Operator::TildeEqual
743 | Operator::GreaterThan
744 | Operator::GreaterThanEqual => true,
745 Operator::LessThanEqual
746 | Operator::LessThan
747 | Operator::NotEqualStar
748 | Operator::NotEqual => false,
749 }
750 }
751}
752
753impl FromStr for VersionSpecifier {
754 type Err = VersionSpecifierParseError;
755
756 fn from_str(spec: &str) -> Result<Self, Self::Err> {
758 let mut s = unscanny::Scanner::new(spec);
759 s.eat_while(|c: char| c.is_whitespace());
760 let operator = s.eat_while(['=', '!', '~', '<', '>']);
762 if operator.is_empty() {
763 s.eat_while(|c: char| c.is_whitespace());
766 let version = s.eat_while(|c: char| !c.is_whitespace());
767 s.eat_while(|c: char| c.is_whitespace());
768 return Err(ParseErrorKind::MissingOperator(VersionOperatorBuildError {
769 version_pattern: VersionPattern::from_str(version).ok(),
770 })
771 .into());
772 }
773 let operator = Operator::from_str(operator).map_err(ParseErrorKind::InvalidOperator)?;
774 s.eat_while(|c: char| c.is_whitespace());
775 let version = s.eat_while(|c: char| !c.is_whitespace());
776 if version.is_empty() {
777 return Err(ParseErrorKind::MissingVersion.into());
778 }
779 let vpat = version.parse().map_err(ParseErrorKind::InvalidVersion)?;
780 let version_specifier =
781 Self::from_pattern(operator, vpat).map_err(ParseErrorKind::InvalidSpecifier)?;
782 s.eat_while(|c: char| c.is_whitespace());
783 if !s.done() {
784 return Err(ParseErrorKind::InvalidTrailing(s.after().to_string()).into());
785 }
786 Ok(version_specifier)
787 }
788}
789
790impl std::fmt::Display for VersionSpecifier {
791 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
792 if self.operator == Operator::EqualStar || self.operator == Operator::NotEqualStar {
793 return write!(f, "{}{}.*", self.operator, self.version);
794 }
795 write!(f, "{}{}", self.operator, self.version)
796 }
797}
798
799#[derive(Clone, Debug, Eq, PartialEq)]
801pub struct VersionSpecifierBuildError {
802 kind: Box<BuildErrorKind>,
805}
806
807impl std::error::Error for VersionSpecifierBuildError {}
808
809impl std::fmt::Display for VersionSpecifierBuildError {
810 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
811 match *self.kind {
812 BuildErrorKind::OperatorLocalCombo {
813 operator: ref op,
814 ref version,
815 } => {
816 let local = version.local();
817 write!(
818 f,
819 "Operator {op} is incompatible with versions \
820 containing non-empty local segments (`+{local}`)",
821 )
822 }
823 BuildErrorKind::OperatorWithStar { operator: ref op } => {
824 write!(
825 f,
826 "Operator {op} cannot be used with a wildcard version specifier",
827 )
828 }
829 BuildErrorKind::CompatibleRelease => {
830 write!(
831 f,
832 "The ~= operator requires at least two segments in the release version"
833 )
834 }
835 }
836 }
837}
838
839#[derive(Clone, Debug, Eq, PartialEq)]
840struct VersionOperatorBuildError {
841 version_pattern: Option<VersionPattern>,
842}
843
844impl std::error::Error for VersionOperatorBuildError {}
845
846impl std::fmt::Display for VersionOperatorBuildError {
847 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
848 write!(f, "Unexpected end of version specifier, expected operator")?;
849 if let Some(version_pattern) = &self.version_pattern {
850 let version_specifier =
851 VersionSpecifier::from_pattern(Operator::Equal, version_pattern.clone()).unwrap();
852 write!(f, ". Did you mean `{version_specifier}`?")?;
853 }
854 Ok(())
855 }
856}
857
858#[derive(Clone, Debug, Eq, PartialEq)]
861enum BuildErrorKind {
862 OperatorLocalCombo {
866 operator: Operator,
868 version: Version,
870 },
871 OperatorWithStar {
874 operator: Operator,
876 },
877 CompatibleRelease,
880}
881
882impl From<BuildErrorKind> for VersionSpecifierBuildError {
883 fn from(kind: BuildErrorKind) -> Self {
884 Self {
885 kind: Box::new(kind),
886 }
887 }
888}
889
890#[derive(Clone, Debug, Eq, PartialEq)]
892pub struct VersionSpecifierParseError {
893 kind: Box<ParseErrorKind>,
896}
897
898impl std::error::Error for VersionSpecifierParseError {}
899
900impl std::fmt::Display for VersionSpecifierParseError {
901 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
902 match *self.kind {
908 ParseErrorKind::InvalidOperator(ref err) => err.fmt(f),
909 ParseErrorKind::InvalidVersion(ref err) => err.fmt(f),
910 ParseErrorKind::InvalidSpecifier(ref err) => err.fmt(f),
911 ParseErrorKind::MissingOperator(ref err) => err.fmt(f),
912 ParseErrorKind::MissingVersion => {
913 write!(f, "Unexpected end of version specifier, expected version")
914 }
915 ParseErrorKind::InvalidTrailing(ref trail) => {
916 write!(f, "Trailing `{trail}` is not allowed")
917 }
918 }
919 }
920}
921
922#[derive(Clone, Debug, Eq, PartialEq)]
925enum ParseErrorKind {
926 InvalidOperator(OperatorParseError),
927 InvalidVersion(VersionPatternParseError),
928 InvalidSpecifier(VersionSpecifierBuildError),
929 MissingOperator(VersionOperatorBuildError),
930 MissingVersion,
931 InvalidTrailing(String),
932}
933
934impl From<ParseErrorKind> for VersionSpecifierParseError {
935 fn from(kind: ParseErrorKind) -> Self {
936 Self {
937 kind: Box::new(kind),
938 }
939 }
940}
941
942fn parse_version_specifiers(
944 spec: &str,
945) -> Result<Vec<VersionSpecifier>, VersionSpecifiersParseError> {
946 let mut version_ranges = Vec::new();
947 if spec.is_empty() {
948 return Ok(version_ranges);
949 }
950 let mut start: usize = 0;
951 let separator = ",";
952 for version_range_spec in spec.split(separator) {
953 match VersionSpecifier::from_str(version_range_spec) {
954 Err(err) => {
955 return Err(VersionSpecifiersParseError {
956 inner: Box::new(VersionSpecifiersParseErrorInner {
957 err,
958 line: spec.to_string(),
959 start,
960 end: start + version_range_spec.len(),
961 }),
962 });
963 }
964 Ok(version_range) => {
965 version_ranges.push(version_range);
966 }
967 }
968 start += version_range_spec.len();
969 start += separator.len();
970 }
971 Ok(version_ranges)
972}
973
974#[derive(Clone, Debug)]
977pub struct TildeVersionSpecifier<'a> {
978 inner: Cow<'a, VersionSpecifier>,
979}
980
981impl<'a> TildeVersionSpecifier<'a> {
982 pub fn from_specifier(specifier: VersionSpecifier) -> Option<Self> {
987 TildeVersionSpecifier::new(Cow::Owned(specifier))
988 }
989
990 pub fn from_specifier_ref(specifier: &'a VersionSpecifier) -> Option<Self> {
994 TildeVersionSpecifier::new(Cow::Borrowed(specifier))
995 }
996
997 fn new(specifier: Cow<'a, VersionSpecifier>) -> Option<Self> {
998 if specifier.operator != Operator::TildeEqual {
999 return None;
1000 }
1001 if specifier.version().release().len() < 2 || specifier.version().release().len() > 3 {
1002 return None;
1003 }
1004 if specifier.version().any_prerelease()
1005 || specifier.version().is_local()
1006 || specifier.version().is_post()
1007 {
1008 return None;
1009 }
1010 Some(Self { inner: specifier })
1011 }
1012
1013 pub fn has_patch(&self) -> bool {
1015 self.inner.version.release().len() == 3
1016 }
1017
1018 pub fn bounding_specifiers(&self) -> (VersionSpecifier, VersionSpecifier) {
1022 let release = self.inner.version().release();
1023 let lower = self.inner.version.clone();
1024 let upper = if self.has_patch() {
1025 Version::new([release[0], release[1] + 1])
1026 } else {
1027 Version::new([release[0] + 1])
1028 };
1029 (
1030 VersionSpecifier::greater_than_equal_version(lower),
1031 VersionSpecifier::less_than_version(upper),
1032 )
1033 }
1034
1035 pub fn with_patch_version(&self, patch: u64) -> TildeVersionSpecifier<'_> {
1037 let mut release = self.inner.version.release().to_vec();
1038 if self.has_patch() {
1039 release.pop();
1040 }
1041 release.push(patch);
1042 TildeVersionSpecifier::from_specifier(
1043 VersionSpecifier::from_version(Operator::TildeEqual, Version::new(release))
1044 .expect("We should always derive a valid new version specifier"),
1045 )
1046 .expect("We should always derive a new tilde version specifier")
1047 }
1048}
1049
1050impl std::fmt::Display for TildeVersionSpecifier<'_> {
1051 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1052 write!(f, "{}", self.inner)
1053 }
1054}
1055
1056#[cfg(test)]
1057mod tests {
1058 use std::{cmp::Ordering, str::FromStr};
1059
1060 use indoc::indoc;
1061
1062 use crate::LocalSegment;
1063
1064 use super::*;
1065
1066 #[test]
1068 fn test_equal() {
1069 let version = Version::from_str("1.1.post1").unwrap();
1070
1071 assert!(
1072 !VersionSpecifier::from_str("== 1.1")
1073 .unwrap()
1074 .contains(&version)
1075 );
1076 assert!(
1077 VersionSpecifier::from_str("== 1.1.post1")
1078 .unwrap()
1079 .contains(&version)
1080 );
1081 assert!(
1082 VersionSpecifier::from_str("== 1.1.*")
1083 .unwrap()
1084 .contains(&version)
1085 );
1086 }
1087
1088 const VERSIONS_ALL: &[&str] = &[
1089 "1.0.dev456",
1091 "1.0a1",
1092 "1.0a2.dev456",
1093 "1.0a12.dev456",
1094 "1.0a12",
1095 "1.0b1.dev456",
1096 "1.0b2",
1097 "1.0b2.post345.dev456",
1098 "1.0b2.post345",
1099 "1.0b2-346",
1100 "1.0c1.dev456",
1101 "1.0c1",
1102 "1.0rc2",
1103 "1.0c3",
1104 "1.0",
1105 "1.0.post456.dev34",
1106 "1.0.post456",
1107 "1.1.dev1",
1108 "1.2+123abc",
1109 "1.2+123abc456",
1110 "1.2+abc",
1111 "1.2+abc123",
1112 "1.2+abc123def",
1113 "1.2+1234.abc",
1114 "1.2+123456",
1115 "1.2.r32+123456",
1116 "1.2.rev33+123456",
1117 "1!1.0.dev456",
1119 "1!1.0a1",
1120 "1!1.0a2.dev456",
1121 "1!1.0a12.dev456",
1122 "1!1.0a12",
1123 "1!1.0b1.dev456",
1124 "1!1.0b2",
1125 "1!1.0b2.post345.dev456",
1126 "1!1.0b2.post345",
1127 "1!1.0b2-346",
1128 "1!1.0c1.dev456",
1129 "1!1.0c1",
1130 "1!1.0rc2",
1131 "1!1.0c3",
1132 "1!1.0",
1133 "1!1.0.post456.dev34",
1134 "1!1.0.post456",
1135 "1!1.1.dev1",
1136 "1!1.2+123abc",
1137 "1!1.2+123abc456",
1138 "1!1.2+abc",
1139 "1!1.2+abc123",
1140 "1!1.2+abc123def",
1141 "1!1.2+1234.abc",
1142 "1!1.2+123456",
1143 "1!1.2.r32+123456",
1144 "1!1.2.rev33+123456",
1145 ];
1146
1147 #[test]
1153 fn test_operators_true() {
1154 let versions: Vec<Version> = VERSIONS_ALL
1155 .iter()
1156 .map(|version| Version::from_str(version).unwrap())
1157 .collect();
1158
1159 let operations = [
1162 versions
1164 .iter()
1165 .enumerate()
1166 .flat_map(|(i, x)| {
1167 versions[i + 1..]
1168 .iter()
1169 .map(move |y| (x, y, Ordering::Less))
1170 })
1171 .collect::<Vec<_>>(),
1172 versions
1174 .iter()
1175 .map(move |x| (x, x, Ordering::Equal))
1176 .collect::<Vec<_>>(),
1177 versions
1179 .iter()
1180 .enumerate()
1181 .flat_map(|(i, x)| versions[..i].iter().map(move |y| (x, y, Ordering::Greater)))
1182 .collect::<Vec<_>>(),
1183 ]
1184 .into_iter()
1185 .flatten();
1186
1187 for (a, b, ordering) in operations {
1188 assert_eq!(a.cmp(b), ordering, "{a} {ordering:?} {b}");
1189 }
1190 }
1191
1192 const VERSIONS_0: &[&str] = &[
1193 "1.0.dev456",
1194 "1.0a1",
1195 "1.0a2.dev456",
1196 "1.0a12.dev456",
1197 "1.0a12",
1198 "1.0b1.dev456",
1199 "1.0b2",
1200 "1.0b2.post345.dev456",
1201 "1.0b2.post345",
1202 "1.0b2-346",
1203 "1.0c1.dev456",
1204 "1.0c1",
1205 "1.0rc2",
1206 "1.0c3",
1207 "1.0",
1208 "1.0.post456.dev34",
1209 "1.0.post456",
1210 "1.1.dev1",
1211 "1.2+123abc",
1212 "1.2+123abc456",
1213 "1.2+abc",
1214 "1.2+abc123",
1215 "1.2+abc123def",
1216 "1.2+1234.abc",
1217 "1.2+123456",
1218 "1.2.r32+123456",
1219 "1.2.rev33+123456",
1220 ];
1221
1222 const SPECIFIERS_OTHER: &[&str] = &[
1223 "== 1.*", "== 1.0.*", "== 1.1.*", "== 1.2.*", "== 2.*", "~= 1.0", "~= 1.0b1", "~= 1.1",
1224 "~= 1.2", "~= 2.0",
1225 ];
1226
1227 const EXPECTED_OTHER: &[[bool; 10]] = &[
1228 [
1229 true, true, false, false, false, false, false, false, false, false,
1230 ],
1231 [
1232 true, true, false, false, false, false, false, false, false, false,
1233 ],
1234 [
1235 true, true, false, false, false, false, false, false, false, false,
1236 ],
1237 [
1238 true, true, false, false, false, false, false, false, false, false,
1239 ],
1240 [
1241 true, true, false, false, false, false, false, false, false, false,
1242 ],
1243 [
1244 true, true, false, false, false, false, false, false, false, false,
1245 ],
1246 [
1247 true, true, false, false, false, false, true, false, false, false,
1248 ],
1249 [
1250 true, true, false, false, false, false, true, false, false, false,
1251 ],
1252 [
1253 true, true, false, false, false, false, true, false, false, false,
1254 ],
1255 [
1256 true, true, false, false, false, false, true, false, false, false,
1257 ],
1258 [
1259 true, true, false, false, false, false, true, false, false, false,
1260 ],
1261 [
1262 true, true, false, false, false, false, true, false, false, false,
1263 ],
1264 [
1265 true, true, false, false, false, false, true, false, false, false,
1266 ],
1267 [
1268 true, true, false, false, false, false, true, false, false, false,
1269 ],
1270 [
1271 true, true, false, false, false, true, true, false, false, false,
1272 ],
1273 [
1274 true, true, false, false, false, true, true, false, false, false,
1275 ],
1276 [
1277 true, true, false, false, false, true, true, false, false, false,
1278 ],
1279 [
1280 true, false, true, false, false, true, true, false, false, false,
1281 ],
1282 [
1283 true, false, false, true, false, true, true, true, true, false,
1284 ],
1285 [
1286 true, false, false, true, false, true, true, true, true, false,
1287 ],
1288 [
1289 true, false, false, true, false, true, true, true, true, false,
1290 ],
1291 [
1292 true, false, false, true, false, true, true, true, true, false,
1293 ],
1294 [
1295 true, false, false, true, false, true, true, true, true, false,
1296 ],
1297 [
1298 true, false, false, true, false, true, true, true, true, false,
1299 ],
1300 [
1301 true, false, false, true, false, true, true, true, true, false,
1302 ],
1303 [
1304 true, false, false, true, false, true, true, true, true, false,
1305 ],
1306 [
1307 true, false, false, true, false, true, true, true, true, false,
1308 ],
1309 ];
1310
1311 #[test]
1315 fn test_operators_other() {
1316 let versions = VERSIONS_0
1317 .iter()
1318 .map(|version| Version::from_str(version).unwrap());
1319 let specifiers: Vec<_> = SPECIFIERS_OTHER
1320 .iter()
1321 .map(|specifier| VersionSpecifier::from_str(specifier).unwrap())
1322 .collect();
1323
1324 for (version, expected) in versions.zip(EXPECTED_OTHER) {
1325 let actual = specifiers
1326 .iter()
1327 .map(|specifier| specifier.contains(&version));
1328 for ((actual, expected), _specifier) in actual.zip(expected).zip(SPECIFIERS_OTHER) {
1329 assert_eq!(actual, *expected);
1330 }
1331 }
1332 }
1333
1334 #[test]
1335 fn test_arbitrary_equality() {
1336 assert!(
1337 VersionSpecifier::from_str("=== 1.2a1")
1338 .unwrap()
1339 .contains(&Version::from_str("1.2a1").unwrap())
1340 );
1341 assert!(
1342 !VersionSpecifier::from_str("=== 1.2a1")
1343 .unwrap()
1344 .contains(&Version::from_str("1.2a1+local").unwrap())
1345 );
1346 }
1347
1348 #[test]
1349 fn test_equal_star_short_version_bug() {
1350 let specifier = VersionSpecifier::from_str("==2.1.*").unwrap();
1352 let version = Version::from_str("2").unwrap();
1353 assert!(
1354 !specifier.contains(&version),
1355 "Bug: version '2' incorrectly matches '==2.1.*'"
1356 );
1357
1358 let specifier = VersionSpecifier::from_str("!=2.1.*").unwrap();
1360 let version = Version::from_str("2").unwrap();
1361 assert!(
1362 specifier.contains(&version),
1363 "Bug: version '2' should match '!=2.1.*' (2.0 is not in 2.1 family)"
1364 );
1365
1366 let specifier = VersionSpecifier::from_str("==2.0.*").unwrap();
1368 let version = Version::from_str("2").unwrap();
1369 assert!(
1370 specifier.contains(&version),
1371 "version '2' should match '==2.0.*'"
1372 );
1373
1374 let specifier = VersionSpecifier::from_str("!=2.0.*").unwrap();
1376 let version = Version::from_str("2").unwrap();
1377 assert!(
1378 !specifier.contains(&version),
1379 "version '2' should not match '!=2.0.*'"
1380 );
1381
1382 let specifier = VersionSpecifier::from_str("==2.1.*").unwrap();
1385 let version = Version::from_str("2+local").unwrap();
1386 assert!(
1387 !specifier.contains(&version),
1388 "version '2+local' should not match '==2.1.*'"
1389 );
1390
1391 let specifier = VersionSpecifier::from_str("!=2.1.*").unwrap();
1393 let version = Version::from_str("2+local").unwrap();
1394 assert!(
1395 specifier.contains(&version),
1396 "version '2+local' should match '!=2.1.*'"
1397 );
1398 }
1399
1400 #[test]
1401 fn test_specifiers_true() {
1402 let pairs = [
1403 ("2.0", "==2"),
1405 ("2.0", "==2.0"),
1406 ("2.0", "==2.0.0"),
1407 ("2.0+deadbeef", "==2"),
1408 ("2.0+deadbeef", "==2.0"),
1409 ("2.0+deadbeef", "==2.0.0"),
1410 ("2.0+deadbeef", "==2+deadbeef"),
1411 ("2.0+deadbeef", "==2.0+deadbeef"),
1412 ("2.0+deadbeef", "==2.0.0+deadbeef"),
1413 ("2.0+deadbeef.0", "==2.0.0+deadbeef.00"),
1414 ("2.dev1", "==2.*"),
1416 ("2a1", "==2.*"),
1417 ("2a1.post1", "==2.*"),
1418 ("2b1", "==2.*"),
1419 ("2b1.dev1", "==2.*"),
1420 ("2c1", "==2.*"),
1421 ("2c1.post1.dev1", "==2.*"),
1422 ("2c1.post1.dev1", "==2.0.*"),
1423 ("2rc1", "==2.*"),
1424 ("2rc1", "==2.0.*"),
1425 ("2", "==2.*"),
1426 ("2", "==2.0.*"),
1427 ("2", "==0!2.*"),
1428 ("0!2", "==2.*"),
1429 ("2.0", "==2.*"),
1430 ("2.0.0", "==2.*"),
1431 ("2.1+local.version", "==2.1.*"),
1432 ("2.1", "!=2"),
1434 ("2.1", "!=2.0"),
1435 ("2.0.1", "!=2"),
1436 ("2.0.1", "!=2.0"),
1437 ("2.0.1", "!=2.0.0"),
1438 ("2.0", "!=2.0+deadbeef"),
1439 ("2.0", "!=3.*"),
1441 ("2.1", "!=2.0.*"),
1442 ("2.0", ">=2"),
1444 ("2.0", ">=2.0"),
1445 ("2.0", ">=2.0.0"),
1446 ("2.0.post1", ">=2"),
1447 ("2.0.post1.dev1", ">=2"),
1448 ("3", ">=2"),
1449 ("2.0", "<=2"),
1451 ("2.0", "<=2.0"),
1452 ("2.0", "<=2.0.0"),
1453 ("2.0.dev1", "<=2"),
1454 ("2.0a1", "<=2"),
1455 ("2.0a1.dev1", "<=2"),
1456 ("2.0b1", "<=2"),
1457 ("2.0b1.post1", "<=2"),
1458 ("2.0c1", "<=2"),
1459 ("2.0c1.post1.dev1", "<=2"),
1460 ("2.0rc1", "<=2"),
1461 ("1", "<=2"),
1462 ("3", ">2"),
1464 ("2.1", ">2.0"),
1465 ("2.0.1", ">2"),
1466 ("2.1.post1", ">2"),
1467 ("2.1+local.version", ">2"),
1468 ("2.post2", ">2.post1"),
1469 ("1", "<2"),
1471 ("2.0", "<2.1"),
1472 ("2.0.dev0", "<2.1"),
1473 ("0.1a1", "<0.1a2"),
1475 ("0.1dev1", "<0.1dev2"),
1476 ("0.1dev1", "<0.1a1"),
1477 ("1", "~=1.0"),
1479 ("1.0.1", "~=1.0"),
1480 ("1.1", "~=1.0"),
1481 ("1.9999999", "~=1.0"),
1482 ("1.1", "~=1.0a1"),
1483 ("2022.01.01", "~=2022.01.01"),
1484 ("2!1.0", "~=2!1.0"),
1486 ("2!1.0", "==2!1.*"),
1487 ("2!1.0", "==2!1.0"),
1488 ("2!1.0", "!=1.0"),
1489 ("1.0", "!=2!1.0"),
1490 ("1.0", "<=2!0.1"),
1491 ("2!1.0", ">=2.0"),
1492 ("1.0", "<2!0.1"),
1493 ("2!1.0", ">2.0"),
1494 ("2.0.5", ">2.0dev"),
1496 ];
1497
1498 for (s_version, s_spec) in pairs {
1499 let version = s_version.parse::<Version>().unwrap();
1500 let spec = s_spec.parse::<VersionSpecifier>().unwrap();
1501 assert!(
1502 spec.contains(&version),
1503 "{s_version} {s_spec}\nversion repr: {:?}\nspec version repr: {:?}",
1504 version.as_bloated_debug(),
1505 spec.version.as_bloated_debug(),
1506 );
1507 }
1508 }
1509
1510 #[test]
1511 fn test_specifier_false() {
1512 let pairs = [
1513 ("2.1", "==2"),
1515 ("2.1", "==2.0"),
1516 ("2.1", "==2.0.0"),
1517 ("2.0", "==2.0+deadbeef"),
1518 ("2.0", "==3.*"),
1520 ("2.1", "==2.0.*"),
1521 ("2.0", "!=2"),
1523 ("2.0", "!=2.0"),
1524 ("2.0", "!=2.0.0"),
1525 ("2.0+deadbeef", "!=2"),
1526 ("2.0+deadbeef", "!=2.0"),
1527 ("2.0+deadbeef", "!=2.0.0"),
1528 ("2.0+deadbeef", "!=2+deadbeef"),
1529 ("2.0+deadbeef", "!=2.0+deadbeef"),
1530 ("2.0+deadbeef", "!=2.0.0+deadbeef"),
1531 ("2.0+deadbeef.0", "!=2.0.0+deadbeef.00"),
1532 ("2.dev1", "!=2.*"),
1534 ("2a1", "!=2.*"),
1535 ("2a1.post1", "!=2.*"),
1536 ("2b1", "!=2.*"),
1537 ("2b1.dev1", "!=2.*"),
1538 ("2c1", "!=2.*"),
1539 ("2c1.post1.dev1", "!=2.*"),
1540 ("2c1.post1.dev1", "!=2.0.*"),
1541 ("2rc1", "!=2.*"),
1542 ("2rc1", "!=2.0.*"),
1543 ("2", "!=2.*"),
1544 ("2", "!=2.0.*"),
1545 ("2.0", "!=2.*"),
1546 ("2.0.0", "!=2.*"),
1547 ("2.0.dev1", ">=2"),
1549 ("2.0a1", ">=2"),
1550 ("2.0a1.dev1", ">=2"),
1551 ("2.0b1", ">=2"),
1552 ("2.0b1.post1", ">=2"),
1553 ("2.0c1", ">=2"),
1554 ("2.0c1.post1.dev1", ">=2"),
1555 ("2.0rc1", ">=2"),
1556 ("1", ">=2"),
1557 ("2.0.post1", "<=2"),
1559 ("2.0.post1.dev1", "<=2"),
1560 ("3", "<=2"),
1561 ("1", ">2"),
1563 ("2.0.dev1", ">2"),
1564 ("2.0a1", ">2"),
1565 ("2.0a1.post1", ">2"),
1566 ("2.0b1", ">2"),
1567 ("2.0b1.dev1", ">2"),
1568 ("2.0c1", ">2"),
1569 ("2.0c1.post1.dev1", ">2"),
1570 ("2.0rc1", ">2"),
1571 ("2.0", ">2"),
1572 ("2.post2", ">2"),
1573 ("2.0.post1", ">2"),
1574 ("2.0.post1.dev1", ">2"),
1575 ("2.0+local.version", ">2"),
1576 ("2.0.dev1", "<2"),
1578 ("2.0a1", "<2"),
1579 ("2.0a1.post1", "<2"),
1580 ("2.0b1", "<2"),
1581 ("2.0b2.dev1", "<2"),
1582 ("2.0c1", "<2"),
1583 ("2.0c1.post1.dev1", "<2"),
1584 ("2.0rc1", "<2"),
1585 ("2.0", "<2"),
1586 ("2.post1", "<2"),
1587 ("2.post1.dev1", "<2"),
1588 ("3", "<2"),
1589 ("2.0", "~=1.0"),
1591 ("1.1.0", "~=1.0.0"),
1592 ("1.1.post1", "~=1.0.0"),
1593 ("1.0", "~=2!1.0"),
1595 ("2!1.0", "~=1.0"),
1596 ("2!1.0", "==1.0"),
1597 ("1.0", "==2!1.0"),
1598 ("2!1.0", "==1.*"),
1599 ("1.0", "==2!1.*"),
1600 ("2!1.0", "!=2!1.0"),
1601 ];
1602 for (version, specifier) in pairs {
1603 assert!(
1604 !VersionSpecifier::from_str(specifier)
1605 .unwrap()
1606 .contains(&Version::from_str(version).unwrap()),
1607 "{version} {specifier}"
1608 );
1609 }
1610 }
1611
1612 #[test]
1613 fn test_parse_version_specifiers() {
1614 let result = VersionSpecifiers::from_str("~= 0.9, >= 1.0, != 1.3.4.*, < 2.0").unwrap();
1615 assert_eq!(
1616 result.0.as_ref(),
1617 [
1618 VersionSpecifier {
1619 operator: Operator::TildeEqual,
1620 version: Version::new([0, 9]),
1621 },
1622 VersionSpecifier {
1623 operator: Operator::GreaterThanEqual,
1624 version: Version::new([1, 0]),
1625 },
1626 VersionSpecifier {
1627 operator: Operator::NotEqualStar,
1628 version: Version::new([1, 3, 4]),
1629 },
1630 VersionSpecifier {
1631 operator: Operator::LessThan,
1632 version: Version::new([2, 0]),
1633 }
1634 ]
1635 );
1636 }
1637
1638 #[test]
1639 fn test_parse_error() {
1640 let result = VersionSpecifiers::from_str("~= 0.9, %= 1.0, != 1.3.4.*");
1641 assert_eq!(
1642 result.unwrap_err().to_string(),
1643 indoc! {r"
1644 Failed to parse version: Unexpected end of version specifier, expected operator:
1645 ~= 0.9, %= 1.0, != 1.3.4.*
1646 ^^^^^^^
1647 "}
1648 );
1649 }
1650
1651 #[test]
1652 fn test_parse_specifier_missing_operator_error() {
1653 let result = VersionSpecifiers::from_str("3.12");
1654 assert_eq!(
1655 result.unwrap_err().to_string(),
1656 indoc! {"
1657 Failed to parse version: Unexpected end of version specifier, expected operator. Did you mean `==3.12`?:
1658 3.12
1659 ^^^^
1660 "}
1661 );
1662 }
1663
1664 #[test]
1665 fn test_parse_specifier_missing_operator_invalid_version_error() {
1666 let result = VersionSpecifiers::from_str("blergh");
1667 assert_eq!(
1668 result.unwrap_err().to_string(),
1669 indoc! {r"
1670 Failed to parse version: Unexpected end of version specifier, expected operator:
1671 blergh
1672 ^^^^^^
1673 "}
1674 );
1675 }
1676
1677 #[test]
1678 fn test_non_star_after_star() {
1679 let result = VersionSpecifiers::from_str("== 0.9.*.1");
1680 assert_eq!(
1681 result.unwrap_err().inner.err,
1682 ParseErrorKind::InvalidVersion(version::PatternErrorKind::WildcardNotTrailing.into())
1683 .into(),
1684 );
1685 }
1686
1687 #[test]
1688 fn test_star_wrong_operator() {
1689 let result = VersionSpecifiers::from_str(">= 0.9.1.*");
1690 assert_eq!(
1691 result.unwrap_err().inner.err,
1692 ParseErrorKind::InvalidSpecifier(
1693 BuildErrorKind::OperatorWithStar {
1694 operator: Operator::GreaterThanEqual,
1695 }
1696 .into()
1697 )
1698 .into(),
1699 );
1700 }
1701
1702 #[test]
1703 fn test_invalid_word() {
1704 let result = VersionSpecifiers::from_str("blergh");
1705 assert_eq!(
1706 result.unwrap_err().inner.err,
1707 ParseErrorKind::MissingOperator(VersionOperatorBuildError {
1708 version_pattern: None
1709 })
1710 .into(),
1711 );
1712 }
1713
1714 #[test]
1716 fn test_invalid_specifier() {
1717 let specifiers = [
1718 (
1720 "2.0",
1721 ParseErrorKind::MissingOperator(VersionOperatorBuildError {
1722 version_pattern: VersionPattern::from_str("2.0").ok(),
1723 })
1724 .into(),
1725 ),
1726 (
1728 "=>2.0",
1729 ParseErrorKind::InvalidOperator(OperatorParseError {
1730 got: "=>".to_string(),
1731 })
1732 .into(),
1733 ),
1734 ("==", ParseErrorKind::MissingVersion.into()),
1736 (
1738 "~=1.0+5",
1739 ParseErrorKind::InvalidSpecifier(
1740 BuildErrorKind::OperatorLocalCombo {
1741 operator: Operator::TildeEqual,
1742 version: Version::new([1, 0])
1743 .with_local_segments(vec![LocalSegment::Number(5)]),
1744 }
1745 .into(),
1746 )
1747 .into(),
1748 ),
1749 (
1750 ">=1.0+deadbeef",
1751 ParseErrorKind::InvalidSpecifier(
1752 BuildErrorKind::OperatorLocalCombo {
1753 operator: Operator::GreaterThanEqual,
1754 version: Version::new([1, 0]).with_local_segments(vec![
1755 LocalSegment::String("deadbeef".to_string()),
1756 ]),
1757 }
1758 .into(),
1759 )
1760 .into(),
1761 ),
1762 (
1763 "<=1.0+abc123",
1764 ParseErrorKind::InvalidSpecifier(
1765 BuildErrorKind::OperatorLocalCombo {
1766 operator: Operator::LessThanEqual,
1767 version: Version::new([1, 0])
1768 .with_local_segments(vec![LocalSegment::String("abc123".to_string())]),
1769 }
1770 .into(),
1771 )
1772 .into(),
1773 ),
1774 (
1775 ">1.0+watwat",
1776 ParseErrorKind::InvalidSpecifier(
1777 BuildErrorKind::OperatorLocalCombo {
1778 operator: Operator::GreaterThan,
1779 version: Version::new([1, 0])
1780 .with_local_segments(vec![LocalSegment::String("watwat".to_string())]),
1781 }
1782 .into(),
1783 )
1784 .into(),
1785 ),
1786 (
1787 "<1.0+1.0",
1788 ParseErrorKind::InvalidSpecifier(
1789 BuildErrorKind::OperatorLocalCombo {
1790 operator: Operator::LessThan,
1791 version: Version::new([1, 0]).with_local_segments(vec![
1792 LocalSegment::Number(1),
1793 LocalSegment::Number(0),
1794 ]),
1795 }
1796 .into(),
1797 )
1798 .into(),
1799 ),
1800 (
1802 "~=1.0.*",
1803 ParseErrorKind::InvalidSpecifier(
1804 BuildErrorKind::OperatorWithStar {
1805 operator: Operator::TildeEqual,
1806 }
1807 .into(),
1808 )
1809 .into(),
1810 ),
1811 (
1812 ">=1.0.*",
1813 ParseErrorKind::InvalidSpecifier(
1814 BuildErrorKind::OperatorWithStar {
1815 operator: Operator::GreaterThanEqual,
1816 }
1817 .into(),
1818 )
1819 .into(),
1820 ),
1821 (
1822 "<=1.0.*",
1823 ParseErrorKind::InvalidSpecifier(
1824 BuildErrorKind::OperatorWithStar {
1825 operator: Operator::LessThanEqual,
1826 }
1827 .into(),
1828 )
1829 .into(),
1830 ),
1831 (
1832 ">1.0.*",
1833 ParseErrorKind::InvalidSpecifier(
1834 BuildErrorKind::OperatorWithStar {
1835 operator: Operator::GreaterThan,
1836 }
1837 .into(),
1838 )
1839 .into(),
1840 ),
1841 (
1842 "<1.0.*",
1843 ParseErrorKind::InvalidSpecifier(
1844 BuildErrorKind::OperatorWithStar {
1845 operator: Operator::LessThan,
1846 }
1847 .into(),
1848 )
1849 .into(),
1850 ),
1851 (
1854 "==1.0.*+5",
1855 ParseErrorKind::InvalidVersion(
1856 version::PatternErrorKind::WildcardNotTrailing.into(),
1857 )
1858 .into(),
1859 ),
1860 (
1861 "!=1.0.*+deadbeef",
1862 ParseErrorKind::InvalidVersion(
1863 version::PatternErrorKind::WildcardNotTrailing.into(),
1864 )
1865 .into(),
1866 ),
1867 (
1870 "==2.0a1.*",
1871 ParseErrorKind::InvalidVersion(
1872 version::ErrorKind::UnexpectedEnd {
1873 version: "2.0a1".to_string(),
1874 remaining: ".*".to_string(),
1875 }
1876 .into(),
1877 )
1878 .into(),
1879 ),
1880 (
1881 "!=2.0a1.*",
1882 ParseErrorKind::InvalidVersion(
1883 version::ErrorKind::UnexpectedEnd {
1884 version: "2.0a1".to_string(),
1885 remaining: ".*".to_string(),
1886 }
1887 .into(),
1888 )
1889 .into(),
1890 ),
1891 (
1892 "==2.0.post1.*",
1893 ParseErrorKind::InvalidVersion(
1894 version::ErrorKind::UnexpectedEnd {
1895 version: "2.0.post1".to_string(),
1896 remaining: ".*".to_string(),
1897 }
1898 .into(),
1899 )
1900 .into(),
1901 ),
1902 (
1903 "!=2.0.post1.*",
1904 ParseErrorKind::InvalidVersion(
1905 version::ErrorKind::UnexpectedEnd {
1906 version: "2.0.post1".to_string(),
1907 remaining: ".*".to_string(),
1908 }
1909 .into(),
1910 )
1911 .into(),
1912 ),
1913 (
1914 "==2.0.dev1.*",
1915 ParseErrorKind::InvalidVersion(
1916 version::ErrorKind::UnexpectedEnd {
1917 version: "2.0.dev1".to_string(),
1918 remaining: ".*".to_string(),
1919 }
1920 .into(),
1921 )
1922 .into(),
1923 ),
1924 (
1925 "!=2.0.dev1.*",
1926 ParseErrorKind::InvalidVersion(
1927 version::ErrorKind::UnexpectedEnd {
1928 version: "2.0.dev1".to_string(),
1929 remaining: ".*".to_string(),
1930 }
1931 .into(),
1932 )
1933 .into(),
1934 ),
1935 (
1936 "==1.0+5.*",
1937 ParseErrorKind::InvalidVersion(
1938 version::ErrorKind::LocalEmpty { precursor: '.' }.into(),
1939 )
1940 .into(),
1941 ),
1942 (
1943 "!=1.0+deadbeef.*",
1944 ParseErrorKind::InvalidVersion(
1945 version::ErrorKind::LocalEmpty { precursor: '.' }.into(),
1946 )
1947 .into(),
1948 ),
1949 (
1951 "==1.0.*.5",
1952 ParseErrorKind::InvalidVersion(
1953 version::PatternErrorKind::WildcardNotTrailing.into(),
1954 )
1955 .into(),
1956 ),
1957 (
1959 "~=1",
1960 ParseErrorKind::InvalidSpecifier(BuildErrorKind::CompatibleRelease.into()).into(),
1961 ),
1962 (
1964 "==1.0.dev1.*",
1965 ParseErrorKind::InvalidVersion(
1966 version::ErrorKind::UnexpectedEnd {
1967 version: "1.0.dev1".to_string(),
1968 remaining: ".*".to_string(),
1969 }
1970 .into(),
1971 )
1972 .into(),
1973 ),
1974 (
1975 "!=1.0.dev1.*",
1976 ParseErrorKind::InvalidVersion(
1977 version::ErrorKind::UnexpectedEnd {
1978 version: "1.0.dev1".to_string(),
1979 remaining: ".*".to_string(),
1980 }
1981 .into(),
1982 )
1983 .into(),
1984 ),
1985 ];
1986 for (specifier, error) in specifiers {
1987 assert_eq!(VersionSpecifier::from_str(specifier).unwrap_err(), error);
1988 }
1989 }
1990
1991 #[test]
1992 fn test_display_start() {
1993 assert_eq!(
1994 VersionSpecifier::from_str("== 1.1.*")
1995 .unwrap()
1996 .to_string(),
1997 "==1.1.*"
1998 );
1999 assert_eq!(
2000 VersionSpecifier::from_str("!= 1.1.*")
2001 .unwrap()
2002 .to_string(),
2003 "!=1.1.*"
2004 );
2005 }
2006
2007 #[test]
2008 fn test_version_specifiers_str() {
2009 assert_eq!(
2010 VersionSpecifiers::from_str(">= 3.7").unwrap().to_string(),
2011 ">=3.7"
2012 );
2013 assert_eq!(
2014 VersionSpecifiers::from_str(">=3.7, < 4.0, != 3.9.0")
2015 .unwrap()
2016 .to_string(),
2017 ">=3.7, !=3.9.0, <4.0"
2018 );
2019 }
2020
2021 #[test]
2022 fn test_version_specifiers_singular_interval() {
2023 let lower_then_upper = VersionSpecifiers::from_str(">=1.4.4, <=1.4.4").unwrap();
2024 let upper_then_lower = VersionSpecifiers::from_str("<=1.4.4, >=1.4.4").unwrap();
2025
2026 assert_eq!(lower_then_upper, upper_then_lower);
2027 assert_eq!(lower_then_upper.to_string(), "<=1.4.4, >=1.4.4");
2028 }
2029
2030 #[test]
2033 fn test_version_specifiers_empty() {
2034 assert_eq!(VersionSpecifiers::from_str("").unwrap().to_string(), "");
2035 }
2036
2037 #[test]
2041 fn non_ascii_version_specifier() {
2042 let s = "💩";
2043 let err = s.parse::<VersionSpecifiers>().unwrap_err();
2044 assert_eq!(err.inner.start, 0);
2045 assert_eq!(err.inner.end, 4);
2046
2047 let s = ">=3.7, <4.0,>5.%";
2051 let err = s.parse::<VersionSpecifiers>().unwrap_err();
2052 assert_eq!(err.inner.start, 12);
2053 assert_eq!(err.inner.end, 16);
2054 let s = ">=3.7,\u{3000}<4.0,>5.%";
2063 let err = s.parse::<VersionSpecifiers>().unwrap_err();
2064 assert_eq!(err.inner.start, 14);
2065 assert_eq!(err.inner.end, 18);
2066 }
2067
2068 #[test]
2071 fn error_message_version_specifiers_parse_error() {
2072 let specs = ">=1.2.3, 5.4.3, >=3.4.5";
2073 let err = VersionSpecifierParseError {
2074 kind: Box::new(ParseErrorKind::MissingOperator(VersionOperatorBuildError {
2075 version_pattern: VersionPattern::from_str("5.4.3").ok(),
2076 })),
2077 };
2078 let inner = Box::new(VersionSpecifiersParseErrorInner {
2079 err,
2080 line: specs.to_string(),
2081 start: 8,
2082 end: 14,
2083 });
2084 let err = VersionSpecifiersParseError { inner };
2085 assert_eq!(err, VersionSpecifiers::from_str(specs).unwrap_err());
2086 assert_eq!(
2087 err.to_string(),
2088 "\
2089Failed to parse version: Unexpected end of version specifier, expected operator. Did you mean `==5.4.3`?:
2090>=1.2.3, 5.4.3, >=3.4.5
2091 ^^^^^^
2092"
2093 );
2094 }
2095
2096 #[test]
2099 fn error_message_version_specifier_build_error() {
2100 let err = VersionSpecifierBuildError {
2101 kind: Box::new(BuildErrorKind::CompatibleRelease),
2102 };
2103 let op = Operator::TildeEqual;
2104 let v = Version::new([5]);
2105 let vpat = VersionPattern::verbatim(v);
2106 assert_eq!(err, VersionSpecifier::from_pattern(op, vpat).unwrap_err());
2107 assert_eq!(
2108 err.to_string(),
2109 "The ~= operator requires at least two segments in the release version"
2110 );
2111 }
2112
2113 #[test]
2116 fn error_message_version_specifier_parse_error() {
2117 let err = VersionSpecifierParseError {
2118 kind: Box::new(ParseErrorKind::InvalidSpecifier(
2119 VersionSpecifierBuildError {
2120 kind: Box::new(BuildErrorKind::CompatibleRelease),
2121 },
2122 )),
2123 };
2124 assert_eq!(err, VersionSpecifier::from_str("~=5").unwrap_err());
2125 assert_eq!(
2126 err.to_string(),
2127 "The ~= operator requires at least two segments in the release version"
2128 );
2129 }
2130
2131 #[test]
2134 fn trailing_zero_equality() {
2135 let equal = [
2136 (">=3.3", ">=3.3.0"),
2138 ("<2", "<2.0.0"),
2139 ("==1.2", "==1.2.0"),
2140 ("~=2.2.0", "~=2.2.0"),
2142 ];
2143 for (a, b) in equal {
2144 let a = VersionSpecifier::from_str(a).unwrap();
2145 let b = VersionSpecifier::from_str(b).unwrap();
2146 assert_eq!(a, b);
2147 }
2148
2149 let not_equal = [
2150 ("~=2.2", "~=2.2.0"),
2152 ("~=1.4.5", "~=1.4.5.0"),
2153 ("~=2.2.post3", "~=2.2.post5"),
2155 ("~=2.2.post3", "~=2.2.0.post3"),
2157 ];
2158 for (a, b) in not_equal {
2159 let a = VersionSpecifier::from_str(a).unwrap();
2160 let b = VersionSpecifier::from_str(b).unwrap();
2161 assert_ne!(a, b);
2162 }
2163 }
2164
2165 #[test]
2167 fn bounding_specifiers_u64_max_rejected_at_parse_time() {
2168 assert!(VersionSpecifier::from_str("~=3.18446744073709551615.0").is_err());
2169 assert!(VersionSpecifier::from_str("~=18446744073709551615.0").is_err());
2170
2171 let specifier = VersionSpecifier::from_str("~=3.18446744073709551614.0").unwrap();
2173 let tilde = TildeVersionSpecifier::from_specifier(specifier).unwrap();
2174 let (_lower, _upper) = tilde.bounding_specifiers();
2175 }
2176}