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 !this.any_prerelease()
735 && other.any_prerelease()
736 && other.as_ref() >= &this.clone().with_dev(Some(0))
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 #[test]
1101 fn test_less_than_post_release() {
1102 for (specifier, candidate, expected) in [
1103 ("<1.0.post1", "1.0a1", true),
1104 ("<1.0.post1", "1.0.post0.dev0", true),
1105 ("<1.0.post1", "1.0.post1.dev0", false),
1106 ("<1!1.0.post1", "1!1.0a1", true),
1107 ] {
1108 assert_eq!(
1109 VersionSpecifier::from_str(specifier)
1110 .unwrap()
1111 .contains(&Version::from_str(candidate).unwrap()),
1112 expected,
1113 "expected `{specifier}` to contain `{candidate}`: {expected}"
1114 );
1115 }
1116 }
1117
1118 const VERSIONS_ALL: &[&str] = &[
1119 "1.0.dev456",
1121 "1.0a1",
1122 "1.0a2.dev456",
1123 "1.0a12.dev456",
1124 "1.0a12",
1125 "1.0b1.dev456",
1126 "1.0b2",
1127 "1.0b2.post345.dev456",
1128 "1.0b2.post345",
1129 "1.0b2-346",
1130 "1.0c1.dev456",
1131 "1.0c1",
1132 "1.0rc2",
1133 "1.0c3",
1134 "1.0",
1135 "1.0.post456.dev34",
1136 "1.0.post456",
1137 "1.1.dev1",
1138 "1.2+123abc",
1139 "1.2+123abc456",
1140 "1.2+abc",
1141 "1.2+abc123",
1142 "1.2+abc123def",
1143 "1.2+1234.abc",
1144 "1.2+123456",
1145 "1.2.r32+123456",
1146 "1.2.rev33+123456",
1147 "1!1.0.dev456",
1149 "1!1.0a1",
1150 "1!1.0a2.dev456",
1151 "1!1.0a12.dev456",
1152 "1!1.0a12",
1153 "1!1.0b1.dev456",
1154 "1!1.0b2",
1155 "1!1.0b2.post345.dev456",
1156 "1!1.0b2.post345",
1157 "1!1.0b2-346",
1158 "1!1.0c1.dev456",
1159 "1!1.0c1",
1160 "1!1.0rc2",
1161 "1!1.0c3",
1162 "1!1.0",
1163 "1!1.0.post456.dev34",
1164 "1!1.0.post456",
1165 "1!1.1.dev1",
1166 "1!1.2+123abc",
1167 "1!1.2+123abc456",
1168 "1!1.2+abc",
1169 "1!1.2+abc123",
1170 "1!1.2+abc123def",
1171 "1!1.2+1234.abc",
1172 "1!1.2+123456",
1173 "1!1.2.r32+123456",
1174 "1!1.2.rev33+123456",
1175 ];
1176
1177 #[test]
1183 fn test_operators_true() {
1184 let versions: Vec<Version> = VERSIONS_ALL
1185 .iter()
1186 .map(|version| Version::from_str(version).unwrap())
1187 .collect();
1188
1189 let operations = [
1192 versions
1194 .iter()
1195 .enumerate()
1196 .flat_map(|(i, x)| {
1197 versions[i + 1..]
1198 .iter()
1199 .map(move |y| (x, y, Ordering::Less))
1200 })
1201 .collect::<Vec<_>>(),
1202 versions
1204 .iter()
1205 .map(move |x| (x, x, Ordering::Equal))
1206 .collect::<Vec<_>>(),
1207 versions
1209 .iter()
1210 .enumerate()
1211 .flat_map(|(i, x)| versions[..i].iter().map(move |y| (x, y, Ordering::Greater)))
1212 .collect::<Vec<_>>(),
1213 ]
1214 .into_iter()
1215 .flatten();
1216
1217 for (a, b, ordering) in operations {
1218 assert_eq!(a.cmp(b), ordering, "{a} {ordering:?} {b}");
1219 }
1220 }
1221
1222 const VERSIONS_0: &[&str] = &[
1223 "1.0.dev456",
1224 "1.0a1",
1225 "1.0a2.dev456",
1226 "1.0a12.dev456",
1227 "1.0a12",
1228 "1.0b1.dev456",
1229 "1.0b2",
1230 "1.0b2.post345.dev456",
1231 "1.0b2.post345",
1232 "1.0b2-346",
1233 "1.0c1.dev456",
1234 "1.0c1",
1235 "1.0rc2",
1236 "1.0c3",
1237 "1.0",
1238 "1.0.post456.dev34",
1239 "1.0.post456",
1240 "1.1.dev1",
1241 "1.2+123abc",
1242 "1.2+123abc456",
1243 "1.2+abc",
1244 "1.2+abc123",
1245 "1.2+abc123def",
1246 "1.2+1234.abc",
1247 "1.2+123456",
1248 "1.2.r32+123456",
1249 "1.2.rev33+123456",
1250 ];
1251
1252 const SPECIFIERS_OTHER: &[&str] = &[
1253 "== 1.*", "== 1.0.*", "== 1.1.*", "== 1.2.*", "== 2.*", "~= 1.0", "~= 1.0b1", "~= 1.1",
1254 "~= 1.2", "~= 2.0",
1255 ];
1256
1257 const EXPECTED_OTHER: &[[bool; 10]] = &[
1258 [
1259 true, true, false, false, false, false, false, false, false, false,
1260 ],
1261 [
1262 true, true, false, false, false, false, false, false, false, false,
1263 ],
1264 [
1265 true, true, false, false, false, false, false, false, false, false,
1266 ],
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, true, false, false, false,
1278 ],
1279 [
1280 true, true, false, false, false, false, true, false, false, false,
1281 ],
1282 [
1283 true, true, false, false, false, false, true, 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, true, true, false, false, false,
1302 ],
1303 [
1304 true, true, false, false, false, true, true, false, false, false,
1305 ],
1306 [
1307 true, true, false, false, false, true, true, false, false, false,
1308 ],
1309 [
1310 true, false, true, false, false, true, true, false, false, 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 true, false, false, true, false, true, true, true, true, 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
1341 #[test]
1345 fn test_operators_other() {
1346 let versions = VERSIONS_0
1347 .iter()
1348 .map(|version| Version::from_str(version).unwrap());
1349 let specifiers: Vec<_> = SPECIFIERS_OTHER
1350 .iter()
1351 .map(|specifier| VersionSpecifier::from_str(specifier).unwrap())
1352 .collect();
1353
1354 for (version, expected) in versions.zip(EXPECTED_OTHER) {
1355 let actual = specifiers
1356 .iter()
1357 .map(|specifier| specifier.contains(&version));
1358 for ((actual, expected), _specifier) in actual.zip(expected).zip(SPECIFIERS_OTHER) {
1359 assert_eq!(actual, *expected);
1360 }
1361 }
1362 }
1363
1364 #[test]
1365 fn test_arbitrary_equality() {
1366 assert!(
1367 VersionSpecifier::from_str("=== 1.2a1")
1368 .unwrap()
1369 .contains(&Version::from_str("1.2a1").unwrap())
1370 );
1371 assert!(
1372 !VersionSpecifier::from_str("=== 1.2a1")
1373 .unwrap()
1374 .contains(&Version::from_str("1.2a1+local").unwrap())
1375 );
1376 }
1377
1378 #[test]
1379 fn test_equal_star_short_version_bug() {
1380 let specifier = VersionSpecifier::from_str("==2.1.*").unwrap();
1382 let version = Version::from_str("2").unwrap();
1383 assert!(
1384 !specifier.contains(&version),
1385 "Bug: version '2' incorrectly matches '==2.1.*'"
1386 );
1387
1388 let specifier = VersionSpecifier::from_str("!=2.1.*").unwrap();
1390 let version = Version::from_str("2").unwrap();
1391 assert!(
1392 specifier.contains(&version),
1393 "Bug: version '2' should match '!=2.1.*' (2.0 is not in 2.1 family)"
1394 );
1395
1396 let specifier = VersionSpecifier::from_str("==2.0.*").unwrap();
1398 let version = Version::from_str("2").unwrap();
1399 assert!(
1400 specifier.contains(&version),
1401 "version '2' should match '==2.0.*'"
1402 );
1403
1404 let specifier = VersionSpecifier::from_str("!=2.0.*").unwrap();
1406 let version = Version::from_str("2").unwrap();
1407 assert!(
1408 !specifier.contains(&version),
1409 "version '2' should not match '!=2.0.*'"
1410 );
1411
1412 let specifier = VersionSpecifier::from_str("==2.1.*").unwrap();
1415 let version = Version::from_str("2+local").unwrap();
1416 assert!(
1417 !specifier.contains(&version),
1418 "version '2+local' should not match '==2.1.*'"
1419 );
1420
1421 let specifier = VersionSpecifier::from_str("!=2.1.*").unwrap();
1423 let version = Version::from_str("2+local").unwrap();
1424 assert!(
1425 specifier.contains(&version),
1426 "version '2+local' should match '!=2.1.*'"
1427 );
1428 }
1429
1430 #[test]
1431 fn test_specifiers_true() {
1432 let pairs = [
1433 ("2.0", "==2"),
1435 ("2.0", "==2.0"),
1436 ("2.0", "==2.0.0"),
1437 ("2.0+deadbeef", "==2"),
1438 ("2.0+deadbeef", "==2.0"),
1439 ("2.0+deadbeef", "==2.0.0"),
1440 ("2.0+deadbeef", "==2+deadbeef"),
1441 ("2.0+deadbeef", "==2.0+deadbeef"),
1442 ("2.0+deadbeef", "==2.0.0+deadbeef"),
1443 ("2.0+deadbeef.0", "==2.0.0+deadbeef.00"),
1444 ("2.dev1", "==2.*"),
1446 ("2a1", "==2.*"),
1447 ("2a1.post1", "==2.*"),
1448 ("2b1", "==2.*"),
1449 ("2b1.dev1", "==2.*"),
1450 ("2c1", "==2.*"),
1451 ("2c1.post1.dev1", "==2.*"),
1452 ("2c1.post1.dev1", "==2.0.*"),
1453 ("2rc1", "==2.*"),
1454 ("2rc1", "==2.0.*"),
1455 ("2", "==2.*"),
1456 ("2", "==2.0.*"),
1457 ("2", "==0!2.*"),
1458 ("0!2", "==2.*"),
1459 ("2.0", "==2.*"),
1460 ("2.0.0", "==2.*"),
1461 ("2.1+local.version", "==2.1.*"),
1462 ("2.1", "!=2"),
1464 ("2.1", "!=2.0"),
1465 ("2.0.1", "!=2"),
1466 ("2.0.1", "!=2.0"),
1467 ("2.0.1", "!=2.0.0"),
1468 ("2.0", "!=2.0+deadbeef"),
1469 ("2.0", "!=3.*"),
1471 ("2.1", "!=2.0.*"),
1472 ("2.0", ">=2"),
1474 ("2.0", ">=2.0"),
1475 ("2.0", ">=2.0.0"),
1476 ("2.0.post1", ">=2"),
1477 ("2.0.post1.dev1", ">=2"),
1478 ("3", ">=2"),
1479 ("2.0", "<=2"),
1481 ("2.0", "<=2.0"),
1482 ("2.0", "<=2.0.0"),
1483 ("2.0.dev1", "<=2"),
1484 ("2.0a1", "<=2"),
1485 ("2.0a1.dev1", "<=2"),
1486 ("2.0b1", "<=2"),
1487 ("2.0b1.post1", "<=2"),
1488 ("2.0c1", "<=2"),
1489 ("2.0c1.post1.dev1", "<=2"),
1490 ("2.0rc1", "<=2"),
1491 ("1", "<=2"),
1492 ("3", ">2"),
1494 ("2.1", ">2.0"),
1495 ("2.0.1", ">2"),
1496 ("2.1.post1", ">2"),
1497 ("2.1+local.version", ">2"),
1498 ("2.post2", ">2.post1"),
1499 ("1", "<2"),
1501 ("2.0", "<2.1"),
1502 ("2.0.dev0", "<2.1"),
1503 ("0.1a1", "<0.1a2"),
1505 ("0.1dev1", "<0.1dev2"),
1506 ("0.1dev1", "<0.1a1"),
1507 ("1", "~=1.0"),
1509 ("1.0.1", "~=1.0"),
1510 ("1.1", "~=1.0"),
1511 ("1.9999999", "~=1.0"),
1512 ("1.1", "~=1.0a1"),
1513 ("2022.01.01", "~=2022.01.01"),
1514 ("2!1.0", "~=2!1.0"),
1516 ("2!1.0", "==2!1.*"),
1517 ("2!1.0", "==2!1.0"),
1518 ("2!1.0", "!=1.0"),
1519 ("1.0", "!=2!1.0"),
1520 ("1.0", "<=2!0.1"),
1521 ("2!1.0", ">=2.0"),
1522 ("1.0", "<2!0.1"),
1523 ("2!1.0", ">2.0"),
1524 ("2.0.5", ">2.0dev"),
1526 ];
1527
1528 for (s_version, s_spec) in pairs {
1529 let version = s_version.parse::<Version>().unwrap();
1530 let spec = s_spec.parse::<VersionSpecifier>().unwrap();
1531 assert!(
1532 spec.contains(&version),
1533 "{s_version} {s_spec}\nversion repr: {:?}\nspec version repr: {:?}",
1534 version.as_bloated_debug(),
1535 spec.version.as_bloated_debug(),
1536 );
1537 }
1538 }
1539
1540 #[test]
1541 fn test_specifier_false() {
1542 let pairs = [
1543 ("2.1", "==2"),
1545 ("2.1", "==2.0"),
1546 ("2.1", "==2.0.0"),
1547 ("2.0", "==2.0+deadbeef"),
1548 ("2.0", "==3.*"),
1550 ("2.1", "==2.0.*"),
1551 ("2.0", "!=2"),
1553 ("2.0", "!=2.0"),
1554 ("2.0", "!=2.0.0"),
1555 ("2.0+deadbeef", "!=2"),
1556 ("2.0+deadbeef", "!=2.0"),
1557 ("2.0+deadbeef", "!=2.0.0"),
1558 ("2.0+deadbeef", "!=2+deadbeef"),
1559 ("2.0+deadbeef", "!=2.0+deadbeef"),
1560 ("2.0+deadbeef", "!=2.0.0+deadbeef"),
1561 ("2.0+deadbeef.0", "!=2.0.0+deadbeef.00"),
1562 ("2.dev1", "!=2.*"),
1564 ("2a1", "!=2.*"),
1565 ("2a1.post1", "!=2.*"),
1566 ("2b1", "!=2.*"),
1567 ("2b1.dev1", "!=2.*"),
1568 ("2c1", "!=2.*"),
1569 ("2c1.post1.dev1", "!=2.*"),
1570 ("2c1.post1.dev1", "!=2.0.*"),
1571 ("2rc1", "!=2.*"),
1572 ("2rc1", "!=2.0.*"),
1573 ("2", "!=2.*"),
1574 ("2", "!=2.0.*"),
1575 ("2.0", "!=2.*"),
1576 ("2.0.0", "!=2.*"),
1577 ("2.0.dev1", ">=2"),
1579 ("2.0a1", ">=2"),
1580 ("2.0a1.dev1", ">=2"),
1581 ("2.0b1", ">=2"),
1582 ("2.0b1.post1", ">=2"),
1583 ("2.0c1", ">=2"),
1584 ("2.0c1.post1.dev1", ">=2"),
1585 ("2.0rc1", ">=2"),
1586 ("1", ">=2"),
1587 ("2.0.post1", "<=2"),
1589 ("2.0.post1.dev1", "<=2"),
1590 ("3", "<=2"),
1591 ("1", ">2"),
1593 ("2.0.dev1", ">2"),
1594 ("2.0a1", ">2"),
1595 ("2.0a1.post1", ">2"),
1596 ("2.0b1", ">2"),
1597 ("2.0b1.dev1", ">2"),
1598 ("2.0c1", ">2"),
1599 ("2.0c1.post1.dev1", ">2"),
1600 ("2.0rc1", ">2"),
1601 ("2.0", ">2"),
1602 ("2.post2", ">2"),
1603 ("2.0.post1", ">2"),
1604 ("2.0.post1.dev1", ">2"),
1605 ("2.0+local.version", ">2"),
1606 ("2.0.dev1", "<2"),
1608 ("2.0a1", "<2"),
1609 ("2.0a1.post1", "<2"),
1610 ("2.0b1", "<2"),
1611 ("2.0b2.dev1", "<2"),
1612 ("2.0c1", "<2"),
1613 ("2.0c1.post1.dev1", "<2"),
1614 ("2.0rc1", "<2"),
1615 ("2.0", "<2"),
1616 ("2.post1", "<2"),
1617 ("2.post1.dev1", "<2"),
1618 ("3", "<2"),
1619 ("2.0", "~=1.0"),
1621 ("1.1.0", "~=1.0.0"),
1622 ("1.1.post1", "~=1.0.0"),
1623 ("1.0", "~=2!1.0"),
1625 ("2!1.0", "~=1.0"),
1626 ("2!1.0", "==1.0"),
1627 ("1.0", "==2!1.0"),
1628 ("2!1.0", "==1.*"),
1629 ("1.0", "==2!1.*"),
1630 ("2!1.0", "!=2!1.0"),
1631 ];
1632 for (version, specifier) in pairs {
1633 assert!(
1634 !VersionSpecifier::from_str(specifier)
1635 .unwrap()
1636 .contains(&Version::from_str(version).unwrap()),
1637 "{version} {specifier}"
1638 );
1639 }
1640 }
1641
1642 #[test]
1643 fn test_parse_version_specifiers() {
1644 let result = VersionSpecifiers::from_str("~= 0.9, >= 1.0, != 1.3.4.*, < 2.0").unwrap();
1645 assert_eq!(
1646 result.0.as_ref(),
1647 [
1648 VersionSpecifier {
1649 operator: Operator::TildeEqual,
1650 version: Version::new([0, 9]),
1651 },
1652 VersionSpecifier {
1653 operator: Operator::GreaterThanEqual,
1654 version: Version::new([1, 0]),
1655 },
1656 VersionSpecifier {
1657 operator: Operator::NotEqualStar,
1658 version: Version::new([1, 3, 4]),
1659 },
1660 VersionSpecifier {
1661 operator: Operator::LessThan,
1662 version: Version::new([2, 0]),
1663 }
1664 ]
1665 );
1666 }
1667
1668 #[test]
1669 fn test_parse_error() {
1670 let result = VersionSpecifiers::from_str("~= 0.9, %= 1.0, != 1.3.4.*");
1671 assert_eq!(
1672 result.unwrap_err().to_string(),
1673 indoc! {r"
1674 Failed to parse version: Unexpected end of version specifier, expected operator:
1675 ~= 0.9, %= 1.0, != 1.3.4.*
1676 ^^^^^^^
1677 "}
1678 );
1679 }
1680
1681 #[test]
1682 fn test_parse_specifier_missing_operator_error() {
1683 let result = VersionSpecifiers::from_str("3.12");
1684 assert_eq!(
1685 result.unwrap_err().to_string(),
1686 indoc! {"
1687 Failed to parse version: Unexpected end of version specifier, expected operator. Did you mean `==3.12`?:
1688 3.12
1689 ^^^^
1690 "}
1691 );
1692 }
1693
1694 #[test]
1695 fn test_parse_specifier_missing_operator_invalid_version_error() {
1696 let result = VersionSpecifiers::from_str("blergh");
1697 assert_eq!(
1698 result.unwrap_err().to_string(),
1699 indoc! {r"
1700 Failed to parse version: Unexpected end of version specifier, expected operator:
1701 blergh
1702 ^^^^^^
1703 "}
1704 );
1705 }
1706
1707 #[test]
1708 fn test_non_star_after_star() {
1709 let result = VersionSpecifiers::from_str("== 0.9.*.1");
1710 assert_eq!(
1711 result.unwrap_err().inner.err,
1712 ParseErrorKind::InvalidVersion(version::PatternErrorKind::WildcardNotTrailing.into())
1713 .into(),
1714 );
1715 }
1716
1717 #[test]
1718 fn test_star_wrong_operator() {
1719 let result = VersionSpecifiers::from_str(">= 0.9.1.*");
1720 assert_eq!(
1721 result.unwrap_err().inner.err,
1722 ParseErrorKind::InvalidSpecifier(
1723 BuildErrorKind::OperatorWithStar {
1724 operator: Operator::GreaterThanEqual,
1725 }
1726 .into()
1727 )
1728 .into(),
1729 );
1730 }
1731
1732 #[test]
1733 fn test_invalid_word() {
1734 let result = VersionSpecifiers::from_str("blergh");
1735 assert_eq!(
1736 result.unwrap_err().inner.err,
1737 ParseErrorKind::MissingOperator(VersionOperatorBuildError {
1738 version_pattern: None
1739 })
1740 .into(),
1741 );
1742 }
1743
1744 #[test]
1746 fn test_invalid_specifier() {
1747 let specifiers = [
1748 (
1750 "2.0",
1751 ParseErrorKind::MissingOperator(VersionOperatorBuildError {
1752 version_pattern: VersionPattern::from_str("2.0").ok(),
1753 })
1754 .into(),
1755 ),
1756 (
1758 "=>2.0",
1759 ParseErrorKind::InvalidOperator(OperatorParseError {
1760 got: "=>".to_string(),
1761 })
1762 .into(),
1763 ),
1764 ("==", ParseErrorKind::MissingVersion.into()),
1766 (
1768 "~=1.0+5",
1769 ParseErrorKind::InvalidSpecifier(
1770 BuildErrorKind::OperatorLocalCombo {
1771 operator: Operator::TildeEqual,
1772 version: Version::new([1, 0])
1773 .with_local_segments(vec![LocalSegment::Number(5)]),
1774 }
1775 .into(),
1776 )
1777 .into(),
1778 ),
1779 (
1780 ">=1.0+deadbeef",
1781 ParseErrorKind::InvalidSpecifier(
1782 BuildErrorKind::OperatorLocalCombo {
1783 operator: Operator::GreaterThanEqual,
1784 version: Version::new([1, 0]).with_local_segments(vec![
1785 LocalSegment::String("deadbeef".to_string()),
1786 ]),
1787 }
1788 .into(),
1789 )
1790 .into(),
1791 ),
1792 (
1793 "<=1.0+abc123",
1794 ParseErrorKind::InvalidSpecifier(
1795 BuildErrorKind::OperatorLocalCombo {
1796 operator: Operator::LessThanEqual,
1797 version: Version::new([1, 0])
1798 .with_local_segments(vec![LocalSegment::String("abc123".to_string())]),
1799 }
1800 .into(),
1801 )
1802 .into(),
1803 ),
1804 (
1805 ">1.0+watwat",
1806 ParseErrorKind::InvalidSpecifier(
1807 BuildErrorKind::OperatorLocalCombo {
1808 operator: Operator::GreaterThan,
1809 version: Version::new([1, 0])
1810 .with_local_segments(vec![LocalSegment::String("watwat".to_string())]),
1811 }
1812 .into(),
1813 )
1814 .into(),
1815 ),
1816 (
1817 "<1.0+1.0",
1818 ParseErrorKind::InvalidSpecifier(
1819 BuildErrorKind::OperatorLocalCombo {
1820 operator: Operator::LessThan,
1821 version: Version::new([1, 0]).with_local_segments(vec![
1822 LocalSegment::Number(1),
1823 LocalSegment::Number(0),
1824 ]),
1825 }
1826 .into(),
1827 )
1828 .into(),
1829 ),
1830 (
1832 "~=1.0.*",
1833 ParseErrorKind::InvalidSpecifier(
1834 BuildErrorKind::OperatorWithStar {
1835 operator: Operator::TildeEqual,
1836 }
1837 .into(),
1838 )
1839 .into(),
1840 ),
1841 (
1842 ">=1.0.*",
1843 ParseErrorKind::InvalidSpecifier(
1844 BuildErrorKind::OperatorWithStar {
1845 operator: Operator::GreaterThanEqual,
1846 }
1847 .into(),
1848 )
1849 .into(),
1850 ),
1851 (
1852 "<=1.0.*",
1853 ParseErrorKind::InvalidSpecifier(
1854 BuildErrorKind::OperatorWithStar {
1855 operator: Operator::LessThanEqual,
1856 }
1857 .into(),
1858 )
1859 .into(),
1860 ),
1861 (
1862 ">1.0.*",
1863 ParseErrorKind::InvalidSpecifier(
1864 BuildErrorKind::OperatorWithStar {
1865 operator: Operator::GreaterThan,
1866 }
1867 .into(),
1868 )
1869 .into(),
1870 ),
1871 (
1872 "<1.0.*",
1873 ParseErrorKind::InvalidSpecifier(
1874 BuildErrorKind::OperatorWithStar {
1875 operator: Operator::LessThan,
1876 }
1877 .into(),
1878 )
1879 .into(),
1880 ),
1881 (
1884 "==1.0.*+5",
1885 ParseErrorKind::InvalidVersion(
1886 version::PatternErrorKind::WildcardNotTrailing.into(),
1887 )
1888 .into(),
1889 ),
1890 (
1891 "!=1.0.*+deadbeef",
1892 ParseErrorKind::InvalidVersion(
1893 version::PatternErrorKind::WildcardNotTrailing.into(),
1894 )
1895 .into(),
1896 ),
1897 (
1900 "==2.0a1.*",
1901 ParseErrorKind::InvalidVersion(
1902 version::ErrorKind::UnexpectedEnd {
1903 version: "2.0a1".to_string(),
1904 remaining: ".*".to_string(),
1905 }
1906 .into(),
1907 )
1908 .into(),
1909 ),
1910 (
1911 "!=2.0a1.*",
1912 ParseErrorKind::InvalidVersion(
1913 version::ErrorKind::UnexpectedEnd {
1914 version: "2.0a1".to_string(),
1915 remaining: ".*".to_string(),
1916 }
1917 .into(),
1918 )
1919 .into(),
1920 ),
1921 (
1922 "==2.0.post1.*",
1923 ParseErrorKind::InvalidVersion(
1924 version::ErrorKind::UnexpectedEnd {
1925 version: "2.0.post1".to_string(),
1926 remaining: ".*".to_string(),
1927 }
1928 .into(),
1929 )
1930 .into(),
1931 ),
1932 (
1933 "!=2.0.post1.*",
1934 ParseErrorKind::InvalidVersion(
1935 version::ErrorKind::UnexpectedEnd {
1936 version: "2.0.post1".to_string(),
1937 remaining: ".*".to_string(),
1938 }
1939 .into(),
1940 )
1941 .into(),
1942 ),
1943 (
1944 "==2.0.dev1.*",
1945 ParseErrorKind::InvalidVersion(
1946 version::ErrorKind::UnexpectedEnd {
1947 version: "2.0.dev1".to_string(),
1948 remaining: ".*".to_string(),
1949 }
1950 .into(),
1951 )
1952 .into(),
1953 ),
1954 (
1955 "!=2.0.dev1.*",
1956 ParseErrorKind::InvalidVersion(
1957 version::ErrorKind::UnexpectedEnd {
1958 version: "2.0.dev1".to_string(),
1959 remaining: ".*".to_string(),
1960 }
1961 .into(),
1962 )
1963 .into(),
1964 ),
1965 (
1966 "==1.0+5.*",
1967 ParseErrorKind::InvalidVersion(
1968 version::ErrorKind::LocalEmpty { precursor: '.' }.into(),
1969 )
1970 .into(),
1971 ),
1972 (
1973 "!=1.0+deadbeef.*",
1974 ParseErrorKind::InvalidVersion(
1975 version::ErrorKind::LocalEmpty { precursor: '.' }.into(),
1976 )
1977 .into(),
1978 ),
1979 (
1981 "==1.0.*.5",
1982 ParseErrorKind::InvalidVersion(
1983 version::PatternErrorKind::WildcardNotTrailing.into(),
1984 )
1985 .into(),
1986 ),
1987 (
1989 "~=1",
1990 ParseErrorKind::InvalidSpecifier(BuildErrorKind::CompatibleRelease.into()).into(),
1991 ),
1992 (
1994 "==1.0.dev1.*",
1995 ParseErrorKind::InvalidVersion(
1996 version::ErrorKind::UnexpectedEnd {
1997 version: "1.0.dev1".to_string(),
1998 remaining: ".*".to_string(),
1999 }
2000 .into(),
2001 )
2002 .into(),
2003 ),
2004 (
2005 "!=1.0.dev1.*",
2006 ParseErrorKind::InvalidVersion(
2007 version::ErrorKind::UnexpectedEnd {
2008 version: "1.0.dev1".to_string(),
2009 remaining: ".*".to_string(),
2010 }
2011 .into(),
2012 )
2013 .into(),
2014 ),
2015 ];
2016 for (specifier, error) in specifiers {
2017 assert_eq!(VersionSpecifier::from_str(specifier).unwrap_err(), error);
2018 }
2019 }
2020
2021 #[test]
2022 fn test_display_start() {
2023 assert_eq!(
2024 VersionSpecifier::from_str("== 1.1.*")
2025 .unwrap()
2026 .to_string(),
2027 "==1.1.*"
2028 );
2029 assert_eq!(
2030 VersionSpecifier::from_str("!= 1.1.*")
2031 .unwrap()
2032 .to_string(),
2033 "!=1.1.*"
2034 );
2035 }
2036
2037 #[test]
2038 fn test_version_specifiers_str() {
2039 assert_eq!(
2040 VersionSpecifiers::from_str(">= 3.7").unwrap().to_string(),
2041 ">=3.7"
2042 );
2043 assert_eq!(
2044 VersionSpecifiers::from_str(">=3.7, < 4.0, != 3.9.0")
2045 .unwrap()
2046 .to_string(),
2047 ">=3.7, !=3.9.0, <4.0"
2048 );
2049 }
2050
2051 #[test]
2052 fn test_version_specifiers_singular_interval() {
2053 let lower_then_upper = VersionSpecifiers::from_str(">=1.4.4, <=1.4.4").unwrap();
2054 let upper_then_lower = VersionSpecifiers::from_str("<=1.4.4, >=1.4.4").unwrap();
2055
2056 assert_eq!(lower_then_upper, upper_then_lower);
2057 assert_eq!(lower_then_upper.to_string(), "<=1.4.4, >=1.4.4");
2058 }
2059
2060 #[test]
2063 fn test_version_specifiers_empty() {
2064 assert_eq!(VersionSpecifiers::from_str("").unwrap().to_string(), "");
2065 }
2066
2067 #[test]
2071 fn non_ascii_version_specifier() {
2072 let s = "💩";
2073 let err = s.parse::<VersionSpecifiers>().unwrap_err();
2074 assert_eq!(err.inner.start, 0);
2075 assert_eq!(err.inner.end, 4);
2076
2077 let s = ">=3.7, <4.0,>5.%";
2081 let err = s.parse::<VersionSpecifiers>().unwrap_err();
2082 assert_eq!(err.inner.start, 12);
2083 assert_eq!(err.inner.end, 16);
2084 let s = ">=3.7,\u{3000}<4.0,>5.%";
2093 let err = s.parse::<VersionSpecifiers>().unwrap_err();
2094 assert_eq!(err.inner.start, 14);
2095 assert_eq!(err.inner.end, 18);
2096 }
2097
2098 #[test]
2101 fn error_message_version_specifiers_parse_error() {
2102 let specs = ">=1.2.3, 5.4.3, >=3.4.5";
2103 let err = VersionSpecifierParseError {
2104 kind: Box::new(ParseErrorKind::MissingOperator(VersionOperatorBuildError {
2105 version_pattern: VersionPattern::from_str("5.4.3").ok(),
2106 })),
2107 };
2108 let inner = Box::new(VersionSpecifiersParseErrorInner {
2109 err,
2110 line: specs.to_string(),
2111 start: 8,
2112 end: 14,
2113 });
2114 let err = VersionSpecifiersParseError { inner };
2115 assert_eq!(err, VersionSpecifiers::from_str(specs).unwrap_err());
2116 assert_eq!(
2117 err.to_string(),
2118 "\
2119Failed to parse version: Unexpected end of version specifier, expected operator. Did you mean `==5.4.3`?:
2120>=1.2.3, 5.4.3, >=3.4.5
2121 ^^^^^^
2122"
2123 );
2124 }
2125
2126 #[test]
2129 fn error_message_version_specifier_build_error() {
2130 let err = VersionSpecifierBuildError {
2131 kind: Box::new(BuildErrorKind::CompatibleRelease),
2132 };
2133 let op = Operator::TildeEqual;
2134 let v = Version::new([5]);
2135 let vpat = VersionPattern::verbatim(v);
2136 assert_eq!(err, VersionSpecifier::from_pattern(op, vpat).unwrap_err());
2137 assert_eq!(
2138 err.to_string(),
2139 "The ~= operator requires at least two segments in the release version"
2140 );
2141 }
2142
2143 #[test]
2146 fn error_message_version_specifier_parse_error() {
2147 let err = VersionSpecifierParseError {
2148 kind: Box::new(ParseErrorKind::InvalidSpecifier(
2149 VersionSpecifierBuildError {
2150 kind: Box::new(BuildErrorKind::CompatibleRelease),
2151 },
2152 )),
2153 };
2154 assert_eq!(err, VersionSpecifier::from_str("~=5").unwrap_err());
2155 assert_eq!(
2156 err.to_string(),
2157 "The ~= operator requires at least two segments in the release version"
2158 );
2159 }
2160
2161 #[test]
2164 fn trailing_zero_equality() {
2165 let equal = [
2166 (">=3.3", ">=3.3.0"),
2168 ("<2", "<2.0.0"),
2169 ("==1.2", "==1.2.0"),
2170 ("~=2.2.0", "~=2.2.0"),
2172 ];
2173 for (a, b) in equal {
2174 let a = VersionSpecifier::from_str(a).unwrap();
2175 let b = VersionSpecifier::from_str(b).unwrap();
2176 assert_eq!(a, b);
2177 }
2178
2179 let not_equal = [
2180 ("~=2.2", "~=2.2.0"),
2182 ("~=1.4.5", "~=1.4.5.0"),
2183 ("~=2.2.post3", "~=2.2.post5"),
2185 ("~=2.2.post3", "~=2.2.0.post3"),
2187 ];
2188 for (a, b) in not_equal {
2189 let a = VersionSpecifier::from_str(a).unwrap();
2190 let b = VersionSpecifier::from_str(b).unwrap();
2191 assert_ne!(a, b);
2192 }
2193 }
2194
2195 #[test]
2197 fn bounding_specifiers_u64_max_rejected_at_parse_time() {
2198 assert!(VersionSpecifier::from_str("~=3.18446744073709551615.0").is_err());
2199 assert!(VersionSpecifier::from_str("~=18446744073709551615.0").is_err());
2200
2201 let specifier = VersionSpecifier::from_str("~=3.18446744073709551614.0").unwrap();
2203 let tilde = TildeVersionSpecifier::from_specifier(specifier).unwrap();
2204 let (_lower, _upper) = tilde.bounding_specifiers();
2205 }
2206}