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