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 = (Bound<&'a Version>, Bound<&'a 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 any_prerelease(&self) -> bool {
548 self.version.any_prerelease()
549 }
550
551 pub fn from_release_only_bounds(
555 bounds: (Bound<&Version>, Bound<&Version>),
556 ) -> impl Iterator<Item = Self> {
557 let (b1, b2) = match bounds {
558 (Bound::Included(v1), Bound::Included(v2)) if v1 == v2 => {
559 (Some(Self::equals_version(v1.clone())), None)
560 }
561 (Bound::Included(v1), Bound::Excluded(v2)) => {
563 match *v1.only_release_trimmed().release() {
564 [major] if *v2.only_release_trimmed().release() == [major, 1] => {
565 let version = Version::new([major, 0]);
566 (Some(Self::equals_star_version(version)), None)
567 }
568 [major, minor]
569 if *v2.only_release_trimmed().release() == [major, minor + 1] =>
570 {
571 let version = Version::new([major, minor]);
572 (Some(Self::equals_star_version(version)), None)
573 }
574 _ => (
575 Self::from_lower_bound(Bound::Included(v1)),
576 Self::from_upper_bound(Bound::Excluded(v2)),
577 ),
578 }
579 }
580 (lower, upper) => (Self::from_lower_bound(lower), Self::from_upper_bound(upper)),
581 };
582
583 b1.into_iter().chain(b2)
584 }
585
586 fn from_lower_bound(bound: Bound<&Version>) -> Option<Self> {
588 match bound {
589 Bound::Included(version) => {
590 Some(Self::from_version(Operator::GreaterThanEqual, version.clone()).unwrap())
591 }
592 Bound::Excluded(version) => {
593 Some(Self::from_version(Operator::GreaterThan, version.clone()).unwrap())
594 }
595 Bound::Unbounded => None,
596 }
597 }
598
599 fn from_upper_bound(bound: Bound<&Version>) -> Option<Self> {
601 match bound {
602 Bound::Included(version) => {
603 Some(Self::from_version(Operator::LessThanEqual, version.clone()).unwrap())
604 }
605 Bound::Excluded(version) => {
606 Some(Self::from_version(Operator::LessThan, version.clone()).unwrap())
607 }
608 Bound::Unbounded => None,
609 }
610 }
611
612 pub fn contains(&self, version: &Version) -> bool {
620 let this = self.version();
624 let other = if this.local().is_empty() && !version.local().is_empty() {
625 Cow::Owned(version.clone().without_local())
626 } else {
627 Cow::Borrowed(version)
628 };
629
630 match self.operator {
631 Operator::Equal => other.as_ref() == this,
632 Operator::EqualStar => {
633 this.epoch() == other.epoch()
634 && self
635 .version
636 .release()
637 .iter()
638 .zip(other.release().iter().chain(std::iter::repeat(&0)))
642 .all(|(this, other)| this == other)
643 }
644 #[allow(deprecated)]
645 Operator::ExactEqual => {
646 #[cfg(feature = "tracing")]
647 {
648 warn!("Using arbitrary equality (`===`) is discouraged");
649 }
650 self.version.to_string() == version.to_string()
651 }
652 Operator::NotEqual => this != other.as_ref(),
653 Operator::NotEqualStar => {
654 this.epoch() != other.epoch()
655 || !this
656 .release()
657 .iter()
658 .zip(other.release().iter().chain(std::iter::repeat(&0)))
662 .all(|(this, other)| this == other)
663 }
664 Operator::TildeEqual => {
665 assert!(this.release().len() > 1);
670 if this.epoch() != other.epoch() {
671 return false;
672 }
673
674 if !this.release()[..this.release().len() - 1]
675 .iter()
676 .zip(&*other.release())
677 .all(|(this, other)| this == other)
678 {
679 return false;
680 }
681
682 other.as_ref() >= this
685 }
686 Operator::GreaterThan => {
687 if other.epoch() > this.epoch() {
688 return true;
689 }
690
691 if version::compare_release(&this.release(), &other.release()) == Ordering::Equal {
692 if !this.is_post() && other.is_post() {
697 return false;
698 }
699
700 if other.is_local() {
702 return false;
703 }
704 }
705
706 other.as_ref() > this
707 }
708 Operator::GreaterThanEqual => other.as_ref() >= this,
709 Operator::LessThan => {
710 if other.epoch() < this.epoch() {
711 return true;
712 }
713
714 if version::compare_release(&this.release(), &other.release()) == Ordering::Equal
719 && !this.any_prerelease()
720 && other.any_prerelease()
721 {
722 return false;
723 }
724
725 other.as_ref() < this
726 }
727 Operator::LessThanEqual => other.as_ref() <= this,
728 }
729 }
730
731 pub fn has_lower_bound(&self) -> bool {
733 match self.operator() {
734 Operator::Equal
735 | Operator::EqualStar
736 | Operator::ExactEqual
737 | Operator::TildeEqual
738 | Operator::GreaterThan
739 | Operator::GreaterThanEqual => true,
740 Operator::LessThanEqual
741 | Operator::LessThan
742 | Operator::NotEqualStar
743 | Operator::NotEqual => false,
744 }
745 }
746}
747
748impl FromStr for VersionSpecifier {
749 type Err = VersionSpecifierParseError;
750
751 fn from_str(spec: &str) -> Result<Self, Self::Err> {
753 let mut s = unscanny::Scanner::new(spec);
754 s.eat_while(|c: char| c.is_whitespace());
755 let operator = s.eat_while(['=', '!', '~', '<', '>']);
757 if operator.is_empty() {
758 s.eat_while(|c: char| c.is_whitespace());
761 let version = s.eat_while(|c: char| !c.is_whitespace());
762 s.eat_while(|c: char| c.is_whitespace());
763 return Err(ParseErrorKind::MissingOperator(VersionOperatorBuildError {
764 version_pattern: VersionPattern::from_str(version).ok(),
765 })
766 .into());
767 }
768 let operator = Operator::from_str(operator).map_err(ParseErrorKind::InvalidOperator)?;
769 s.eat_while(|c: char| c.is_whitespace());
770 let version = s.eat_while(|c: char| !c.is_whitespace());
771 if version.is_empty() {
772 return Err(ParseErrorKind::MissingVersion.into());
773 }
774 let vpat = version.parse().map_err(ParseErrorKind::InvalidVersion)?;
775 let version_specifier =
776 Self::from_pattern(operator, vpat).map_err(ParseErrorKind::InvalidSpecifier)?;
777 s.eat_while(|c: char| c.is_whitespace());
778 if !s.done() {
779 return Err(ParseErrorKind::InvalidTrailing(s.after().to_string()).into());
780 }
781 Ok(version_specifier)
782 }
783}
784
785impl std::fmt::Display for VersionSpecifier {
786 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
787 if self.operator == Operator::EqualStar || self.operator == Operator::NotEqualStar {
788 return write!(f, "{}{}.*", self.operator, self.version);
789 }
790 write!(f, "{}{}", self.operator, self.version)
791 }
792}
793
794#[derive(Clone, Debug, Eq, PartialEq)]
796pub struct VersionSpecifierBuildError {
797 kind: Box<BuildErrorKind>,
800}
801
802impl std::error::Error for VersionSpecifierBuildError {}
803
804impl std::fmt::Display for VersionSpecifierBuildError {
805 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
806 match *self.kind {
807 BuildErrorKind::OperatorLocalCombo {
808 operator: ref op,
809 ref version,
810 } => {
811 let local = version.local();
812 write!(
813 f,
814 "Operator {op} is incompatible with versions \
815 containing non-empty local segments (`+{local}`)",
816 )
817 }
818 BuildErrorKind::OperatorWithStar { operator: ref op } => {
819 write!(
820 f,
821 "Operator {op} cannot be used with a wildcard version specifier",
822 )
823 }
824 BuildErrorKind::CompatibleRelease => {
825 write!(
826 f,
827 "The ~= operator requires at least two segments in the release version"
828 )
829 }
830 }
831 }
832}
833
834#[derive(Clone, Debug, Eq, PartialEq)]
835struct VersionOperatorBuildError {
836 version_pattern: Option<VersionPattern>,
837}
838
839impl std::error::Error for VersionOperatorBuildError {}
840
841impl std::fmt::Display for VersionOperatorBuildError {
842 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
843 write!(f, "Unexpected end of version specifier, expected operator")?;
844 if let Some(version_pattern) = &self.version_pattern {
845 let version_specifier =
846 VersionSpecifier::from_pattern(Operator::Equal, version_pattern.clone()).unwrap();
847 write!(f, ". Did you mean `{version_specifier}`?")?;
848 }
849 Ok(())
850 }
851}
852
853#[derive(Clone, Debug, Eq, PartialEq)]
856enum BuildErrorKind {
857 OperatorLocalCombo {
861 operator: Operator,
863 version: Version,
865 },
866 OperatorWithStar {
869 operator: Operator,
871 },
872 CompatibleRelease,
875}
876
877impl From<BuildErrorKind> for VersionSpecifierBuildError {
878 fn from(kind: BuildErrorKind) -> Self {
879 Self {
880 kind: Box::new(kind),
881 }
882 }
883}
884
885#[derive(Clone, Debug, Eq, PartialEq)]
887pub struct VersionSpecifierParseError {
888 kind: Box<ParseErrorKind>,
891}
892
893impl std::error::Error for VersionSpecifierParseError {}
894
895impl std::fmt::Display for VersionSpecifierParseError {
896 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
897 match *self.kind {
903 ParseErrorKind::InvalidOperator(ref err) => err.fmt(f),
904 ParseErrorKind::InvalidVersion(ref err) => err.fmt(f),
905 ParseErrorKind::InvalidSpecifier(ref err) => err.fmt(f),
906 ParseErrorKind::MissingOperator(ref err) => err.fmt(f),
907 ParseErrorKind::MissingVersion => {
908 write!(f, "Unexpected end of version specifier, expected version")
909 }
910 ParseErrorKind::InvalidTrailing(ref trail) => {
911 write!(f, "Trailing `{trail}` is not allowed")
912 }
913 }
914 }
915}
916
917#[derive(Clone, Debug, Eq, PartialEq)]
920enum ParseErrorKind {
921 InvalidOperator(OperatorParseError),
922 InvalidVersion(VersionPatternParseError),
923 InvalidSpecifier(VersionSpecifierBuildError),
924 MissingOperator(VersionOperatorBuildError),
925 MissingVersion,
926 InvalidTrailing(String),
927}
928
929impl From<ParseErrorKind> for VersionSpecifierParseError {
930 fn from(kind: ParseErrorKind) -> Self {
931 Self {
932 kind: Box::new(kind),
933 }
934 }
935}
936
937fn parse_version_specifiers(
939 spec: &str,
940) -> Result<Vec<VersionSpecifier>, VersionSpecifiersParseError> {
941 let mut version_ranges = Vec::new();
942 if spec.is_empty() {
943 return Ok(version_ranges);
944 }
945 let mut start: usize = 0;
946 let separator = ",";
947 for version_range_spec in spec.split(separator) {
948 match VersionSpecifier::from_str(version_range_spec) {
949 Err(err) => {
950 return Err(VersionSpecifiersParseError {
951 inner: Box::new(VersionSpecifiersParseErrorInner {
952 err,
953 line: spec.to_string(),
954 start,
955 end: start + version_range_spec.len(),
956 }),
957 });
958 }
959 Ok(version_range) => {
960 version_ranges.push(version_range);
961 }
962 }
963 start += version_range_spec.len();
964 start += separator.len();
965 }
966 Ok(version_ranges)
967}
968
969#[derive(Clone, Debug)]
972pub struct TildeVersionSpecifier<'a> {
973 inner: Cow<'a, VersionSpecifier>,
974}
975
976impl<'a> TildeVersionSpecifier<'a> {
977 fn from_specifier(specifier: VersionSpecifier) -> Option<Self> {
982 TildeVersionSpecifier::new(Cow::Owned(specifier))
983 }
984
985 pub fn from_specifier_ref(specifier: &'a VersionSpecifier) -> Option<Self> {
989 TildeVersionSpecifier::new(Cow::Borrowed(specifier))
990 }
991
992 fn new(specifier: Cow<'a, VersionSpecifier>) -> Option<Self> {
993 if specifier.operator != Operator::TildeEqual {
994 return None;
995 }
996 if specifier.version().release().len() < 2 || specifier.version().release().len() > 3 {
997 return None;
998 }
999 if specifier.version().any_prerelease()
1000 || specifier.version().is_local()
1001 || specifier.version().is_post()
1002 {
1003 return None;
1004 }
1005 Some(Self { inner: specifier })
1006 }
1007
1008 pub fn has_patch(&self) -> bool {
1010 self.inner.version.release().len() == 3
1011 }
1012
1013 pub fn bounding_specifiers(&self) -> (VersionSpecifier, VersionSpecifier) {
1017 let release = self.inner.version().release();
1018 let lower = self.inner.version.clone();
1019 let upper = if self.has_patch() {
1020 Version::new([release[0], release[1] + 1])
1021 } else {
1022 Version::new([release[0] + 1])
1023 };
1024 (
1025 VersionSpecifier::greater_than_equal_version(lower),
1026 VersionSpecifier::less_than_version(upper),
1027 )
1028 }
1029
1030 pub fn with_patch_version(&self, patch: u64) -> TildeVersionSpecifier<'_> {
1032 let mut release = self.inner.version.release().to_vec();
1033 if self.has_patch() {
1034 release.pop();
1035 }
1036 release.push(patch);
1037 TildeVersionSpecifier::from_specifier(
1038 VersionSpecifier::from_version(Operator::TildeEqual, Version::new(release))
1039 .expect("We should always derive a valid new version specifier"),
1040 )
1041 .expect("We should always derive a new tilde version specifier")
1042 }
1043}
1044
1045impl std::fmt::Display for TildeVersionSpecifier<'_> {
1046 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1047 write!(f, "{}", self.inner)
1048 }
1049}
1050
1051#[cfg(test)]
1052mod tests {
1053 use std::{cmp::Ordering, str::FromStr};
1054
1055 use indoc::indoc;
1056
1057 use crate::LocalSegment;
1058
1059 use super::*;
1060
1061 #[test]
1063 fn test_equal() {
1064 let version = Version::from_str("1.1.post1").unwrap();
1065
1066 assert!(
1067 !VersionSpecifier::from_str("== 1.1")
1068 .unwrap()
1069 .contains(&version)
1070 );
1071 assert!(
1072 VersionSpecifier::from_str("== 1.1.post1")
1073 .unwrap()
1074 .contains(&version)
1075 );
1076 assert!(
1077 VersionSpecifier::from_str("== 1.1.*")
1078 .unwrap()
1079 .contains(&version)
1080 );
1081 }
1082
1083 const VERSIONS_ALL: &[&str] = &[
1084 "1.0.dev456",
1086 "1.0a1",
1087 "1.0a2.dev456",
1088 "1.0a12.dev456",
1089 "1.0a12",
1090 "1.0b1.dev456",
1091 "1.0b2",
1092 "1.0b2.post345.dev456",
1093 "1.0b2.post345",
1094 "1.0b2-346",
1095 "1.0c1.dev456",
1096 "1.0c1",
1097 "1.0rc2",
1098 "1.0c3",
1099 "1.0",
1100 "1.0.post456.dev34",
1101 "1.0.post456",
1102 "1.1.dev1",
1103 "1.2+123abc",
1104 "1.2+123abc456",
1105 "1.2+abc",
1106 "1.2+abc123",
1107 "1.2+abc123def",
1108 "1.2+1234.abc",
1109 "1.2+123456",
1110 "1.2.r32+123456",
1111 "1.2.rev33+123456",
1112 "1!1.0.dev456",
1114 "1!1.0a1",
1115 "1!1.0a2.dev456",
1116 "1!1.0a12.dev456",
1117 "1!1.0a12",
1118 "1!1.0b1.dev456",
1119 "1!1.0b2",
1120 "1!1.0b2.post345.dev456",
1121 "1!1.0b2.post345",
1122 "1!1.0b2-346",
1123 "1!1.0c1.dev456",
1124 "1!1.0c1",
1125 "1!1.0rc2",
1126 "1!1.0c3",
1127 "1!1.0",
1128 "1!1.0.post456.dev34",
1129 "1!1.0.post456",
1130 "1!1.1.dev1",
1131 "1!1.2+123abc",
1132 "1!1.2+123abc456",
1133 "1!1.2+abc",
1134 "1!1.2+abc123",
1135 "1!1.2+abc123def",
1136 "1!1.2+1234.abc",
1137 "1!1.2+123456",
1138 "1!1.2.r32+123456",
1139 "1!1.2.rev33+123456",
1140 ];
1141
1142 #[test]
1148 fn test_operators_true() {
1149 let versions: Vec<Version> = VERSIONS_ALL
1150 .iter()
1151 .map(|version| Version::from_str(version).unwrap())
1152 .collect();
1153
1154 let operations = [
1157 versions
1159 .iter()
1160 .enumerate()
1161 .flat_map(|(i, x)| {
1162 versions[i + 1..]
1163 .iter()
1164 .map(move |y| (x, y, Ordering::Less))
1165 })
1166 .collect::<Vec<_>>(),
1167 versions
1169 .iter()
1170 .map(move |x| (x, x, Ordering::Equal))
1171 .collect::<Vec<_>>(),
1172 versions
1174 .iter()
1175 .enumerate()
1176 .flat_map(|(i, x)| versions[..i].iter().map(move |y| (x, y, Ordering::Greater)))
1177 .collect::<Vec<_>>(),
1178 ]
1179 .into_iter()
1180 .flatten();
1181
1182 for (a, b, ordering) in operations {
1183 assert_eq!(a.cmp(b), ordering, "{a} {ordering:?} {b}");
1184 }
1185 }
1186
1187 const VERSIONS_0: &[&str] = &[
1188 "1.0.dev456",
1189 "1.0a1",
1190 "1.0a2.dev456",
1191 "1.0a12.dev456",
1192 "1.0a12",
1193 "1.0b1.dev456",
1194 "1.0b2",
1195 "1.0b2.post345.dev456",
1196 "1.0b2.post345",
1197 "1.0b2-346",
1198 "1.0c1.dev456",
1199 "1.0c1",
1200 "1.0rc2",
1201 "1.0c3",
1202 "1.0",
1203 "1.0.post456.dev34",
1204 "1.0.post456",
1205 "1.1.dev1",
1206 "1.2+123abc",
1207 "1.2+123abc456",
1208 "1.2+abc",
1209 "1.2+abc123",
1210 "1.2+abc123def",
1211 "1.2+1234.abc",
1212 "1.2+123456",
1213 "1.2.r32+123456",
1214 "1.2.rev33+123456",
1215 ];
1216
1217 const SPECIFIERS_OTHER: &[&str] = &[
1218 "== 1.*", "== 1.0.*", "== 1.1.*", "== 1.2.*", "== 2.*", "~= 1.0", "~= 1.0b1", "~= 1.1",
1219 "~= 1.2", "~= 2.0",
1220 ];
1221
1222 const EXPECTED_OTHER: &[[bool; 10]] = &[
1223 [
1224 true, true, false, false, false, false, false, false, false, false,
1225 ],
1226 [
1227 true, true, false, false, false, false, false, false, false, false,
1228 ],
1229 [
1230 true, true, false, false, false, false, false, false, false, false,
1231 ],
1232 [
1233 true, true, false, false, false, false, false, false, false, false,
1234 ],
1235 [
1236 true, true, false, false, false, false, false, false, false, false,
1237 ],
1238 [
1239 true, true, false, false, false, false, false, false, false, false,
1240 ],
1241 [
1242 true, true, false, false, false, false, true, false, false, false,
1243 ],
1244 [
1245 true, true, false, false, false, false, true, false, false, false,
1246 ],
1247 [
1248 true, true, false, false, false, false, true, false, false, false,
1249 ],
1250 [
1251 true, true, false, false, false, false, true, false, false, false,
1252 ],
1253 [
1254 true, true, false, false, false, false, true, false, false, false,
1255 ],
1256 [
1257 true, true, false, false, false, false, true, false, false, false,
1258 ],
1259 [
1260 true, true, false, false, false, false, true, false, false, false,
1261 ],
1262 [
1263 true, true, false, false, false, false, true, false, false, false,
1264 ],
1265 [
1266 true, true, false, false, false, true, true, false, false, false,
1267 ],
1268 [
1269 true, true, false, false, false, true, true, false, false, false,
1270 ],
1271 [
1272 true, true, false, false, false, true, true, false, false, false,
1273 ],
1274 [
1275 true, false, true, false, false, true, true, false, false, false,
1276 ],
1277 [
1278 true, false, false, true, false, true, true, true, true, false,
1279 ],
1280 [
1281 true, false, false, true, false, true, true, true, true, false,
1282 ],
1283 [
1284 true, false, false, true, false, true, true, true, true, false,
1285 ],
1286 [
1287 true, false, false, true, false, true, true, true, true, false,
1288 ],
1289 [
1290 true, false, false, true, false, true, true, true, true, false,
1291 ],
1292 [
1293 true, false, false, true, false, true, true, true, true, false,
1294 ],
1295 [
1296 true, false, false, true, false, true, true, true, true, false,
1297 ],
1298 [
1299 true, false, false, true, false, true, true, true, true, false,
1300 ],
1301 [
1302 true, false, false, true, false, true, true, true, true, false,
1303 ],
1304 ];
1305
1306 #[test]
1310 fn test_operators_other() {
1311 let versions = VERSIONS_0
1312 .iter()
1313 .map(|version| Version::from_str(version).unwrap());
1314 let specifiers: Vec<_> = SPECIFIERS_OTHER
1315 .iter()
1316 .map(|specifier| VersionSpecifier::from_str(specifier).unwrap())
1317 .collect();
1318
1319 for (version, expected) in versions.zip(EXPECTED_OTHER) {
1320 let actual = specifiers
1321 .iter()
1322 .map(|specifier| specifier.contains(&version));
1323 for ((actual, expected), _specifier) in actual.zip(expected).zip(SPECIFIERS_OTHER) {
1324 assert_eq!(actual, *expected);
1325 }
1326 }
1327 }
1328
1329 #[test]
1330 fn test_arbitrary_equality() {
1331 assert!(
1332 VersionSpecifier::from_str("=== 1.2a1")
1333 .unwrap()
1334 .contains(&Version::from_str("1.2a1").unwrap())
1335 );
1336 assert!(
1337 !VersionSpecifier::from_str("=== 1.2a1")
1338 .unwrap()
1339 .contains(&Version::from_str("1.2a1+local").unwrap())
1340 );
1341 }
1342
1343 #[test]
1344 fn test_equal_star_short_version_bug() {
1345 let specifier = VersionSpecifier::from_str("==2.1.*").unwrap();
1347 let version = Version::from_str("2").unwrap();
1348 assert!(
1349 !specifier.contains(&version),
1350 "Bug: version '2' incorrectly matches '==2.1.*'"
1351 );
1352
1353 let specifier = VersionSpecifier::from_str("!=2.1.*").unwrap();
1355 let version = Version::from_str("2").unwrap();
1356 assert!(
1357 specifier.contains(&version),
1358 "Bug: version '2' should match '!=2.1.*' (2.0 is not in 2.1 family)"
1359 );
1360
1361 let specifier = VersionSpecifier::from_str("==2.0.*").unwrap();
1363 let version = Version::from_str("2").unwrap();
1364 assert!(
1365 specifier.contains(&version),
1366 "version '2' should match '==2.0.*'"
1367 );
1368
1369 let specifier = VersionSpecifier::from_str("!=2.0.*").unwrap();
1371 let version = Version::from_str("2").unwrap();
1372 assert!(
1373 !specifier.contains(&version),
1374 "version '2' should not match '!=2.0.*'"
1375 );
1376
1377 let specifier = VersionSpecifier::from_str("==2.1.*").unwrap();
1380 let version = Version::from_str("2+local").unwrap();
1381 assert!(
1382 !specifier.contains(&version),
1383 "version '2+local' should not match '==2.1.*'"
1384 );
1385
1386 let specifier = VersionSpecifier::from_str("!=2.1.*").unwrap();
1388 let version = Version::from_str("2+local").unwrap();
1389 assert!(
1390 specifier.contains(&version),
1391 "version '2+local' should match '!=2.1.*'"
1392 );
1393 }
1394
1395 #[test]
1396 fn test_specifiers_true() {
1397 let pairs = [
1398 ("2.0", "==2"),
1400 ("2.0", "==2.0"),
1401 ("2.0", "==2.0.0"),
1402 ("2.0+deadbeef", "==2"),
1403 ("2.0+deadbeef", "==2.0"),
1404 ("2.0+deadbeef", "==2.0.0"),
1405 ("2.0+deadbeef", "==2+deadbeef"),
1406 ("2.0+deadbeef", "==2.0+deadbeef"),
1407 ("2.0+deadbeef", "==2.0.0+deadbeef"),
1408 ("2.0+deadbeef.0", "==2.0.0+deadbeef.00"),
1409 ("2.dev1", "==2.*"),
1411 ("2a1", "==2.*"),
1412 ("2a1.post1", "==2.*"),
1413 ("2b1", "==2.*"),
1414 ("2b1.dev1", "==2.*"),
1415 ("2c1", "==2.*"),
1416 ("2c1.post1.dev1", "==2.*"),
1417 ("2c1.post1.dev1", "==2.0.*"),
1418 ("2rc1", "==2.*"),
1419 ("2rc1", "==2.0.*"),
1420 ("2", "==2.*"),
1421 ("2", "==2.0.*"),
1422 ("2", "==0!2.*"),
1423 ("0!2", "==2.*"),
1424 ("2.0", "==2.*"),
1425 ("2.0.0", "==2.*"),
1426 ("2.1+local.version", "==2.1.*"),
1427 ("2.1", "!=2"),
1429 ("2.1", "!=2.0"),
1430 ("2.0.1", "!=2"),
1431 ("2.0.1", "!=2.0"),
1432 ("2.0.1", "!=2.0.0"),
1433 ("2.0", "!=2.0+deadbeef"),
1434 ("2.0", "!=3.*"),
1436 ("2.1", "!=2.0.*"),
1437 ("2.0", ">=2"),
1439 ("2.0", ">=2.0"),
1440 ("2.0", ">=2.0.0"),
1441 ("2.0.post1", ">=2"),
1442 ("2.0.post1.dev1", ">=2"),
1443 ("3", ">=2"),
1444 ("2.0", "<=2"),
1446 ("2.0", "<=2.0"),
1447 ("2.0", "<=2.0.0"),
1448 ("2.0.dev1", "<=2"),
1449 ("2.0a1", "<=2"),
1450 ("2.0a1.dev1", "<=2"),
1451 ("2.0b1", "<=2"),
1452 ("2.0b1.post1", "<=2"),
1453 ("2.0c1", "<=2"),
1454 ("2.0c1.post1.dev1", "<=2"),
1455 ("2.0rc1", "<=2"),
1456 ("1", "<=2"),
1457 ("3", ">2"),
1459 ("2.1", ">2.0"),
1460 ("2.0.1", ">2"),
1461 ("2.1.post1", ">2"),
1462 ("2.1+local.version", ">2"),
1463 ("2.post2", ">2.post1"),
1464 ("1", "<2"),
1466 ("2.0", "<2.1"),
1467 ("2.0.dev0", "<2.1"),
1468 ("0.1a1", "<0.1a2"),
1470 ("0.1dev1", "<0.1dev2"),
1471 ("0.1dev1", "<0.1a1"),
1472 ("1", "~=1.0"),
1474 ("1.0.1", "~=1.0"),
1475 ("1.1", "~=1.0"),
1476 ("1.9999999", "~=1.0"),
1477 ("1.1", "~=1.0a1"),
1478 ("2022.01.01", "~=2022.01.01"),
1479 ("2!1.0", "~=2!1.0"),
1481 ("2!1.0", "==2!1.*"),
1482 ("2!1.0", "==2!1.0"),
1483 ("2!1.0", "!=1.0"),
1484 ("1.0", "!=2!1.0"),
1485 ("1.0", "<=2!0.1"),
1486 ("2!1.0", ">=2.0"),
1487 ("1.0", "<2!0.1"),
1488 ("2!1.0", ">2.0"),
1489 ("2.0.5", ">2.0dev"),
1491 ];
1492
1493 for (s_version, s_spec) in pairs {
1494 let version = s_version.parse::<Version>().unwrap();
1495 let spec = s_spec.parse::<VersionSpecifier>().unwrap();
1496 assert!(
1497 spec.contains(&version),
1498 "{s_version} {s_spec}\nversion repr: {:?}\nspec version repr: {:?}",
1499 version.as_bloated_debug(),
1500 spec.version.as_bloated_debug(),
1501 );
1502 }
1503 }
1504
1505 #[test]
1506 fn test_specifier_false() {
1507 let pairs = [
1508 ("2.1", "==2"),
1510 ("2.1", "==2.0"),
1511 ("2.1", "==2.0.0"),
1512 ("2.0", "==2.0+deadbeef"),
1513 ("2.0", "==3.*"),
1515 ("2.1", "==2.0.*"),
1516 ("2.0", "!=2"),
1518 ("2.0", "!=2.0"),
1519 ("2.0", "!=2.0.0"),
1520 ("2.0+deadbeef", "!=2"),
1521 ("2.0+deadbeef", "!=2.0"),
1522 ("2.0+deadbeef", "!=2.0.0"),
1523 ("2.0+deadbeef", "!=2+deadbeef"),
1524 ("2.0+deadbeef", "!=2.0+deadbeef"),
1525 ("2.0+deadbeef", "!=2.0.0+deadbeef"),
1526 ("2.0+deadbeef.0", "!=2.0.0+deadbeef.00"),
1527 ("2.dev1", "!=2.*"),
1529 ("2a1", "!=2.*"),
1530 ("2a1.post1", "!=2.*"),
1531 ("2b1", "!=2.*"),
1532 ("2b1.dev1", "!=2.*"),
1533 ("2c1", "!=2.*"),
1534 ("2c1.post1.dev1", "!=2.*"),
1535 ("2c1.post1.dev1", "!=2.0.*"),
1536 ("2rc1", "!=2.*"),
1537 ("2rc1", "!=2.0.*"),
1538 ("2", "!=2.*"),
1539 ("2", "!=2.0.*"),
1540 ("2.0", "!=2.*"),
1541 ("2.0.0", "!=2.*"),
1542 ("2.0.dev1", ">=2"),
1544 ("2.0a1", ">=2"),
1545 ("2.0a1.dev1", ">=2"),
1546 ("2.0b1", ">=2"),
1547 ("2.0b1.post1", ">=2"),
1548 ("2.0c1", ">=2"),
1549 ("2.0c1.post1.dev1", ">=2"),
1550 ("2.0rc1", ">=2"),
1551 ("1", ">=2"),
1552 ("2.0.post1", "<=2"),
1554 ("2.0.post1.dev1", "<=2"),
1555 ("3", "<=2"),
1556 ("1", ">2"),
1558 ("2.0.dev1", ">2"),
1559 ("2.0a1", ">2"),
1560 ("2.0a1.post1", ">2"),
1561 ("2.0b1", ">2"),
1562 ("2.0b1.dev1", ">2"),
1563 ("2.0c1", ">2"),
1564 ("2.0c1.post1.dev1", ">2"),
1565 ("2.0rc1", ">2"),
1566 ("2.0", ">2"),
1567 ("2.post2", ">2"),
1568 ("2.0.post1", ">2"),
1569 ("2.0.post1.dev1", ">2"),
1570 ("2.0+local.version", ">2"),
1571 ("2.0.dev1", "<2"),
1573 ("2.0a1", "<2"),
1574 ("2.0a1.post1", "<2"),
1575 ("2.0b1", "<2"),
1576 ("2.0b2.dev1", "<2"),
1577 ("2.0c1", "<2"),
1578 ("2.0c1.post1.dev1", "<2"),
1579 ("2.0rc1", "<2"),
1580 ("2.0", "<2"),
1581 ("2.post1", "<2"),
1582 ("2.post1.dev1", "<2"),
1583 ("3", "<2"),
1584 ("2.0", "~=1.0"),
1586 ("1.1.0", "~=1.0.0"),
1587 ("1.1.post1", "~=1.0.0"),
1588 ("1.0", "~=2!1.0"),
1590 ("2!1.0", "~=1.0"),
1591 ("2!1.0", "==1.0"),
1592 ("1.0", "==2!1.0"),
1593 ("2!1.0", "==1.*"),
1594 ("1.0", "==2!1.*"),
1595 ("2!1.0", "!=2!1.0"),
1596 ];
1597 for (version, specifier) in pairs {
1598 assert!(
1599 !VersionSpecifier::from_str(specifier)
1600 .unwrap()
1601 .contains(&Version::from_str(version).unwrap()),
1602 "{version} {specifier}"
1603 );
1604 }
1605 }
1606
1607 #[test]
1608 fn test_parse_version_specifiers() {
1609 let result = VersionSpecifiers::from_str("~= 0.9, >= 1.0, != 1.3.4.*, < 2.0").unwrap();
1610 assert_eq!(
1611 result.0.as_ref(),
1612 [
1613 VersionSpecifier {
1614 operator: Operator::TildeEqual,
1615 version: Version::new([0, 9]),
1616 },
1617 VersionSpecifier {
1618 operator: Operator::GreaterThanEqual,
1619 version: Version::new([1, 0]),
1620 },
1621 VersionSpecifier {
1622 operator: Operator::NotEqualStar,
1623 version: Version::new([1, 3, 4]),
1624 },
1625 VersionSpecifier {
1626 operator: Operator::LessThan,
1627 version: Version::new([2, 0]),
1628 }
1629 ]
1630 );
1631 }
1632
1633 #[test]
1634 fn test_parse_error() {
1635 let result = VersionSpecifiers::from_str("~= 0.9, %= 1.0, != 1.3.4.*");
1636 assert_eq!(
1637 result.unwrap_err().to_string(),
1638 indoc! {r"
1639 Failed to parse version: Unexpected end of version specifier, expected operator:
1640 ~= 0.9, %= 1.0, != 1.3.4.*
1641 ^^^^^^^
1642 "}
1643 );
1644 }
1645
1646 #[test]
1647 fn test_parse_specifier_missing_operator_error() {
1648 let result = VersionSpecifiers::from_str("3.12");
1649 assert_eq!(
1650 result.unwrap_err().to_string(),
1651 indoc! {"
1652 Failed to parse version: Unexpected end of version specifier, expected operator. Did you mean `==3.12`?:
1653 3.12
1654 ^^^^
1655 "}
1656 );
1657 }
1658
1659 #[test]
1660 fn test_parse_specifier_missing_operator_invalid_version_error() {
1661 let result = VersionSpecifiers::from_str("blergh");
1662 assert_eq!(
1663 result.unwrap_err().to_string(),
1664 indoc! {r"
1665 Failed to parse version: Unexpected end of version specifier, expected operator:
1666 blergh
1667 ^^^^^^
1668 "}
1669 );
1670 }
1671
1672 #[test]
1673 fn test_non_star_after_star() {
1674 let result = VersionSpecifiers::from_str("== 0.9.*.1");
1675 assert_eq!(
1676 result.unwrap_err().inner.err,
1677 ParseErrorKind::InvalidVersion(version::PatternErrorKind::WildcardNotTrailing.into())
1678 .into(),
1679 );
1680 }
1681
1682 #[test]
1683 fn test_star_wrong_operator() {
1684 let result = VersionSpecifiers::from_str(">= 0.9.1.*");
1685 assert_eq!(
1686 result.unwrap_err().inner.err,
1687 ParseErrorKind::InvalidSpecifier(
1688 BuildErrorKind::OperatorWithStar {
1689 operator: Operator::GreaterThanEqual,
1690 }
1691 .into()
1692 )
1693 .into(),
1694 );
1695 }
1696
1697 #[test]
1698 fn test_invalid_word() {
1699 let result = VersionSpecifiers::from_str("blergh");
1700 assert_eq!(
1701 result.unwrap_err().inner.err,
1702 ParseErrorKind::MissingOperator(VersionOperatorBuildError {
1703 version_pattern: None
1704 })
1705 .into(),
1706 );
1707 }
1708
1709 #[test]
1711 fn test_invalid_specifier() {
1712 let specifiers = [
1713 (
1715 "2.0",
1716 ParseErrorKind::MissingOperator(VersionOperatorBuildError {
1717 version_pattern: VersionPattern::from_str("2.0").ok(),
1718 })
1719 .into(),
1720 ),
1721 (
1723 "=>2.0",
1724 ParseErrorKind::InvalidOperator(OperatorParseError {
1725 got: "=>".to_string(),
1726 })
1727 .into(),
1728 ),
1729 ("==", ParseErrorKind::MissingVersion.into()),
1731 (
1733 "~=1.0+5",
1734 ParseErrorKind::InvalidSpecifier(
1735 BuildErrorKind::OperatorLocalCombo {
1736 operator: Operator::TildeEqual,
1737 version: Version::new([1, 0])
1738 .with_local_segments(vec![LocalSegment::Number(5)]),
1739 }
1740 .into(),
1741 )
1742 .into(),
1743 ),
1744 (
1745 ">=1.0+deadbeef",
1746 ParseErrorKind::InvalidSpecifier(
1747 BuildErrorKind::OperatorLocalCombo {
1748 operator: Operator::GreaterThanEqual,
1749 version: Version::new([1, 0]).with_local_segments(vec![
1750 LocalSegment::String("deadbeef".to_string()),
1751 ]),
1752 }
1753 .into(),
1754 )
1755 .into(),
1756 ),
1757 (
1758 "<=1.0+abc123",
1759 ParseErrorKind::InvalidSpecifier(
1760 BuildErrorKind::OperatorLocalCombo {
1761 operator: Operator::LessThanEqual,
1762 version: Version::new([1, 0])
1763 .with_local_segments(vec![LocalSegment::String("abc123".to_string())]),
1764 }
1765 .into(),
1766 )
1767 .into(),
1768 ),
1769 (
1770 ">1.0+watwat",
1771 ParseErrorKind::InvalidSpecifier(
1772 BuildErrorKind::OperatorLocalCombo {
1773 operator: Operator::GreaterThan,
1774 version: Version::new([1, 0])
1775 .with_local_segments(vec![LocalSegment::String("watwat".to_string())]),
1776 }
1777 .into(),
1778 )
1779 .into(),
1780 ),
1781 (
1782 "<1.0+1.0",
1783 ParseErrorKind::InvalidSpecifier(
1784 BuildErrorKind::OperatorLocalCombo {
1785 operator: Operator::LessThan,
1786 version: Version::new([1, 0]).with_local_segments(vec![
1787 LocalSegment::Number(1),
1788 LocalSegment::Number(0),
1789 ]),
1790 }
1791 .into(),
1792 )
1793 .into(),
1794 ),
1795 (
1797 "~=1.0.*",
1798 ParseErrorKind::InvalidSpecifier(
1799 BuildErrorKind::OperatorWithStar {
1800 operator: Operator::TildeEqual,
1801 }
1802 .into(),
1803 )
1804 .into(),
1805 ),
1806 (
1807 ">=1.0.*",
1808 ParseErrorKind::InvalidSpecifier(
1809 BuildErrorKind::OperatorWithStar {
1810 operator: Operator::GreaterThanEqual,
1811 }
1812 .into(),
1813 )
1814 .into(),
1815 ),
1816 (
1817 "<=1.0.*",
1818 ParseErrorKind::InvalidSpecifier(
1819 BuildErrorKind::OperatorWithStar {
1820 operator: Operator::LessThanEqual,
1821 }
1822 .into(),
1823 )
1824 .into(),
1825 ),
1826 (
1827 ">1.0.*",
1828 ParseErrorKind::InvalidSpecifier(
1829 BuildErrorKind::OperatorWithStar {
1830 operator: Operator::GreaterThan,
1831 }
1832 .into(),
1833 )
1834 .into(),
1835 ),
1836 (
1837 "<1.0.*",
1838 ParseErrorKind::InvalidSpecifier(
1839 BuildErrorKind::OperatorWithStar {
1840 operator: Operator::LessThan,
1841 }
1842 .into(),
1843 )
1844 .into(),
1845 ),
1846 (
1849 "==1.0.*+5",
1850 ParseErrorKind::InvalidVersion(
1851 version::PatternErrorKind::WildcardNotTrailing.into(),
1852 )
1853 .into(),
1854 ),
1855 (
1856 "!=1.0.*+deadbeef",
1857 ParseErrorKind::InvalidVersion(
1858 version::PatternErrorKind::WildcardNotTrailing.into(),
1859 )
1860 .into(),
1861 ),
1862 (
1865 "==2.0a1.*",
1866 ParseErrorKind::InvalidVersion(
1867 version::ErrorKind::UnexpectedEnd {
1868 version: "2.0a1".to_string(),
1869 remaining: ".*".to_string(),
1870 }
1871 .into(),
1872 )
1873 .into(),
1874 ),
1875 (
1876 "!=2.0a1.*",
1877 ParseErrorKind::InvalidVersion(
1878 version::ErrorKind::UnexpectedEnd {
1879 version: "2.0a1".to_string(),
1880 remaining: ".*".to_string(),
1881 }
1882 .into(),
1883 )
1884 .into(),
1885 ),
1886 (
1887 "==2.0.post1.*",
1888 ParseErrorKind::InvalidVersion(
1889 version::ErrorKind::UnexpectedEnd {
1890 version: "2.0.post1".to_string(),
1891 remaining: ".*".to_string(),
1892 }
1893 .into(),
1894 )
1895 .into(),
1896 ),
1897 (
1898 "!=2.0.post1.*",
1899 ParseErrorKind::InvalidVersion(
1900 version::ErrorKind::UnexpectedEnd {
1901 version: "2.0.post1".to_string(),
1902 remaining: ".*".to_string(),
1903 }
1904 .into(),
1905 )
1906 .into(),
1907 ),
1908 (
1909 "==2.0.dev1.*",
1910 ParseErrorKind::InvalidVersion(
1911 version::ErrorKind::UnexpectedEnd {
1912 version: "2.0.dev1".to_string(),
1913 remaining: ".*".to_string(),
1914 }
1915 .into(),
1916 )
1917 .into(),
1918 ),
1919 (
1920 "!=2.0.dev1.*",
1921 ParseErrorKind::InvalidVersion(
1922 version::ErrorKind::UnexpectedEnd {
1923 version: "2.0.dev1".to_string(),
1924 remaining: ".*".to_string(),
1925 }
1926 .into(),
1927 )
1928 .into(),
1929 ),
1930 (
1931 "==1.0+5.*",
1932 ParseErrorKind::InvalidVersion(
1933 version::ErrorKind::LocalEmpty { precursor: '.' }.into(),
1934 )
1935 .into(),
1936 ),
1937 (
1938 "!=1.0+deadbeef.*",
1939 ParseErrorKind::InvalidVersion(
1940 version::ErrorKind::LocalEmpty { precursor: '.' }.into(),
1941 )
1942 .into(),
1943 ),
1944 (
1946 "==1.0.*.5",
1947 ParseErrorKind::InvalidVersion(
1948 version::PatternErrorKind::WildcardNotTrailing.into(),
1949 )
1950 .into(),
1951 ),
1952 (
1954 "~=1",
1955 ParseErrorKind::InvalidSpecifier(BuildErrorKind::CompatibleRelease.into()).into(),
1956 ),
1957 (
1959 "==1.0.dev1.*",
1960 ParseErrorKind::InvalidVersion(
1961 version::ErrorKind::UnexpectedEnd {
1962 version: "1.0.dev1".to_string(),
1963 remaining: ".*".to_string(),
1964 }
1965 .into(),
1966 )
1967 .into(),
1968 ),
1969 (
1970 "!=1.0.dev1.*",
1971 ParseErrorKind::InvalidVersion(
1972 version::ErrorKind::UnexpectedEnd {
1973 version: "1.0.dev1".to_string(),
1974 remaining: ".*".to_string(),
1975 }
1976 .into(),
1977 )
1978 .into(),
1979 ),
1980 ];
1981 for (specifier, error) in specifiers {
1982 assert_eq!(VersionSpecifier::from_str(specifier).unwrap_err(), error);
1983 }
1984 }
1985
1986 #[test]
1987 fn test_display_start() {
1988 assert_eq!(
1989 VersionSpecifier::from_str("== 1.1.*")
1990 .unwrap()
1991 .to_string(),
1992 "==1.1.*"
1993 );
1994 assert_eq!(
1995 VersionSpecifier::from_str("!= 1.1.*")
1996 .unwrap()
1997 .to_string(),
1998 "!=1.1.*"
1999 );
2000 }
2001
2002 #[test]
2003 fn test_version_specifiers_str() {
2004 assert_eq!(
2005 VersionSpecifiers::from_str(">= 3.7").unwrap().to_string(),
2006 ">=3.7"
2007 );
2008 assert_eq!(
2009 VersionSpecifiers::from_str(">=3.7, < 4.0, != 3.9.0")
2010 .unwrap()
2011 .to_string(),
2012 ">=3.7, !=3.9.0, <4.0"
2013 );
2014 }
2015
2016 #[test]
2017 fn test_version_specifiers_singular_interval() {
2018 let lower_then_upper = VersionSpecifiers::from_str(">=1.4.4, <=1.4.4").unwrap();
2019 let upper_then_lower = VersionSpecifiers::from_str("<=1.4.4, >=1.4.4").unwrap();
2020
2021 assert_eq!(lower_then_upper, upper_then_lower);
2022 assert_eq!(lower_then_upper.to_string(), "<=1.4.4, >=1.4.4");
2023 }
2024
2025 #[test]
2028 fn test_version_specifiers_empty() {
2029 assert_eq!(VersionSpecifiers::from_str("").unwrap().to_string(), "");
2030 }
2031
2032 #[test]
2036 fn non_ascii_version_specifier() {
2037 let s = "💩";
2038 let err = s.parse::<VersionSpecifiers>().unwrap_err();
2039 assert_eq!(err.inner.start, 0);
2040 assert_eq!(err.inner.end, 4);
2041
2042 let s = ">=3.7, <4.0,>5.%";
2046 let err = s.parse::<VersionSpecifiers>().unwrap_err();
2047 assert_eq!(err.inner.start, 12);
2048 assert_eq!(err.inner.end, 16);
2049 let s = ">=3.7,\u{3000}<4.0,>5.%";
2058 let err = s.parse::<VersionSpecifiers>().unwrap_err();
2059 assert_eq!(err.inner.start, 14);
2060 assert_eq!(err.inner.end, 18);
2061 }
2062
2063 #[test]
2066 fn error_message_version_specifiers_parse_error() {
2067 let specs = ">=1.2.3, 5.4.3, >=3.4.5";
2068 let err = VersionSpecifierParseError {
2069 kind: Box::new(ParseErrorKind::MissingOperator(VersionOperatorBuildError {
2070 version_pattern: VersionPattern::from_str("5.4.3").ok(),
2071 })),
2072 };
2073 let inner = Box::new(VersionSpecifiersParseErrorInner {
2074 err,
2075 line: specs.to_string(),
2076 start: 8,
2077 end: 14,
2078 });
2079 let err = VersionSpecifiersParseError { inner };
2080 assert_eq!(err, VersionSpecifiers::from_str(specs).unwrap_err());
2081 assert_eq!(
2082 err.to_string(),
2083 "\
2084Failed to parse version: Unexpected end of version specifier, expected operator. Did you mean `==5.4.3`?:
2085>=1.2.3, 5.4.3, >=3.4.5
2086 ^^^^^^
2087"
2088 );
2089 }
2090
2091 #[test]
2094 fn error_message_version_specifier_build_error() {
2095 let err = VersionSpecifierBuildError {
2096 kind: Box::new(BuildErrorKind::CompatibleRelease),
2097 };
2098 let op = Operator::TildeEqual;
2099 let v = Version::new([5]);
2100 let vpat = VersionPattern::verbatim(v);
2101 assert_eq!(err, VersionSpecifier::from_pattern(op, vpat).unwrap_err());
2102 assert_eq!(
2103 err.to_string(),
2104 "The ~= operator requires at least two segments in the release version"
2105 );
2106 }
2107
2108 #[test]
2111 fn error_message_version_specifier_parse_error() {
2112 let err = VersionSpecifierParseError {
2113 kind: Box::new(ParseErrorKind::InvalidSpecifier(
2114 VersionSpecifierBuildError {
2115 kind: Box::new(BuildErrorKind::CompatibleRelease),
2116 },
2117 )),
2118 };
2119 assert_eq!(err, VersionSpecifier::from_str("~=5").unwrap_err());
2120 assert_eq!(
2121 err.to_string(),
2122 "The ~= operator requires at least two segments in the release version"
2123 );
2124 }
2125
2126 #[test]
2129 fn trailing_zero_equality() {
2130 let equal = [
2131 (">=3.3", ">=3.3.0"),
2133 ("<2", "<2.0.0"),
2134 ("==1.2", "==1.2.0"),
2135 ("~=2.2.0", "~=2.2.0"),
2137 ];
2138 for (a, b) in equal {
2139 let a = VersionSpecifier::from_str(a).unwrap();
2140 let b = VersionSpecifier::from_str(b).unwrap();
2141 assert_eq!(a, b);
2142 }
2143
2144 let not_equal = [
2145 ("~=2.2", "~=2.2.0"),
2147 ("~=1.4.5", "~=1.4.5.0"),
2148 ("~=2.2.post3", "~=2.2.post5"),
2150 ("~=2.2.post3", "~=2.2.0.post3"),
2152 ];
2153 for (a, b) in not_equal {
2154 let a = VersionSpecifier::from_str(a).unwrap();
2155 let b = VersionSpecifier::from_str(b).unwrap();
2156 assert_ne!(a, b);
2157 }
2158 }
2159
2160 #[test]
2162 fn bounding_specifiers_u64_max_rejected_at_parse_time() {
2163 assert!(VersionSpecifier::from_str("~=3.18446744073709551615.0").is_err());
2164 assert!(VersionSpecifier::from_str("~=18446744073709551615.0").is_err());
2165
2166 let specifier = VersionSpecifier::from_str("~=3.18446744073709551614.0").unwrap();
2168 let tilde = TildeVersionSpecifier::from_specifier(specifier).unwrap();
2169 let (_lower, _upper) = tilde.bounding_specifiers();
2170 }
2171}