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 gap_specifiers = match (next, lower) {
97 (Bound::Excluded(prev), Bound::Excluded(lower)) if prev == lower => {
99 vec![VersionSpecifier::not_equals_version(prev.clone())]
100 }
101 (Bound::Excluded(prev), Bound::Included(lower)) => {
103 let prev = prev.only_release_trimmed();
104 let lower = lower.only_release_trimmed();
105 match (&*prev.release(), &*lower.release()) {
106 ([major], [lower_major, lower_minor]) if major == lower_major => (0
107 ..*lower_minor)
108 .map(|minor| {
109 VersionSpecifier::not_equals_star_version(Version::new([
110 *major, minor,
111 ]))
112 })
113 .collect(),
114 ([major, minor], [lower_major, lower_minor])
115 if major == lower_major && minor < lower_minor =>
116 {
117 (*minor..*lower_minor)
118 .map(|minor| {
119 VersionSpecifier::not_equals_star_version(Version::new([
120 *major, minor,
121 ]))
122 })
123 .collect()
124 }
125 _ => Vec::new(),
126 }
127 }
128 _ => Vec::new(),
129 };
130 if gap_specifiers.is_empty() {
131 #[cfg(feature = "tracing")]
132 warn!(
133 "Ignoring unsupported gap in `requires-python` version: {next:?} -> {lower:?}"
134 );
135 } else {
136 specifiers.extend(gap_specifiers);
137 }
138 next = upper;
139 }
140 let end = next;
141
142 specifiers.extend(VersionSpecifier::from_release_only_bounds((start, end)));
144
145 Self::from_unsorted(specifiers)
146 }
147}
148
149impl FromIterator<VersionSpecifier> for VersionSpecifiers {
150 fn from_iter<T: IntoIterator<Item = VersionSpecifier>>(iter: T) -> Self {
151 Self::from_unsorted(iter.into_iter().collect())
152 }
153}
154
155impl IntoIterator for VersionSpecifiers {
156 type Item = VersionSpecifier;
157 type IntoIter = std::vec::IntoIter<VersionSpecifier>;
158
159 fn into_iter(self) -> Self::IntoIter {
160 self.0.into_vec().into_iter()
161 }
162}
163
164impl FromStr for VersionSpecifiers {
165 type Err = VersionSpecifiersParseError;
166
167 fn from_str(s: &str) -> Result<Self, Self::Err> {
168 let separator_count = s.bytes().filter(|byte| *byte == b',').count();
169 if separator_count == 0 {
170 if s.is_empty() {
171 return Ok(Self::empty());
172 }
173 return VersionSpecifier::from_str(s)
174 .map(Self::from)
175 .map_err(|err| VersionSpecifiersParseError {
176 inner: Box::new(VersionSpecifiersParseErrorInner {
177 err,
178 line: s.to_string(),
179 start: 0,
180 end: s.len(),
181 }),
182 });
183 }
184 parse_version_specifiers(s, separator_count + 1).map(Self::from_unsorted)
185 }
186}
187
188impl From<VersionSpecifier> for VersionSpecifiers {
189 fn from(specifier: VersionSpecifier) -> Self {
190 Self(Box::new([specifier]))
191 }
192}
193
194impl std::fmt::Display for VersionSpecifiers {
195 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
196 for (idx, version_specifier) in self.0.iter().enumerate() {
197 if idx == 0 {
200 write!(f, "{version_specifier}")?;
201 } else {
202 write!(f, ", {version_specifier}")?;
203 }
204 }
205 Ok(())
206 }
207}
208
209impl Default for VersionSpecifiers {
210 fn default() -> Self {
211 Self::empty()
212 }
213}
214
215impl<'de> Deserialize<'de> for VersionSpecifiers {
216 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
217 where
218 D: Deserializer<'de>,
219 {
220 struct Visitor;
221
222 impl de::Visitor<'_> for Visitor {
223 type Value = VersionSpecifiers;
224
225 fn expecting(&self, f: &mut Formatter) -> std::fmt::Result {
226 f.write_str("a string")
227 }
228
229 fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
230 VersionSpecifiers::from_str(v).map_err(de::Error::custom)
231 }
232 }
233
234 deserializer.deserialize_str(Visitor)
235 }
236}
237
238impl Serialize for VersionSpecifiers {
239 #[allow(unstable_name_collisions)]
240 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
241 where
242 S: Serializer,
243 {
244 serializer.serialize_str(
245 &self
246 .iter()
247 .map(ToString::to_string)
248 .collect::<Vec<String>>()
249 .join(","),
250 )
251 }
252}
253
254#[derive(Debug, Eq, PartialEq, Clone)]
256pub struct VersionSpecifiersParseError {
257 inner: Box<VersionSpecifiersParseErrorInner>,
260}
261
262#[derive(Debug, Eq, PartialEq, Clone)]
263struct VersionSpecifiersParseErrorInner {
264 err: VersionSpecifierParseError,
266 line: String,
268 start: usize,
271 end: usize,
274}
275
276impl std::fmt::Display for VersionSpecifiersParseError {
277 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
278 use unicode_width::UnicodeWidthStr;
279
280 let VersionSpecifiersParseErrorInner {
281 ref err,
282 ref line,
283 start,
284 end,
285 } = *self.inner;
286 writeln!(f, "Failed to parse version: {err}:")?;
287 writeln!(f, "{line}")?;
288 let indent = line[..start].width();
289 let point = line[start..end].width();
290 writeln!(f, "{}{}", " ".repeat(indent), "^".repeat(point))?;
291 Ok(())
292 }
293}
294
295impl VersionSpecifiersParseError {
296 pub fn line(&self) -> &String {
298 &self.inner.line
299 }
300}
301
302impl std::error::Error for VersionSpecifiersParseError {}
303
304#[derive(Debug, Clone)]
321#[cfg_attr(
322 feature = "rkyv",
323 derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)
324)]
325#[cfg_attr(feature = "rkyv", rkyv(derive(Debug)))]
326pub struct VersionSpecifier {
327 pub(crate) operator: Operator,
329 pub(crate) version: Version,
331}
332
333impl PartialEq for VersionSpecifier {
334 fn eq(&self, other: &Self) -> bool {
335 if self.operator != other.operator {
336 return false;
337 }
338 if self.operator == Operator::TildeEqual
340 && self.version.release().len() != other.version.release().len()
341 {
342 return false;
343 }
344 self.version == other.version
345 }
346}
347
348impl Eq for VersionSpecifier {}
349
350impl Hash for VersionSpecifier {
351 fn hash<H: Hasher>(&self, state: &mut H) {
352 self.operator.hash(state);
353 if self.operator == Operator::TildeEqual {
356 self.version.release().len().hash(state);
357 }
358 self.version.hash(state);
359 }
360}
361
362impl PartialOrd for VersionSpecifier {
363 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
364 Some(self.cmp(other))
365 }
366}
367
368impl Ord for VersionSpecifier {
369 fn cmp(&self, other: &Self) -> Ordering {
370 self.operator
371 .cmp(&other.operator)
372 .then_with(|| self.version.cmp(&other.version))
373 .then_with(|| {
374 if self.operator == Operator::TildeEqual {
376 self.version
377 .release()
378 .len()
379 .cmp(&other.version.release().len())
380 } else {
381 Ordering::Equal
382 }
383 })
384 }
385}
386
387impl<'de> Deserialize<'de> for VersionSpecifier {
388 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
389 where
390 D: Deserializer<'de>,
391 {
392 struct Visitor;
393
394 impl de::Visitor<'_> for Visitor {
395 type Value = VersionSpecifier;
396
397 fn expecting(&self, f: &mut Formatter) -> std::fmt::Result {
398 f.write_str("a string")
399 }
400
401 fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
402 VersionSpecifier::from_str(v).map_err(de::Error::custom)
403 }
404 }
405
406 deserializer.deserialize_str(Visitor)
407 }
408}
409
410impl Serialize for VersionSpecifier {
412 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
413 where
414 S: Serializer,
415 {
416 serializer.collect_str(self)
417 }
418}
419
420impl VersionSpecifier {
421 pub fn from_pattern(
424 operator: Operator,
425 version_pattern: VersionPattern,
426 ) -> Result<Self, VersionSpecifierBuildError> {
427 let star = version_pattern.is_wildcard();
428 let version = version_pattern.into_version();
429
430 let operator = if star {
432 match operator.to_star() {
433 Some(starop) => starop,
434 None => {
435 return Err(BuildErrorKind::OperatorWithStar { operator }.into());
436 }
437 }
438 } else {
439 operator
440 };
441
442 Self::from_version(operator, version)
443 }
444
445 pub fn from_version(
447 operator: Operator,
448 version: Version,
449 ) -> Result<Self, VersionSpecifierBuildError> {
450 if version.is_local() && !operator.is_local_compatible() {
452 return Err(BuildErrorKind::OperatorLocalCombo { operator, version }.into());
453 }
454
455 if operator == Operator::TildeEqual && version.release().len() < 2 {
456 return Err(BuildErrorKind::CompatibleRelease.into());
457 }
458
459 Ok(Self { operator, version })
460 }
461
462 #[must_use]
482 pub fn only_release(self) -> Self {
483 Self {
484 operator: self.operator,
485 version: self.version.only_release(),
486 }
487 }
488
489 #[must_use]
491 pub fn only_minor_release(&self) -> Self {
492 Self {
493 operator: self.operator,
494 version: self.version.only_minor_release(),
495 }
496 }
497
498 pub fn equals_version(version: Version) -> Self {
500 Self {
501 operator: Operator::Equal,
502 version,
503 }
504 }
505
506 pub fn equals_star_version(version: Version) -> Self {
508 Self {
509 operator: Operator::EqualStar,
510 version,
511 }
512 }
513
514 pub fn not_equals_star_version(version: Version) -> Self {
516 Self {
517 operator: Operator::NotEqualStar,
518 version,
519 }
520 }
521
522 pub fn not_equals_version(version: Version) -> Self {
524 Self {
525 operator: Operator::NotEqual,
526 version,
527 }
528 }
529
530 pub fn greater_than_equal_version(version: Version) -> Self {
532 Self {
533 operator: Operator::GreaterThanEqual,
534 version,
535 }
536 }
537 pub fn greater_than_version(version: Version) -> Self {
539 Self {
540 operator: Operator::GreaterThan,
541 version,
542 }
543 }
544
545 pub fn less_than_equal_version(version: Version) -> Self {
547 Self {
548 operator: Operator::LessThanEqual,
549 version,
550 }
551 }
552
553 pub fn less_than_version(version: Version) -> Self {
555 Self {
556 operator: Operator::LessThan,
557 version,
558 }
559 }
560
561 pub fn operator(&self) -> &Operator {
563 &self.operator
564 }
565
566 pub fn version(&self) -> &Version {
568 &self.version
569 }
570
571 pub fn any_prerelease(&self) -> bool {
573 self.version.any_prerelease()
574 }
575
576 pub fn from_release_only_bounds(
580 bounds: (Bound<&Version>, Bound<&Version>),
581 ) -> impl Iterator<Item = Self> {
582 let (b1, b2) = match bounds {
583 (Bound::Included(v1), Bound::Included(v2)) if v1 == v2 => {
584 (Some(Self::equals_version(v1.clone())), None)
585 }
586 (Bound::Included(v1), Bound::Excluded(v2)) => {
588 match *v1.only_release_trimmed().release() {
589 [major] if *v2.only_release_trimmed().release() == [major, 1] => {
590 let version = Version::new([major, 0]);
591 (Some(Self::equals_star_version(version)), None)
592 }
593 [major, minor]
594 if *v2.only_release_trimmed().release() == [major, minor + 1] =>
595 {
596 let version = Version::new([major, minor]);
597 (Some(Self::equals_star_version(version)), None)
598 }
599 _ => (
600 Self::from_lower_bound(Bound::Included(v1)),
601 Self::from_upper_bound(Bound::Excluded(v2)),
602 ),
603 }
604 }
605 (lower, upper) => (Self::from_lower_bound(lower), Self::from_upper_bound(upper)),
606 };
607
608 b1.into_iter().chain(b2)
609 }
610
611 fn from_lower_bound(bound: Bound<&Version>) -> Option<Self> {
613 match bound {
614 Bound::Included(version) => {
615 Some(Self::from_version(Operator::GreaterThanEqual, version.clone()).unwrap())
616 }
617 Bound::Excluded(version) => {
618 Some(Self::from_version(Operator::GreaterThan, version.clone()).unwrap())
619 }
620 Bound::Unbounded => None,
621 }
622 }
623
624 fn from_upper_bound(bound: Bound<&Version>) -> Option<Self> {
626 match bound {
627 Bound::Included(version) => {
628 Some(Self::from_version(Operator::LessThanEqual, version.clone()).unwrap())
629 }
630 Bound::Excluded(version) => {
631 Some(Self::from_version(Operator::LessThan, version.clone()).unwrap())
632 }
633 Bound::Unbounded => None,
634 }
635 }
636
637 pub fn contains(&self, version: &Version) -> bool {
645 let this = self.version();
649 let other = if this.local().is_empty() && !version.local().is_empty() {
650 Cow::Owned(version.clone().without_local())
651 } else {
652 Cow::Borrowed(version)
653 };
654
655 match self.operator {
656 Operator::Equal => other.as_ref() == this,
657 Operator::EqualStar => {
658 this.epoch() == other.epoch()
659 && self
660 .version
661 .release()
662 .iter()
663 .zip(other.release().iter().chain(std::iter::repeat(&0)))
667 .all(|(this, other)| this == other)
668 }
669 #[allow(deprecated)]
670 Operator::ExactEqual => {
671 #[cfg(feature = "tracing")]
672 {
673 warn!("Using arbitrary equality (`===`) is discouraged");
674 }
675 self.version.to_string() == version.to_string()
676 }
677 Operator::NotEqual => this != other.as_ref(),
678 Operator::NotEqualStar => {
679 this.epoch() != other.epoch()
680 || !this
681 .release()
682 .iter()
683 .zip(other.release().iter().chain(std::iter::repeat(&0)))
687 .all(|(this, other)| this == other)
688 }
689 Operator::TildeEqual => {
690 assert!(this.release().len() > 1);
695 if this.epoch() != other.epoch() {
696 return false;
697 }
698
699 if !this.release()[..this.release().len() - 1]
700 .iter()
701 .zip(&*other.release())
702 .all(|(this, other)| this == other)
703 {
704 return false;
705 }
706
707 other.as_ref() >= this
710 }
711 Operator::GreaterThan => {
712 if other.epoch() > this.epoch() {
713 return true;
714 }
715
716 if version::compare_release(&this.release(), &other.release()) == Ordering::Equal {
717 if !this.is_post() && other.is_post() {
722 return false;
723 }
724
725 if other.is_local() {
727 return false;
728 }
729 }
730
731 other.as_ref() > this
732 }
733 Operator::GreaterThanEqual => other.as_ref() >= this,
734 Operator::LessThan => {
735 if other.epoch() < this.epoch() {
736 return true;
737 }
738
739 if !this.any_prerelease()
744 && other.any_prerelease()
745 && other.as_ref() >= &this.clone().with_dev(Some(0))
746 {
747 return false;
748 }
749
750 other.as_ref() < this
751 }
752 Operator::LessThanEqual => other.as_ref() <= this,
753 }
754 }
755
756 pub fn has_lower_bound(&self) -> bool {
758 match self.operator() {
759 Operator::Equal
760 | Operator::EqualStar
761 | Operator::ExactEqual
762 | Operator::TildeEqual
763 | Operator::GreaterThan
764 | Operator::GreaterThanEqual => true,
765 Operator::LessThanEqual
766 | Operator::LessThan
767 | Operator::NotEqualStar
768 | Operator::NotEqual => false,
769 }
770 }
771}
772
773impl FromStr for VersionSpecifier {
774 type Err = VersionSpecifierParseError;
775
776 fn from_str(spec: &str) -> Result<Self, Self::Err> {
778 let mut s = unscanny::Scanner::new(spec);
779 s.eat_while(|c: char| c.is_whitespace());
780 let operator = s.eat_while(['=', '!', '~', '<', '>']);
782 if operator.is_empty() {
783 s.eat_while(|c: char| c.is_whitespace());
786 let version = s.eat_while(|c: char| !c.is_whitespace());
787 s.eat_while(|c: char| c.is_whitespace());
788 return Err(ParseErrorKind::MissingOperator(VersionOperatorBuildError {
789 version_pattern: VersionPattern::from_str(version).ok(),
790 })
791 .into());
792 }
793 let operator = Operator::from_str(operator).map_err(ParseErrorKind::InvalidOperator)?;
794 s.eat_while(|c: char| c.is_whitespace());
795 let version = s.eat_while(|c: char| !c.is_whitespace());
796 if version.is_empty() {
797 return Err(ParseErrorKind::MissingVersion.into());
798 }
799 let vpat = version.parse().map_err(ParseErrorKind::InvalidVersion)?;
800 let version_specifier =
801 Self::from_pattern(operator, vpat).map_err(ParseErrorKind::InvalidSpecifier)?;
802 s.eat_while(|c: char| c.is_whitespace());
803 if !s.done() {
804 return Err(ParseErrorKind::InvalidTrailing(s.after().to_string()).into());
805 }
806 Ok(version_specifier)
807 }
808}
809
810impl std::fmt::Display for VersionSpecifier {
811 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
812 if self.operator == Operator::EqualStar || self.operator == Operator::NotEqualStar {
813 return write!(f, "{}{}.*", self.operator, self.version);
814 }
815 write!(f, "{}{}", self.operator, self.version)
816 }
817}
818
819#[derive(Clone, Debug, Eq, PartialEq)]
821pub struct VersionSpecifierBuildError {
822 kind: Box<BuildErrorKind>,
825}
826
827impl std::error::Error for VersionSpecifierBuildError {}
828
829impl std::fmt::Display for VersionSpecifierBuildError {
830 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
831 match *self.kind {
832 BuildErrorKind::OperatorLocalCombo {
833 operator: ref op,
834 ref version,
835 } => {
836 let local = version.local();
837 write!(
838 f,
839 "Operator {op} is incompatible with versions \
840 containing non-empty local segments (`+{local}`)",
841 )
842 }
843 BuildErrorKind::OperatorWithStar { operator: ref op } => {
844 write!(
845 f,
846 "Operator {op} cannot be used with a wildcard version specifier",
847 )
848 }
849 BuildErrorKind::CompatibleRelease => {
850 write!(
851 f,
852 "The ~= operator requires at least two segments in the release version"
853 )
854 }
855 }
856 }
857}
858
859#[derive(Clone, Debug, Eq, PartialEq)]
860struct VersionOperatorBuildError {
861 version_pattern: Option<VersionPattern>,
862}
863
864impl std::error::Error for VersionOperatorBuildError {}
865
866impl std::fmt::Display for VersionOperatorBuildError {
867 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
868 write!(f, "Unexpected end of version specifier, expected operator")?;
869 if let Some(version_pattern) = &self.version_pattern {
870 let version_specifier =
871 VersionSpecifier::from_pattern(Operator::Equal, version_pattern.clone()).unwrap();
872 write!(f, ". Did you mean `{version_specifier}`?")?;
873 }
874 Ok(())
875 }
876}
877
878#[derive(Clone, Debug, Eq, PartialEq)]
881enum BuildErrorKind {
882 OperatorLocalCombo {
886 operator: Operator,
888 version: Version,
890 },
891 OperatorWithStar {
894 operator: Operator,
896 },
897 CompatibleRelease,
900}
901
902impl From<BuildErrorKind> for VersionSpecifierBuildError {
903 fn from(kind: BuildErrorKind) -> Self {
904 Self {
905 kind: Box::new(kind),
906 }
907 }
908}
909
910#[derive(Clone, Debug, Eq, PartialEq)]
912pub struct VersionSpecifierParseError {
913 kind: Box<ParseErrorKind>,
916}
917
918impl std::error::Error for VersionSpecifierParseError {}
919
920impl std::fmt::Display for VersionSpecifierParseError {
921 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
922 match *self.kind {
928 ParseErrorKind::InvalidOperator(ref err) => err.fmt(f),
929 ParseErrorKind::InvalidVersion(ref err) => err.fmt(f),
930 ParseErrorKind::InvalidSpecifier(ref err) => err.fmt(f),
931 ParseErrorKind::MissingOperator(ref err) => err.fmt(f),
932 ParseErrorKind::MissingVersion => {
933 write!(f, "Unexpected end of version specifier, expected version")
934 }
935 ParseErrorKind::InvalidTrailing(ref trail) => {
936 write!(f, "Trailing `{trail}` is not allowed")
937 }
938 }
939 }
940}
941
942#[derive(Clone, Debug, Eq, PartialEq)]
945enum ParseErrorKind {
946 InvalidOperator(OperatorParseError),
947 InvalidVersion(VersionPatternParseError),
948 InvalidSpecifier(VersionSpecifierBuildError),
949 MissingOperator(VersionOperatorBuildError),
950 MissingVersion,
951 InvalidTrailing(String),
952}
953
954impl From<ParseErrorKind> for VersionSpecifierParseError {
955 fn from(kind: ParseErrorKind) -> Self {
956 Self {
957 kind: Box::new(kind),
958 }
959 }
960}
961
962fn parse_version_specifiers(
964 spec: &str,
965 specifier_count: usize,
966) -> Result<Vec<VersionSpecifier>, VersionSpecifiersParseError> {
967 let mut version_ranges = Vec::with_capacity(specifier_count);
968 let mut start: usize = 0;
969 let separator = ",";
970 for version_range_spec in spec.split(separator) {
971 match VersionSpecifier::from_str(version_range_spec) {
972 Err(err) => {
973 return Err(VersionSpecifiersParseError {
974 inner: Box::new(VersionSpecifiersParseErrorInner {
975 err,
976 line: spec.to_string(),
977 start,
978 end: start + version_range_spec.len(),
979 }),
980 });
981 }
982 Ok(version_range) => {
983 version_ranges.push(version_range);
984 }
985 }
986 start += version_range_spec.len();
987 start += separator.len();
988 }
989 Ok(version_ranges)
990}
991
992#[derive(Clone, Debug)]
995pub struct TildeVersionSpecifier<'a> {
996 inner: Cow<'a, VersionSpecifier>,
997}
998
999impl<'a> TildeVersionSpecifier<'a> {
1000 fn from_specifier(specifier: VersionSpecifier) -> Option<Self> {
1005 TildeVersionSpecifier::new(Cow::Owned(specifier))
1006 }
1007
1008 pub fn from_specifier_ref(specifier: &'a VersionSpecifier) -> Option<Self> {
1012 TildeVersionSpecifier::new(Cow::Borrowed(specifier))
1013 }
1014
1015 fn new(specifier: Cow<'a, VersionSpecifier>) -> Option<Self> {
1016 if specifier.operator != Operator::TildeEqual {
1017 return None;
1018 }
1019 if specifier.version().release().len() < 2 || specifier.version().release().len() > 3 {
1020 return None;
1021 }
1022 if specifier.version().any_prerelease()
1023 || specifier.version().is_local()
1024 || specifier.version().is_post()
1025 {
1026 return None;
1027 }
1028 Some(Self { inner: specifier })
1029 }
1030
1031 pub fn has_patch(&self) -> bool {
1033 self.inner.version.release().len() == 3
1034 }
1035
1036 pub fn bounding_specifiers(&self) -> (VersionSpecifier, VersionSpecifier) {
1040 let release = self.inner.version().release();
1041 let lower = self.inner.version.clone();
1042 let upper = if self.has_patch() {
1043 Version::new([release[0], release[1] + 1])
1044 } else {
1045 Version::new([release[0] + 1])
1046 };
1047 (
1048 VersionSpecifier::greater_than_equal_version(lower),
1049 VersionSpecifier::less_than_version(upper),
1050 )
1051 }
1052
1053 pub fn with_patch_version(&self, patch: u64) -> TildeVersionSpecifier<'_> {
1055 let mut release = self.inner.version.release().to_vec();
1056 if self.has_patch() {
1057 release.pop();
1058 }
1059 release.push(patch);
1060 TildeVersionSpecifier::from_specifier(
1061 VersionSpecifier::from_version(Operator::TildeEqual, Version::new(release))
1062 .expect("We should always derive a valid new version specifier"),
1063 )
1064 .expect("We should always derive a new tilde version specifier")
1065 }
1066}
1067
1068impl std::fmt::Display for TildeVersionSpecifier<'_> {
1069 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1070 write!(f, "{}", self.inner)
1071 }
1072}
1073
1074#[cfg(test)]
1075mod tests {
1076 use std::{cmp::Ordering, str::FromStr};
1077
1078 use indoc::indoc;
1079
1080 use crate::LocalSegment;
1081
1082 use super::*;
1083
1084 #[test]
1086 fn test_equal() {
1087 let version = Version::from_str("1.1.post1").unwrap();
1088
1089 assert!(
1090 !VersionSpecifier::from_str("== 1.1")
1091 .unwrap()
1092 .contains(&version)
1093 );
1094 assert!(
1095 VersionSpecifier::from_str("== 1.1.post1")
1096 .unwrap()
1097 .contains(&version)
1098 );
1099 assert!(
1100 VersionSpecifier::from_str("== 1.1.*")
1101 .unwrap()
1102 .contains(&version)
1103 );
1104 }
1105
1106 #[test]
1110 fn test_less_than_post_release() {
1111 for (specifier, candidate, expected) in [
1112 ("<1.0.post1", "1.0a1", true),
1113 ("<1.0.post1", "1.0.post0.dev0", true),
1114 ("<1.0.post1", "1.0.post1.dev0", false),
1115 ("<1!1.0.post1", "1!1.0a1", true),
1116 ] {
1117 assert_eq!(
1118 VersionSpecifier::from_str(specifier)
1119 .unwrap()
1120 .contains(&Version::from_str(candidate).unwrap()),
1121 expected,
1122 "expected `{specifier}` to contain `{candidate}`: {expected}"
1123 );
1124 }
1125 }
1126
1127 const VERSIONS_ALL: &[&str] = &[
1128 "1.0.dev456",
1130 "1.0a1",
1131 "1.0a2.dev456",
1132 "1.0a12.dev456",
1133 "1.0a12",
1134 "1.0b1.dev456",
1135 "1.0b2",
1136 "1.0b2.post345.dev456",
1137 "1.0b2.post345",
1138 "1.0b2-346",
1139 "1.0c1.dev456",
1140 "1.0c1",
1141 "1.0rc2",
1142 "1.0c3",
1143 "1.0",
1144 "1.0.post456.dev34",
1145 "1.0.post456",
1146 "1.1.dev1",
1147 "1.2+123abc",
1148 "1.2+123abc456",
1149 "1.2+abc",
1150 "1.2+abc123",
1151 "1.2+abc123def",
1152 "1.2+1234.abc",
1153 "1.2+123456",
1154 "1.2.r32+123456",
1155 "1.2.rev33+123456",
1156 "1!1.0.dev456",
1158 "1!1.0a1",
1159 "1!1.0a2.dev456",
1160 "1!1.0a12.dev456",
1161 "1!1.0a12",
1162 "1!1.0b1.dev456",
1163 "1!1.0b2",
1164 "1!1.0b2.post345.dev456",
1165 "1!1.0b2.post345",
1166 "1!1.0b2-346",
1167 "1!1.0c1.dev456",
1168 "1!1.0c1",
1169 "1!1.0rc2",
1170 "1!1.0c3",
1171 "1!1.0",
1172 "1!1.0.post456.dev34",
1173 "1!1.0.post456",
1174 "1!1.1.dev1",
1175 "1!1.2+123abc",
1176 "1!1.2+123abc456",
1177 "1!1.2+abc",
1178 "1!1.2+abc123",
1179 "1!1.2+abc123def",
1180 "1!1.2+1234.abc",
1181 "1!1.2+123456",
1182 "1!1.2.r32+123456",
1183 "1!1.2.rev33+123456",
1184 ];
1185
1186 #[test]
1192 fn test_operators_true() {
1193 let versions: Vec<Version> = VERSIONS_ALL
1194 .iter()
1195 .map(|version| Version::from_str(version).unwrap())
1196 .collect();
1197
1198 let operations = [
1201 versions
1203 .iter()
1204 .enumerate()
1205 .flat_map(|(i, x)| {
1206 versions[i + 1..]
1207 .iter()
1208 .map(move |y| (x, y, Ordering::Less))
1209 })
1210 .collect::<Vec<_>>(),
1211 versions
1213 .iter()
1214 .map(move |x| (x, x, Ordering::Equal))
1215 .collect::<Vec<_>>(),
1216 versions
1218 .iter()
1219 .enumerate()
1220 .flat_map(|(i, x)| versions[..i].iter().map(move |y| (x, y, Ordering::Greater)))
1221 .collect::<Vec<_>>(),
1222 ]
1223 .into_iter()
1224 .flatten();
1225
1226 for (a, b, ordering) in operations {
1227 assert_eq!(a.cmp(b), ordering, "{a} {ordering:?} {b}");
1228 }
1229 }
1230
1231 const VERSIONS_0: &[&str] = &[
1232 "1.0.dev456",
1233 "1.0a1",
1234 "1.0a2.dev456",
1235 "1.0a12.dev456",
1236 "1.0a12",
1237 "1.0b1.dev456",
1238 "1.0b2",
1239 "1.0b2.post345.dev456",
1240 "1.0b2.post345",
1241 "1.0b2-346",
1242 "1.0c1.dev456",
1243 "1.0c1",
1244 "1.0rc2",
1245 "1.0c3",
1246 "1.0",
1247 "1.0.post456.dev34",
1248 "1.0.post456",
1249 "1.1.dev1",
1250 "1.2+123abc",
1251 "1.2+123abc456",
1252 "1.2+abc",
1253 "1.2+abc123",
1254 "1.2+abc123def",
1255 "1.2+1234.abc",
1256 "1.2+123456",
1257 "1.2.r32+123456",
1258 "1.2.rev33+123456",
1259 ];
1260
1261 const SPECIFIERS_OTHER: &[&str] = &[
1262 "== 1.*", "== 1.0.*", "== 1.1.*", "== 1.2.*", "== 2.*", "~= 1.0", "~= 1.0b1", "~= 1.1",
1263 "~= 1.2", "~= 2.0",
1264 ];
1265
1266 const EXPECTED_OTHER: &[[bool; 10]] = &[
1267 [
1268 true, true, false, false, false, false, false, false, false, false,
1269 ],
1270 [
1271 true, true, false, false, false, false, false, false, false, false,
1272 ],
1273 [
1274 true, true, false, false, false, false, false, false, false, false,
1275 ],
1276 [
1277 true, true, false, false, false, false, false, false, false, false,
1278 ],
1279 [
1280 true, true, false, false, false, false, false, false, false, false,
1281 ],
1282 [
1283 true, true, false, false, false, false, false, false, false, false,
1284 ],
1285 [
1286 true, true, false, false, false, false, true, false, false, false,
1287 ],
1288 [
1289 true, true, false, false, false, false, true, false, false, false,
1290 ],
1291 [
1292 true, true, false, false, false, false, true, false, false, false,
1293 ],
1294 [
1295 true, true, false, false, false, false, true, false, false, false,
1296 ],
1297 [
1298 true, true, false, false, false, false, true, false, false, false,
1299 ],
1300 [
1301 true, true, false, false, false, false, true, false, false, false,
1302 ],
1303 [
1304 true, true, false, false, false, false, true, false, false, false,
1305 ],
1306 [
1307 true, true, false, false, false, false, true, false, false, false,
1308 ],
1309 [
1310 true, true, false, false, false, true, true, false, false, false,
1311 ],
1312 [
1313 true, true, false, false, false, true, true, false, false, false,
1314 ],
1315 [
1316 true, true, false, false, false, true, true, false, false, false,
1317 ],
1318 [
1319 true, false, true, false, false, true, true, false, false, false,
1320 ],
1321 [
1322 true, false, false, true, false, true, true, true, true, false,
1323 ],
1324 [
1325 true, false, false, true, false, true, true, true, true, false,
1326 ],
1327 [
1328 true, false, false, true, false, true, true, true, true, false,
1329 ],
1330 [
1331 true, false, false, true, false, true, true, true, true, false,
1332 ],
1333 [
1334 true, false, false, true, false, true, true, true, true, false,
1335 ],
1336 [
1337 true, false, false, true, false, true, true, true, true, false,
1338 ],
1339 [
1340 true, false, false, true, false, true, true, true, true, false,
1341 ],
1342 [
1343 true, false, false, true, false, true, true, true, true, false,
1344 ],
1345 [
1346 true, false, false, true, false, true, true, true, true, false,
1347 ],
1348 ];
1349
1350 #[test]
1354 fn test_operators_other() {
1355 let versions = VERSIONS_0
1356 .iter()
1357 .map(|version| Version::from_str(version).unwrap());
1358 let specifiers: Vec<_> = SPECIFIERS_OTHER
1359 .iter()
1360 .map(|specifier| VersionSpecifier::from_str(specifier).unwrap())
1361 .collect();
1362
1363 for (version, expected) in versions.zip(EXPECTED_OTHER) {
1364 let actual = specifiers
1365 .iter()
1366 .map(|specifier| specifier.contains(&version));
1367 for ((actual, expected), _specifier) in actual.zip(expected).zip(SPECIFIERS_OTHER) {
1368 assert_eq!(actual, *expected);
1369 }
1370 }
1371 }
1372
1373 #[test]
1374 fn test_arbitrary_equality() {
1375 assert!(
1376 VersionSpecifier::from_str("=== 1.2a1")
1377 .unwrap()
1378 .contains(&Version::from_str("1.2a1").unwrap())
1379 );
1380 assert!(
1381 !VersionSpecifier::from_str("=== 1.2a1")
1382 .unwrap()
1383 .contains(&Version::from_str("1.2a1+local").unwrap())
1384 );
1385 }
1386
1387 #[test]
1388 fn test_equal_star_short_version_bug() {
1389 let specifier = VersionSpecifier::from_str("==2.1.*").unwrap();
1391 let version = Version::from_str("2").unwrap();
1392 assert!(
1393 !specifier.contains(&version),
1394 "Bug: version '2' incorrectly matches '==2.1.*'"
1395 );
1396
1397 let specifier = VersionSpecifier::from_str("!=2.1.*").unwrap();
1399 let version = Version::from_str("2").unwrap();
1400 assert!(
1401 specifier.contains(&version),
1402 "Bug: version '2' should match '!=2.1.*' (2.0 is not in 2.1 family)"
1403 );
1404
1405 let specifier = VersionSpecifier::from_str("==2.0.*").unwrap();
1407 let version = Version::from_str("2").unwrap();
1408 assert!(
1409 specifier.contains(&version),
1410 "version '2' should match '==2.0.*'"
1411 );
1412
1413 let specifier = VersionSpecifier::from_str("!=2.0.*").unwrap();
1415 let version = Version::from_str("2").unwrap();
1416 assert!(
1417 !specifier.contains(&version),
1418 "version '2' should not match '!=2.0.*'"
1419 );
1420
1421 let specifier = VersionSpecifier::from_str("==2.1.*").unwrap();
1424 let version = Version::from_str("2+local").unwrap();
1425 assert!(
1426 !specifier.contains(&version),
1427 "version '2+local' should not match '==2.1.*'"
1428 );
1429
1430 let specifier = VersionSpecifier::from_str("!=2.1.*").unwrap();
1432 let version = Version::from_str("2+local").unwrap();
1433 assert!(
1434 specifier.contains(&version),
1435 "version '2+local' should match '!=2.1.*'"
1436 );
1437 }
1438
1439 #[test]
1440 fn test_specifiers_true() {
1441 let pairs = [
1442 ("2.0", "==2"),
1444 ("2.0", "==2.0"),
1445 ("2.0", "==2.0.0"),
1446 ("2.0+deadbeef", "==2"),
1447 ("2.0+deadbeef", "==2.0"),
1448 ("2.0+deadbeef", "==2.0.0"),
1449 ("2.0+deadbeef", "==2+deadbeef"),
1450 ("2.0+deadbeef", "==2.0+deadbeef"),
1451 ("2.0+deadbeef", "==2.0.0+deadbeef"),
1452 ("2.0+deadbeef.0", "==2.0.0+deadbeef.00"),
1453 ("2.dev1", "==2.*"),
1455 ("2a1", "==2.*"),
1456 ("2a1.post1", "==2.*"),
1457 ("2b1", "==2.*"),
1458 ("2b1.dev1", "==2.*"),
1459 ("2c1", "==2.*"),
1460 ("2c1.post1.dev1", "==2.*"),
1461 ("2c1.post1.dev1", "==2.0.*"),
1462 ("2rc1", "==2.*"),
1463 ("2rc1", "==2.0.*"),
1464 ("2", "==2.*"),
1465 ("2", "==2.0.*"),
1466 ("2", "==0!2.*"),
1467 ("0!2", "==2.*"),
1468 ("2.0", "==2.*"),
1469 ("2.0.0", "==2.*"),
1470 ("2.1+local.version", "==2.1.*"),
1471 ("2.1", "!=2"),
1473 ("2.1", "!=2.0"),
1474 ("2.0.1", "!=2"),
1475 ("2.0.1", "!=2.0"),
1476 ("2.0.1", "!=2.0.0"),
1477 ("2.0", "!=2.0+deadbeef"),
1478 ("2.0", "!=3.*"),
1480 ("2.1", "!=2.0.*"),
1481 ("2.0", ">=2"),
1483 ("2.0", ">=2.0"),
1484 ("2.0", ">=2.0.0"),
1485 ("2.0.post1", ">=2"),
1486 ("2.0.post1.dev1", ">=2"),
1487 ("3", ">=2"),
1488 ("2.0", "<=2"),
1490 ("2.0", "<=2.0"),
1491 ("2.0", "<=2.0.0"),
1492 ("2.0.dev1", "<=2"),
1493 ("2.0a1", "<=2"),
1494 ("2.0a1.dev1", "<=2"),
1495 ("2.0b1", "<=2"),
1496 ("2.0b1.post1", "<=2"),
1497 ("2.0c1", "<=2"),
1498 ("2.0c1.post1.dev1", "<=2"),
1499 ("2.0rc1", "<=2"),
1500 ("1", "<=2"),
1501 ("3", ">2"),
1503 ("2.1", ">2.0"),
1504 ("2.0.1", ">2"),
1505 ("2.1.post1", ">2"),
1506 ("2.1+local.version", ">2"),
1507 ("2.post2", ">2.post1"),
1508 ("1", "<2"),
1510 ("2.0", "<2.1"),
1511 ("2.0.dev0", "<2.1"),
1512 ("0.1a1", "<0.1a2"),
1514 ("0.1dev1", "<0.1dev2"),
1515 ("0.1dev1", "<0.1a1"),
1516 ("1", "~=1.0"),
1518 ("1.0.1", "~=1.0"),
1519 ("1.1", "~=1.0"),
1520 ("1.9999999", "~=1.0"),
1521 ("1.1", "~=1.0a1"),
1522 ("2022.01.01", "~=2022.01.01"),
1523 ("2!1.0", "~=2!1.0"),
1525 ("2!1.0", "==2!1.*"),
1526 ("2!1.0", "==2!1.0"),
1527 ("2!1.0", "!=1.0"),
1528 ("1.0", "!=2!1.0"),
1529 ("1.0", "<=2!0.1"),
1530 ("2!1.0", ">=2.0"),
1531 ("1.0", "<2!0.1"),
1532 ("2!1.0", ">2.0"),
1533 ("2.0.5", ">2.0dev"),
1535 ];
1536
1537 for (s_version, s_spec) in pairs {
1538 let version = s_version.parse::<Version>().unwrap();
1539 let spec = s_spec.parse::<VersionSpecifier>().unwrap();
1540 assert!(
1541 spec.contains(&version),
1542 "{s_version} {s_spec}\nversion repr: {:?}\nspec version repr: {:?}",
1543 version.as_bloated_debug(),
1544 spec.version.as_bloated_debug(),
1545 );
1546 }
1547 }
1548
1549 #[test]
1550 fn test_specifier_false() {
1551 let pairs = [
1552 ("2.1", "==2"),
1554 ("2.1", "==2.0"),
1555 ("2.1", "==2.0.0"),
1556 ("2.0", "==2.0+deadbeef"),
1557 ("2.0", "==3.*"),
1559 ("2.1", "==2.0.*"),
1560 ("2.0", "!=2"),
1562 ("2.0", "!=2.0"),
1563 ("2.0", "!=2.0.0"),
1564 ("2.0+deadbeef", "!=2"),
1565 ("2.0+deadbeef", "!=2.0"),
1566 ("2.0+deadbeef", "!=2.0.0"),
1567 ("2.0+deadbeef", "!=2+deadbeef"),
1568 ("2.0+deadbeef", "!=2.0+deadbeef"),
1569 ("2.0+deadbeef", "!=2.0.0+deadbeef"),
1570 ("2.0+deadbeef.0", "!=2.0.0+deadbeef.00"),
1571 ("2.dev1", "!=2.*"),
1573 ("2a1", "!=2.*"),
1574 ("2a1.post1", "!=2.*"),
1575 ("2b1", "!=2.*"),
1576 ("2b1.dev1", "!=2.*"),
1577 ("2c1", "!=2.*"),
1578 ("2c1.post1.dev1", "!=2.*"),
1579 ("2c1.post1.dev1", "!=2.0.*"),
1580 ("2rc1", "!=2.*"),
1581 ("2rc1", "!=2.0.*"),
1582 ("2", "!=2.*"),
1583 ("2", "!=2.0.*"),
1584 ("2.0", "!=2.*"),
1585 ("2.0.0", "!=2.*"),
1586 ("2.0.dev1", ">=2"),
1588 ("2.0a1", ">=2"),
1589 ("2.0a1.dev1", ">=2"),
1590 ("2.0b1", ">=2"),
1591 ("2.0b1.post1", ">=2"),
1592 ("2.0c1", ">=2"),
1593 ("2.0c1.post1.dev1", ">=2"),
1594 ("2.0rc1", ">=2"),
1595 ("1", ">=2"),
1596 ("2.0.post1", "<=2"),
1598 ("2.0.post1.dev1", "<=2"),
1599 ("3", "<=2"),
1600 ("1", ">2"),
1602 ("2.0.dev1", ">2"),
1603 ("2.0a1", ">2"),
1604 ("2.0a1.post1", ">2"),
1605 ("2.0b1", ">2"),
1606 ("2.0b1.dev1", ">2"),
1607 ("2.0c1", ">2"),
1608 ("2.0c1.post1.dev1", ">2"),
1609 ("2.0rc1", ">2"),
1610 ("2.0", ">2"),
1611 ("2.post2", ">2"),
1612 ("2.0.post1", ">2"),
1613 ("2.0.post1.dev1", ">2"),
1614 ("2.0+local.version", ">2"),
1615 ("2.0.dev1", "<2"),
1617 ("2.0a1", "<2"),
1618 ("2.0a1.post1", "<2"),
1619 ("2.0b1", "<2"),
1620 ("2.0b2.dev1", "<2"),
1621 ("2.0c1", "<2"),
1622 ("2.0c1.post1.dev1", "<2"),
1623 ("2.0rc1", "<2"),
1624 ("2.0", "<2"),
1625 ("2.post1", "<2"),
1626 ("2.post1.dev1", "<2"),
1627 ("3", "<2"),
1628 ("2.0", "~=1.0"),
1630 ("1.1.0", "~=1.0.0"),
1631 ("1.1.post1", "~=1.0.0"),
1632 ("1.0", "~=2!1.0"),
1634 ("2!1.0", "~=1.0"),
1635 ("2!1.0", "==1.0"),
1636 ("1.0", "==2!1.0"),
1637 ("2!1.0", "==1.*"),
1638 ("1.0", "==2!1.*"),
1639 ("2!1.0", "!=2!1.0"),
1640 ];
1641 for (version, specifier) in pairs {
1642 assert!(
1643 !VersionSpecifier::from_str(specifier)
1644 .unwrap()
1645 .contains(&Version::from_str(version).unwrap()),
1646 "{version} {specifier}"
1647 );
1648 }
1649 }
1650
1651 #[test]
1652 fn test_parse_version_specifiers() {
1653 let result = VersionSpecifiers::from_str("~= 0.9, >= 1.0, != 1.3.4.*, < 2.0").unwrap();
1654 assert_eq!(
1655 result.0.as_ref(),
1656 [
1657 VersionSpecifier {
1658 operator: Operator::TildeEqual,
1659 version: Version::new([0, 9]),
1660 },
1661 VersionSpecifier {
1662 operator: Operator::GreaterThanEqual,
1663 version: Version::new([1, 0]),
1664 },
1665 VersionSpecifier {
1666 operator: Operator::NotEqualStar,
1667 version: Version::new([1, 3, 4]),
1668 },
1669 VersionSpecifier {
1670 operator: Operator::LessThan,
1671 version: Version::new([2, 0]),
1672 }
1673 ]
1674 );
1675 }
1676
1677 #[test]
1678 fn test_parse_error() {
1679 let result = VersionSpecifiers::from_str("~= 0.9, %= 1.0, != 1.3.4.*");
1680 assert_eq!(
1681 result.unwrap_err().to_string(),
1682 indoc! {r"
1683 Failed to parse version: Unexpected end of version specifier, expected operator:
1684 ~= 0.9, %= 1.0, != 1.3.4.*
1685 ^^^^^^^
1686 "}
1687 );
1688 }
1689
1690 #[test]
1691 fn test_parse_specifier_missing_operator_error() {
1692 let result = VersionSpecifiers::from_str("3.12");
1693 assert_eq!(
1694 result.unwrap_err().to_string(),
1695 indoc! {"
1696 Failed to parse version: Unexpected end of version specifier, expected operator. Did you mean `==3.12`?:
1697 3.12
1698 ^^^^
1699 "}
1700 );
1701 }
1702
1703 #[test]
1704 fn test_parse_specifier_missing_operator_invalid_version_error() {
1705 let result = VersionSpecifiers::from_str("blergh");
1706 assert_eq!(
1707 result.unwrap_err().to_string(),
1708 indoc! {r"
1709 Failed to parse version: Unexpected end of version specifier, expected operator:
1710 blergh
1711 ^^^^^^
1712 "}
1713 );
1714 }
1715
1716 #[test]
1717 fn test_non_star_after_star() {
1718 let result = VersionSpecifiers::from_str("== 0.9.*.1");
1719 assert_eq!(
1720 result.unwrap_err().inner.err,
1721 ParseErrorKind::InvalidVersion(version::PatternErrorKind::WildcardNotTrailing.into())
1722 .into(),
1723 );
1724 }
1725
1726 #[test]
1727 fn test_star_wrong_operator() {
1728 let result = VersionSpecifiers::from_str(">= 0.9.1.*");
1729 assert_eq!(
1730 result.unwrap_err().inner.err,
1731 ParseErrorKind::InvalidSpecifier(
1732 BuildErrorKind::OperatorWithStar {
1733 operator: Operator::GreaterThanEqual,
1734 }
1735 .into()
1736 )
1737 .into(),
1738 );
1739 }
1740
1741 #[test]
1742 fn test_invalid_word() {
1743 let result = VersionSpecifiers::from_str("blergh");
1744 assert_eq!(
1745 result.unwrap_err().inner.err,
1746 ParseErrorKind::MissingOperator(VersionOperatorBuildError {
1747 version_pattern: None
1748 })
1749 .into(),
1750 );
1751 }
1752
1753 #[test]
1755 fn test_invalid_specifier() {
1756 let specifiers = [
1757 (
1759 "2.0",
1760 ParseErrorKind::MissingOperator(VersionOperatorBuildError {
1761 version_pattern: VersionPattern::from_str("2.0").ok(),
1762 })
1763 .into(),
1764 ),
1765 (
1767 "=>2.0",
1768 ParseErrorKind::InvalidOperator(OperatorParseError {
1769 got: "=>".to_string(),
1770 })
1771 .into(),
1772 ),
1773 ("==", ParseErrorKind::MissingVersion.into()),
1775 (
1777 "~=1.0+5",
1778 ParseErrorKind::InvalidSpecifier(
1779 BuildErrorKind::OperatorLocalCombo {
1780 operator: Operator::TildeEqual,
1781 version: Version::new([1, 0])
1782 .with_local_segments(vec![LocalSegment::Number(5)]),
1783 }
1784 .into(),
1785 )
1786 .into(),
1787 ),
1788 (
1789 ">=1.0+deadbeef",
1790 ParseErrorKind::InvalidSpecifier(
1791 BuildErrorKind::OperatorLocalCombo {
1792 operator: Operator::GreaterThanEqual,
1793 version: Version::new([1, 0]).with_local_segments(vec![
1794 LocalSegment::String("deadbeef".to_string()),
1795 ]),
1796 }
1797 .into(),
1798 )
1799 .into(),
1800 ),
1801 (
1802 "<=1.0+abc123",
1803 ParseErrorKind::InvalidSpecifier(
1804 BuildErrorKind::OperatorLocalCombo {
1805 operator: Operator::LessThanEqual,
1806 version: Version::new([1, 0])
1807 .with_local_segments(vec![LocalSegment::String("abc123".to_string())]),
1808 }
1809 .into(),
1810 )
1811 .into(),
1812 ),
1813 (
1814 ">1.0+watwat",
1815 ParseErrorKind::InvalidSpecifier(
1816 BuildErrorKind::OperatorLocalCombo {
1817 operator: Operator::GreaterThan,
1818 version: Version::new([1, 0])
1819 .with_local_segments(vec![LocalSegment::String("watwat".to_string())]),
1820 }
1821 .into(),
1822 )
1823 .into(),
1824 ),
1825 (
1826 "<1.0+1.0",
1827 ParseErrorKind::InvalidSpecifier(
1828 BuildErrorKind::OperatorLocalCombo {
1829 operator: Operator::LessThan,
1830 version: Version::new([1, 0]).with_local_segments(vec![
1831 LocalSegment::Number(1),
1832 LocalSegment::Number(0),
1833 ]),
1834 }
1835 .into(),
1836 )
1837 .into(),
1838 ),
1839 (
1841 "~=1.0.*",
1842 ParseErrorKind::InvalidSpecifier(
1843 BuildErrorKind::OperatorWithStar {
1844 operator: Operator::TildeEqual,
1845 }
1846 .into(),
1847 )
1848 .into(),
1849 ),
1850 (
1851 ">=1.0.*",
1852 ParseErrorKind::InvalidSpecifier(
1853 BuildErrorKind::OperatorWithStar {
1854 operator: Operator::GreaterThanEqual,
1855 }
1856 .into(),
1857 )
1858 .into(),
1859 ),
1860 (
1861 "<=1.0.*",
1862 ParseErrorKind::InvalidSpecifier(
1863 BuildErrorKind::OperatorWithStar {
1864 operator: Operator::LessThanEqual,
1865 }
1866 .into(),
1867 )
1868 .into(),
1869 ),
1870 (
1871 ">1.0.*",
1872 ParseErrorKind::InvalidSpecifier(
1873 BuildErrorKind::OperatorWithStar {
1874 operator: Operator::GreaterThan,
1875 }
1876 .into(),
1877 )
1878 .into(),
1879 ),
1880 (
1881 "<1.0.*",
1882 ParseErrorKind::InvalidSpecifier(
1883 BuildErrorKind::OperatorWithStar {
1884 operator: Operator::LessThan,
1885 }
1886 .into(),
1887 )
1888 .into(),
1889 ),
1890 (
1893 "==1.0.*+5",
1894 ParseErrorKind::InvalidVersion(
1895 version::PatternErrorKind::WildcardNotTrailing.into(),
1896 )
1897 .into(),
1898 ),
1899 (
1900 "!=1.0.*+deadbeef",
1901 ParseErrorKind::InvalidVersion(
1902 version::PatternErrorKind::WildcardNotTrailing.into(),
1903 )
1904 .into(),
1905 ),
1906 (
1909 "==2.0a1.*",
1910 ParseErrorKind::InvalidVersion(
1911 version::ErrorKind::UnexpectedEnd {
1912 version: "2.0a1".to_string(),
1913 remaining: ".*".to_string(),
1914 }
1915 .into(),
1916 )
1917 .into(),
1918 ),
1919 (
1920 "!=2.0a1.*",
1921 ParseErrorKind::InvalidVersion(
1922 version::ErrorKind::UnexpectedEnd {
1923 version: "2.0a1".to_string(),
1924 remaining: ".*".to_string(),
1925 }
1926 .into(),
1927 )
1928 .into(),
1929 ),
1930 (
1931 "==2.0.post1.*",
1932 ParseErrorKind::InvalidVersion(
1933 version::ErrorKind::UnexpectedEnd {
1934 version: "2.0.post1".to_string(),
1935 remaining: ".*".to_string(),
1936 }
1937 .into(),
1938 )
1939 .into(),
1940 ),
1941 (
1942 "!=2.0.post1.*",
1943 ParseErrorKind::InvalidVersion(
1944 version::ErrorKind::UnexpectedEnd {
1945 version: "2.0.post1".to_string(),
1946 remaining: ".*".to_string(),
1947 }
1948 .into(),
1949 )
1950 .into(),
1951 ),
1952 (
1953 "==2.0.dev1.*",
1954 ParseErrorKind::InvalidVersion(
1955 version::ErrorKind::UnexpectedEnd {
1956 version: "2.0.dev1".to_string(),
1957 remaining: ".*".to_string(),
1958 }
1959 .into(),
1960 )
1961 .into(),
1962 ),
1963 (
1964 "!=2.0.dev1.*",
1965 ParseErrorKind::InvalidVersion(
1966 version::ErrorKind::UnexpectedEnd {
1967 version: "2.0.dev1".to_string(),
1968 remaining: ".*".to_string(),
1969 }
1970 .into(),
1971 )
1972 .into(),
1973 ),
1974 (
1975 "==1.0+5.*",
1976 ParseErrorKind::InvalidVersion(
1977 version::ErrorKind::LocalEmpty { precursor: '.' }.into(),
1978 )
1979 .into(),
1980 ),
1981 (
1982 "!=1.0+deadbeef.*",
1983 ParseErrorKind::InvalidVersion(
1984 version::ErrorKind::LocalEmpty { precursor: '.' }.into(),
1985 )
1986 .into(),
1987 ),
1988 (
1990 "==1.0.*.5",
1991 ParseErrorKind::InvalidVersion(
1992 version::PatternErrorKind::WildcardNotTrailing.into(),
1993 )
1994 .into(),
1995 ),
1996 (
1998 "~=1",
1999 ParseErrorKind::InvalidSpecifier(BuildErrorKind::CompatibleRelease.into()).into(),
2000 ),
2001 (
2003 "==1.0.dev1.*",
2004 ParseErrorKind::InvalidVersion(
2005 version::ErrorKind::UnexpectedEnd {
2006 version: "1.0.dev1".to_string(),
2007 remaining: ".*".to_string(),
2008 }
2009 .into(),
2010 )
2011 .into(),
2012 ),
2013 (
2014 "!=1.0.dev1.*",
2015 ParseErrorKind::InvalidVersion(
2016 version::ErrorKind::UnexpectedEnd {
2017 version: "1.0.dev1".to_string(),
2018 remaining: ".*".to_string(),
2019 }
2020 .into(),
2021 )
2022 .into(),
2023 ),
2024 ];
2025 for (specifier, error) in specifiers {
2026 assert_eq!(VersionSpecifier::from_str(specifier).unwrap_err(), error);
2027 }
2028 }
2029
2030 #[test]
2031 fn test_display_start() {
2032 assert_eq!(
2033 VersionSpecifier::from_str("== 1.1.*")
2034 .unwrap()
2035 .to_string(),
2036 "==1.1.*"
2037 );
2038 assert_eq!(
2039 VersionSpecifier::from_str("!= 1.1.*")
2040 .unwrap()
2041 .to_string(),
2042 "!=1.1.*"
2043 );
2044 }
2045
2046 #[test]
2047 fn test_version_specifiers_str() {
2048 assert_eq!(
2049 VersionSpecifiers::from_str(">= 3.7").unwrap().to_string(),
2050 ">=3.7"
2051 );
2052 assert_eq!(
2053 VersionSpecifiers::from_str(">=3.7, < 4.0, != 3.9.0")
2054 .unwrap()
2055 .to_string(),
2056 ">=3.7, !=3.9.0, <4.0"
2057 );
2058 }
2059
2060 #[test]
2061 fn test_version_specifiers_singular_interval() {
2062 let lower_then_upper = VersionSpecifiers::from_str(">=1.4.4, <=1.4.4").unwrap();
2063 let upper_then_lower = VersionSpecifiers::from_str("<=1.4.4, >=1.4.4").unwrap();
2064
2065 assert_eq!(lower_then_upper, upper_then_lower);
2066 assert_eq!(lower_then_upper.to_string(), "<=1.4.4, >=1.4.4");
2067 }
2068
2069 #[test]
2072 fn test_version_specifiers_empty() {
2073 assert_eq!(VersionSpecifiers::from_str("").unwrap().to_string(), "");
2074 }
2075
2076 #[test]
2080 fn non_ascii_version_specifier() {
2081 let s = "💩";
2082 let err = s.parse::<VersionSpecifiers>().unwrap_err();
2083 assert_eq!(err.inner.start, 0);
2084 assert_eq!(err.inner.end, 4);
2085
2086 let s = ">=3.7, <4.0,>5.%";
2090 let err = s.parse::<VersionSpecifiers>().unwrap_err();
2091 assert_eq!(err.inner.start, 12);
2092 assert_eq!(err.inner.end, 16);
2093 let s = ">=3.7,\u{3000}<4.0,>5.%";
2102 let err = s.parse::<VersionSpecifiers>().unwrap_err();
2103 assert_eq!(err.inner.start, 14);
2104 assert_eq!(err.inner.end, 18);
2105 }
2106
2107 #[test]
2110 fn error_message_version_specifiers_parse_error() {
2111 let specs = ">=1.2.3, 5.4.3, >=3.4.5";
2112 let err = VersionSpecifierParseError {
2113 kind: Box::new(ParseErrorKind::MissingOperator(VersionOperatorBuildError {
2114 version_pattern: VersionPattern::from_str("5.4.3").ok(),
2115 })),
2116 };
2117 let inner = Box::new(VersionSpecifiersParseErrorInner {
2118 err,
2119 line: specs.to_string(),
2120 start: 8,
2121 end: 14,
2122 });
2123 let err = VersionSpecifiersParseError { inner };
2124 assert_eq!(err, VersionSpecifiers::from_str(specs).unwrap_err());
2125 assert_eq!(
2126 err.to_string(),
2127 "\
2128Failed to parse version: Unexpected end of version specifier, expected operator. Did you mean `==5.4.3`?:
2129>=1.2.3, 5.4.3, >=3.4.5
2130 ^^^^^^
2131"
2132 );
2133 }
2134
2135 #[test]
2138 fn error_message_version_specifier_build_error() {
2139 let err = VersionSpecifierBuildError {
2140 kind: Box::new(BuildErrorKind::CompatibleRelease),
2141 };
2142 let op = Operator::TildeEqual;
2143 let v = Version::new([5]);
2144 let vpat = VersionPattern::verbatim(v);
2145 assert_eq!(err, VersionSpecifier::from_pattern(op, vpat).unwrap_err());
2146 assert_eq!(
2147 err.to_string(),
2148 "The ~= operator requires at least two segments in the release version"
2149 );
2150 }
2151
2152 #[test]
2155 fn error_message_version_specifier_parse_error() {
2156 let err = VersionSpecifierParseError {
2157 kind: Box::new(ParseErrorKind::InvalidSpecifier(
2158 VersionSpecifierBuildError {
2159 kind: Box::new(BuildErrorKind::CompatibleRelease),
2160 },
2161 )),
2162 };
2163 assert_eq!(err, VersionSpecifier::from_str("~=5").unwrap_err());
2164 assert_eq!(
2165 err.to_string(),
2166 "The ~= operator requires at least two segments in the release version"
2167 );
2168 }
2169
2170 #[test]
2173 fn trailing_zero_equality() {
2174 let equal = [
2175 (">=3.3", ">=3.3.0"),
2177 ("<2", "<2.0.0"),
2178 ("==1.2", "==1.2.0"),
2179 ("~=2.2.0", "~=2.2.0"),
2181 ];
2182 for (a, b) in equal {
2183 let a = VersionSpecifier::from_str(a).unwrap();
2184 let b = VersionSpecifier::from_str(b).unwrap();
2185 assert_eq!(a, b);
2186 }
2187
2188 let not_equal = [
2189 ("~=2.2", "~=2.2.0"),
2191 ("~=1.4.5", "~=1.4.5.0"),
2192 ("~=2.2.post3", "~=2.2.post5"),
2194 ("~=2.2.post3", "~=2.2.0.post3"),
2196 ];
2197 for (a, b) in not_equal {
2198 let a = VersionSpecifier::from_str(a).unwrap();
2199 let b = VersionSpecifier::from_str(b).unwrap();
2200 assert_ne!(a, b);
2201 }
2202 }
2203
2204 #[test]
2206 fn bounding_specifiers_u64_max_rejected_at_parse_time() {
2207 assert!(VersionSpecifier::from_str("~=3.18446744073709551615.0").is_err());
2208 assert!(VersionSpecifier::from_str("~=18446744073709551615.0").is_err());
2209
2210 let specifier = VersionSpecifier::from_str("~=3.18446744073709551614.0").unwrap();
2212 let tilde = TildeVersionSpecifier::from_specifier(specifier).unwrap();
2213 let (_lower, _upper) = tilde.bounding_specifiers();
2214 }
2215}