diff --git a/Cargo.toml b/Cargo.toml
index 25b7b7d..8dcb5e7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -33,7 +33,7 @@ ring = "0.17"
# PEM -> DER conversion
x509-parser = "0.15"
# Pubgrub dependency resolution algorithm
-pubgrub = "0.2"
+pubgrub = "0.3"
# Basic auth HTTP helper
http-auth-basic = "0.3"
# base16 encoding
diff --git a/src/lib.rs b/src/lib.rs
index d8ad29c..3d95d18 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -790,7 +790,8 @@ fn proto_to_retirement_reason(reason: proto::package::RetirementReason) -> Retir
fn proto_to_dep(dep: proto::package::Dependency) -> Result<(String, Dependency), ApiError> {
let app = dep.app;
let repository = dep.repository;
- let requirement = Range::new(dep.requirement);
+ let requirement = Range::new(dep.requirement.clone())
+ .map_err(|_| ApiError::InvalidVersionFormat(dep.requirement))?;
Ok((
dep.package,
Dependency {
@@ -856,10 +857,6 @@ impl Release {
pub fn is_retired(&self) -> bool {
self.retirement_status.is_some()
}
-
- fn is_pre(&self) -> bool {
- self.version.is_pre()
- }
}
#[derive(Debug, PartialEq, Eq, Clone, serde::Deserialize)]
@@ -926,26 +923,6 @@ pub struct Dependency {
pub repository: Option,
}
-impl Dependency {
- pub(crate) fn from_range(range: Range) -> Self {
- Dependency {
- app: None,
- optional: false,
- repository: None,
- requirement: range,
- }
- }
-
- pub(crate) fn from_version(version: &Version) -> Self {
- Dependency {
- app: None,
- optional: false,
- repository: None,
- requirement: Range::new(version.to_string()),
- }
- }
-}
-
static USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), " (", env!("CARGO_PKG_VERSION"), ")");
fn validate_package_and_version(package: &str, version: &str) -> Result<(), ApiError> {
diff --git a/src/tests.rs b/src/tests.rs
index fb62ede..3f89d16 100644
--- a/src/tests.rs
+++ b/src/tests.rs
@@ -1221,7 +1221,7 @@ async fn get_package_release_ok() {
(
"plug".into(),
Dependency {
- requirement: Range::new("~>0.11.0".into()),
+ requirement: Range::new("~>0.11.0".into()).unwrap(),
optional: false,
app: Some("plug".into()),
repository: None
@@ -1230,7 +1230,7 @@ async fn get_package_release_ok() {
(
"cowboy".into(),
Dependency {
- requirement: Range::new("~>1.0.0".into()),
+ requirement: Range::new("~>1.0.0".into()).unwrap(),
optional: false,
app: Some("cowboy".into()),
repository: None
diff --git a/src/version.rs b/src/version.rs
index 34de180..5912291 100644
--- a/src/version.rs
+++ b/src/version.rs
@@ -2,33 +2,19 @@
//! and compatible with the Elixir Version module, which is used by Hex
//! internally as well as be the Elixir build tool Hex client.
-use std::{
- borrow::Borrow, cell::RefCell, cmp::Ordering, collections::HashMap, convert::TryFrom,
- error::Error as StdError, fmt,
-};
-
-use crate::{Dependency, Package, Release};
+use std::{cmp::Ordering, convert::TryFrom, fmt};
use self::parser::Parser;
-use pubgrub::{
- error::PubGrubError,
- solver::{Dependencies, choose_package_with_fewest_versions},
- type_aliases::Map,
-};
use serde::{
Deserialize, Serialize,
de::{self, Deserializer},
};
-pub use pubgrub::report as pubgrub_report;
-
mod lexer;
mod parser;
#[cfg(test)]
mod tests;
-type PubgrubRange = pubgrub::range::Range;
-
/// In a nutshell, a version is represented by three numbers:
///
/// MAJOR.MINOR.PATCH
@@ -113,7 +99,7 @@ impl Version {
}
/// Parse a Hex compatible version range. i.e. `> 1 and < 2 or == 4.5.2`.
- fn parse_range(input: &str) -> Result, parser::Error> {
+ fn parse_range(input: &str) -> Result, parser::Error> {
let mut parser = Parser::new(input)?;
let version = parser.range()?;
if !parser.is_eof() {
@@ -129,6 +115,10 @@ impl Version {
Ok(version)
}
+ pub fn lowest() -> Self {
+ Self::new(0, 0, 0)
+ }
+
fn tuple(&self) -> (u32, u32, u32, PreOrder<'_>) {
(
self.major,
@@ -143,6 +133,21 @@ impl Version {
}
}
+pub trait LowestVersion {
+ fn lowest_version(&self) -> Option;
+}
+impl LowestVersion for pubgrub::Range {
+ fn lowest_version(&self) -> Option {
+ self.iter()
+ .flat_map(|(lower, _higher)| match lower {
+ std::ops::Bound::Included(v) => Some(v.clone()),
+ std::ops::Bound::Excluded(_) => None,
+ std::ops::Bound::Unbounded => Some(Version::lowest()),
+ })
+ .min()
+ }
+}
+
impl<'de> Deserialize<'de> for Version {
fn deserialize(deserializer: D) -> Result
where
@@ -174,104 +179,6 @@ impl std::cmp::Ord for Version {
}
}
-impl pubgrub::version::Version for Version {
- fn lowest() -> Self {
- Self::new(0, 0, 0)
- }
-
- fn bump(&self) -> Self {
- if self.is_pre() {
- let mut pre = self.pre.clone();
- let last_component = pre
- .last_mut()
- // This `.expect` is safe, as we know there to be at least
- // one pre-release component.
- .expect("no pre-release components");
-
- match last_component {
- Identifier::Numeric(pre) => *pre += 1,
- Identifier::AlphaNumeric(pre) => {
- let mut segments = split_alphanumeric(pre);
- let last_segment = segments.last_mut().unwrap();
-
- match last_segment {
- AlphaOrNumeric::Numeric(n) => *n += 1,
- AlphaOrNumeric::Alpha(alpha) => {
- // We should potentially be smarter about this (for instance, pick the next letter in the
- // alphabetic sequence), however, this seems like it could be quite a bit more complex.
- alpha.push('1')
- }
- }
-
- *pre = segments
- .into_iter()
- .map(|segment| match segment {
- AlphaOrNumeric::Alpha(segment) => segment,
- AlphaOrNumeric::Numeric(segment) => segment.to_string(),
- })
- .collect::>()
- .join("");
- }
- }
-
- Self {
- major: self.major,
- minor: self.minor,
- patch: self.patch,
- pre,
- build: None,
- }
- } else {
- self.bump_patch()
- }
- }
-}
-
-enum AlphaOrNumeric {
- Alpha(String),
- Numeric(u32),
-}
-
-/// Splits the given string into alphabetic and numeric segments.
-fn split_alphanumeric(str: &str) -> Vec {
- let mut segments = Vec::new();
- let mut current_segment = String::new();
- let mut previous_char_was_numeric = None;
-
- for char in str.chars() {
- let is_numeric = char.is_ascii_digit();
- match previous_char_was_numeric {
- Some(previous_char_was_numeric) if previous_char_was_numeric == is_numeric => {
- current_segment.push(char)
- }
- _ => {
- if !current_segment.is_empty() {
- if current_segment.chars().any(|char| char.is_ascii_digit()) {
- segments.push(AlphaOrNumeric::Numeric(current_segment.parse().unwrap()));
- } else {
- segments.push(AlphaOrNumeric::Alpha(current_segment));
- }
-
- current_segment = String::new();
- }
-
- current_segment.push(char);
- previous_char_was_numeric = Some(is_numeric);
- }
- }
- }
-
- if !current_segment.is_empty() {
- if current_segment.chars().any(|char| char.is_ascii_digit()) {
- segments.push(AlphaOrNumeric::Numeric(current_segment.parse().unwrap()));
- } else {
- segments.push(AlphaOrNumeric::Alpha(current_segment));
- }
- }
-
- segments
-}
-
impl<'a> TryFrom<&'a str> for Version {
type Error = parser::Error;
@@ -327,27 +234,43 @@ impl Identifier {
}
#[derive(Clone, PartialEq, Eq)]
-pub struct Range(String);
+pub struct Range {
+ spec: String,
+ range: pubgrub::Range,
+}
impl Range {
- pub fn new(spec: String) -> Self {
- Self(spec)
+ pub fn new(spec: String) -> Result {
+ let range = Version::parse_range(&spec)?;
+ Ok(Self { spec, range })
}
}
impl Range {
- pub fn to_pubgrub(&self) -> Result, parser::Error> {
- Version::parse_range(&self.0)
+ pub fn to_pubgrub(&self) -> &pubgrub::Range {
+ &self.range
}
pub fn as_str(&self) -> &str {
- &self.0
+ &self.spec
+ }
+}
+
+impl From> for Range {
+ fn from(range: pubgrub::Range) -> Self {
+ let spec = range.to_string();
+ Self { spec, range }
+ }
+}
+impl From for Range {
+ fn from(version: Version) -> Self {
+ pubgrub::Range::singleton(version).into()
}
}
impl fmt::Debug for Range {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_tuple("Range").field(&self.0.to_string()).finish()
+ f.debug_tuple("Range").field(&self.spec).finish()
}
}
@@ -357,7 +280,7 @@ impl<'de> Deserialize<'de> for Range {
D: Deserializer<'de>,
{
let s: &str = Deserialize::deserialize(deserializer)?;
- Ok(Range::new(s.to_string()))
+ Range::new(s.to_string()).map_err(serde::de::Error::custom)
}
}
@@ -372,8 +295,7 @@ impl Serialize for Range {
impl fmt::Display for Range {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{}", self.0)?;
- Ok(())
+ f.write_str(&self.spec)
}
}
@@ -408,220 +330,3 @@ impl std::cmp::Ord for PreOrder<'_> {
}
}
}
-
-pub type PackageVersions = HashMap;
-
-pub type ResolutionError = PubGrubError;
-
-pub fn resolve_versions(
- remote: Box,
- root_name: PackageName,
- dependencies: Requirements,
- locked: &HashMap,
-) -> Result
-where
- Requirements: Iterator- ,
-{
- let root_version = Version::new(0, 0, 0);
- let root = Package {
- name: root_name.clone(),
- repository: "local".to_string(),
- releases: vec![Release {
- version: root_version.clone(),
- outer_checksum: vec![],
- retirement_status: None,
- requirements: root_dependencies(dependencies, locked)?,
- meta: (),
- }],
- };
- let packages = pubgrub::solver::resolve(
- &DependencyProvider::new(remote, root, locked),
- root_name.clone(),
- root_version,
- )?
- .into_iter()
- .filter(|(name, _)| name.as_str() != root_name.as_str())
- .collect();
-
- Ok(packages)
-}
-
-fn root_dependencies(
- base_requirements: Requirements,
- locked: &HashMap,
-) -> Result, ResolutionError>
-where
- Requirements: Iterator
- ,
-{
- // Record all of the already locked versions as hard requirements
- let mut requirements: HashMap<_, _> = locked
- .iter()
- .map(|(name, version)| (name.to_string(), Dependency::from_version(version)))
- .collect();
-
- for (name, range) in base_requirements {
- match locked.get(&name) {
- // If the package was not already locked then we can use the
- // specified version requirement without modification.
- None => {
- let _ = requirements.insert(name, Dependency::from_range(range));
- }
-
- // If the version was locked we verify that the requirement is
- // compatible with the locked version.
- Some(locked_version) => {
- let compatible = range
- .to_pubgrub()
- .map_err(|e| ResolutionError::Failure(format!("Failed to parse range {}", e)))?
- .contains(locked_version);
- if !compatible {
- return Err(ResolutionError::Failure(format!(
- "{package} is specified with the requirement `{requirement}`, \
-but it is locked to {version}, which is incompatible.",
- package = name,
- requirement = range,
- version = locked_version,
- )));
- }
- }
- };
- }
-
- Ok(requirements)
- // let locked = locked
- // .iter()
- // .map(|(name, version)| (name.to_string(), Range::new(version.to_string())));
- // // Add the locked versions as new requirements that override any existing
- // // entry in the dependencies list. Collection into a HashMap is used for
- // // de-duplication.
- // let deps: HashMap<_, _> = dependencies.chain(locked).collect();
- // deps.into_iter()
- // .map(|(package, requirement)| {
- // (
- // package,
- // Dependency {
- // app: None,
- // optional: false,
- // repository: None,
- // requirement,
- // },
- // )
- // })
- // .collect()
-}
-
-pub trait PackageFetcher {
- fn get_dependencies(&self, package: &str) -> Result>;
-}
-
-struct DependencyProvider<'a> {
- packages: RefCell>,
- remote: Box,
- locked: &'a HashMap,
-}
-
-impl<'a> DependencyProvider<'a> {
- fn new(
- remote: Box,
- root: Package,
- locked: &'a HashMap,
- ) -> Self {
- let mut packages = HashMap::new();
- let _ = packages.insert(root.name.clone(), root);
- Self {
- packages: RefCell::new(packages),
- locked,
- remote,
- }
- }
-
- /// Download information about the package from the registry into the local
- /// store. Does nothing if the packages are already known.
- ///
- /// Package versions are sorted from newest to oldest, with all pre-releases
- /// at the end to ensure that a non-prerelease version will be picked first
- /// if there is one.
- //
- fn ensure_package_fetched(
- // We would like to use `&mut self` but the pubgrub library enforces
- // `&self` with interop mutability.
- &self,
- name: &str,
- ) -> Result<(), Box> {
- let mut packages = self.packages.borrow_mut();
- if packages.get(name).is_none() {
- let mut package = self.remote.get_dependencies(name)?;
- // Sort the packages from newest to oldest, pres after all others
- package.releases.sort_by(|a, b| a.version.cmp(&b.version));
- package.releases.reverse();
- let (pre, mut norm): (_, Vec<_>) =
- package.releases.into_iter().partition(Release::is_pre);
- norm.extend(pre);
- package.releases = norm;
- packages.insert(name.to_string(), package);
- }
- Ok(())
- }
-}
-
-type PackageName = String;
-
-impl pubgrub::solver::DependencyProvider for DependencyProvider<'_> {
- fn choose_package_version<
- Name: Borrow,
- Ver: Borrow>,
- >(
- &self,
- potential_packages: impl Iterator
- ,
- ) -> Result<(Name, Option), Box> {
- let potential_packages: Vec<_> = potential_packages
- .map::>, _>(|pair| {
- self.ensure_package_fetched(pair.0.borrow())?;
- Ok(pair)
- })
- .collect::>()?;
- let list_available_versions = |name: &String| {
- self.packages
- .borrow()
- .get(name)
- .cloned()
- .into_iter()
- .flat_map(|p| p.releases.into_iter())
- .map(|p| p.version)
- };
- Ok(choose_package_with_fewest_versions(
- list_available_versions,
- potential_packages.into_iter(),
- ))
- }
-
- fn get_dependencies(
- &self,
- name: &PackageName,
- version: &Version,
- ) -> Result, Box> {
- self.ensure_package_fetched(name)?;
- let packages = self.packages.borrow();
- let release = match packages
- .get(name)
- .into_iter()
- .flat_map(|p| p.releases.iter())
- .find(|r| &r.version == version)
- {
- Some(release) => release,
- None => return Ok(Dependencies::Unknown),
- };
-
- // Only use retired versions if they have been locked
- if release.is_retired() && self.locked.get(name) != Some(version) {
- return Ok(Dependencies::Unknown);
- }
-
- let mut deps: Map = Default::default();
- for (name, d) in &release.requirements {
- let range = d.requirement.to_pubgrub()?;
- deps.insert(name.clone(), range);
- }
- Ok(Dependencies::Known(deps))
- }
-}
diff --git a/src/version/parser.rs b/src/version/parser.rs
index 0d606a9..6d2752c 100644
--- a/src/version/parser.rs
+++ b/src/version/parser.rs
@@ -8,7 +8,7 @@ use super::lexer::{self, Lexer, Token};
use crate::version::{Identifier, Version};
use thiserror::Error;
-type PubgrubRange = pubgrub::range::Range;
+type PubgrubRange = pubgrub::Range;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Error)]
pub enum Error {
@@ -322,11 +322,11 @@ impl<'input> Parser<'input> {
self.skip_whitespace()?;
match self.peek() {
None => break,
- Some(Numeric(_)) => range = and(range, PubgrubRange::exact(self.version()?)),
+ Some(Numeric(_)) => range = and(range, PubgrubRange::singleton(self.version()?)),
Some(Eq) => {
self.pop()?;
- range = and(range, PubgrubRange::exact(self.version()?));
+ range = and(range, PubgrubRange::singleton(self.version()?));
}
Some(NotEq) => {
diff --git a/src/version/tests.rs b/src/version/tests.rs
index 3b10b45..414e1ed 100644
--- a/src/version/tests.rs
+++ b/src/version/tests.rs
@@ -1,12 +1,7 @@
-use std::{
- cmp::Ordering::{Equal, Greater, Less},
- collections::HashMap,
-};
+use std::cmp::Ordering::{Equal, Greater, Less};
+use std::collections::HashMap;
use parser::Error;
-use pubgrub::version::Version as _;
-
-use crate::{ApiError, Release, RetirementReason, RetirementStatus};
use super::{
Identifier::{AlphaNumeric, Numeric},
@@ -212,17 +207,21 @@ fn v_(major: u32, minor: u32, patch: u32, pre: Vec, build: Option;
+type PubgrubRange = pubgrub::Range;
-parse_range_test!(leading_space, " 1.2.3", PubgrubRange::exact(v(1, 2, 3)));
-parse_range_test!(trailing_space, "1.2.3 ", PubgrubRange::exact(v(1, 2, 3)));
+parse_range_test!(leading_space, " 1.2.3", PubgrubRange::singleton(v(1, 2, 3)));
+parse_range_test!(
+ trailing_space,
+ "1.2.3 ",
+ PubgrubRange::singleton(v(1, 2, 3))
+);
-parse_range_test!(eq_triplet, "== 1.2.3 ", PubgrubRange::exact(v(1, 2, 3)));
+parse_range_test!(eq_triplet, "== 1.2.3 ", PubgrubRange::singleton(v(1, 2, 3)));
parse_range_test!(
eq_triplet_nospace,
"==1.2.3 ",
- PubgrubRange::exact(v(1, 2, 3))
+ PubgrubRange::singleton(v(1, 2, 3))
);
parse_range_test!(
@@ -231,12 +230,12 @@ parse_range_test!(
PubgrubRange::strictly_lower_than(v(1, 2, 3)).union(&PubgrubRange::higher_than(v(1, 2, 4)))
);
-parse_range_test!(implicit_eq, "2.2.3", PubgrubRange::exact(v(2, 2, 3)));
+parse_range_test!(implicit_eq, "2.2.3", PubgrubRange::singleton(v(2, 2, 3)));
parse_range_test!(
range_pre_build,
"1.2.3-thing+oop",
- PubgrubRange::exact(v_(
+ PubgrubRange::singleton(v_(
1,
2,
3,
@@ -392,397 +391,6 @@ assert_order!(ord_pre_smaller_than_zero_flip, "1.0.0-rc1", Less, "1.0.0");
assert_order!(ord_pre_rc1_2, "1.0.0-rc1", Less, "1.0.0-rc2");
-#[test]
-fn test_pubgrub_bump_patch() {
- assert_eq!(
- Version::parse("1.0.0").unwrap().bump(),
- Version::parse("1.0.1").unwrap()
- );
-}
-
-#[test]
-fn test_pubgrub_bump_prerelease_ending_with_a_number() {
- assert_eq!(
- Version::parse("1.0.0-rc2").unwrap().bump(),
- Version::parse("1.0.0-rc3").unwrap()
- );
-}
-
-#[test]
-fn test_pubgrub_bump_prerelease_ending_with_a_letter() {
- assert_eq!(
- Version::parse("1.0.0-rc2a").unwrap().bump(),
- Version::parse("1.0.0-rc2a1").unwrap()
- );
-}
-
-struct Remote {
- deps: HashMap,
-}
-
-impl PackageFetcher for Remote {
- fn get_dependencies(&self, package: &str) -> Result> {
- self.deps
- .get(package)
- .cloned()
- .ok_or(Box::new(ApiError::NotFound))
- }
-}
-
-fn make_remote() -> Box {
- let mut deps = HashMap::new();
- deps.insert(
- "gleam_stdlib".to_string(),
- Package {
- name: "gleam_stdlib".to_string(),
- repository: "hexpm".to_string(),
- releases: vec![
- Release {
- version: Version::try_from("0.1.0").unwrap(),
- requirements: [].into(),
- retirement_status: None,
- outer_checksum: vec![1, 2, 3],
- meta: (),
- },
- Release {
- version: Version::try_from("0.2.0").unwrap(),
- requirements: [].into(),
- retirement_status: None,
- outer_checksum: vec![1, 2, 3],
- meta: (),
- },
- Release {
- version: Version::try_from("0.2.2").unwrap(),
- requirements: [].into(),
- retirement_status: None,
- outer_checksum: vec![1, 2, 3],
- meta: (),
- },
- Release {
- version: Version::try_from("0.3.0").unwrap(),
- requirements: [].into(),
- retirement_status: None,
- outer_checksum: vec![1, 2, 3],
- meta: (),
- },
- ],
- },
- );
- deps.insert(
- "gleam_otp".to_string(),
- Package {
- name: "gleam_otp".to_string(),
- repository: "hexpm".to_string(),
- releases: vec![
- Release {
- version: Version::try_from("0.1.0").unwrap(),
- requirements: [(
- "gleam_stdlib".to_string(),
- Dependency {
- app: None,
- optional: false,
- repository: None,
- requirement: Range::new(">= 0.1.0".to_string()),
- },
- )]
- .into(),
- retirement_status: None,
- outer_checksum: vec![1, 2, 3],
- meta: (),
- },
- Release {
- version: Version::try_from("0.2.0").unwrap(),
- requirements: [(
- "gleam_stdlib".to_string(),
- Dependency {
- app: None,
- optional: false,
- repository: None,
- requirement: Range::new(">= 0.1.0".to_string()),
- },
- )]
- .into(),
- retirement_status: None,
- outer_checksum: vec![1, 2, 3],
- meta: (),
- },
- Release {
- version: Version::try_from("0.3.0-rc1").unwrap(),
- requirements: [(
- "gleam_stdlib".to_string(),
- Dependency {
- app: None,
- optional: false,
- repository: None,
- requirement: Range::new(">= 0.1.0".to_string()),
- },
- )]
- .into(),
- retirement_status: None,
- outer_checksum: vec![1, 2, 3],
- meta: (),
- },
- ],
- },
- );
- deps.insert(
- "package_with_retired".to_string(),
- Package {
- name: "package_with_retired".to_string(),
- repository: "hexpm".to_string(),
- releases: vec![
- Release {
- version: Version::try_from("0.1.0").unwrap(),
- requirements: [].into(),
- retirement_status: None,
- outer_checksum: vec![1, 2, 3],
- meta: (),
- },
- Release {
- version: Version::try_from("0.2.0").unwrap(),
- requirements: [].into(),
- retirement_status: Some(RetirementStatus {
- reason: RetirementReason::Security,
- message: "It's bad".to_string(),
- }),
- outer_checksum: vec![1, 2, 3],
- meta: (),
- },
- ],
- },
- );
- Box::new(Remote { deps })
-}
-
-#[test]
-fn resolution_with_locked() {
- let locked_stdlib = ("gleam_stdlib".to_string(), Version::parse("0.1.0").unwrap());
- let result = resolve_versions(
- make_remote(),
- "app".to_string(),
- vec![("gleam_stdlib".to_string(), Range::new("~> 0.1".to_string()))].into_iter(),
- &vec![locked_stdlib].into_iter().collect(),
- )
- .unwrap();
- assert_eq!(
- result,
- vec![("gleam_stdlib".to_string(), Version::parse("0.1.0").unwrap())]
- .into_iter()
- .collect()
- );
-}
-
-#[test]
-fn resolution_without_deps() {
- let result = resolve_versions(
- make_remote(),
- "app".to_string(),
- vec![].into_iter(),
- &vec![].into_iter().collect(),
- )
- .unwrap();
- assert_eq!(result, vec![].into_iter().collect())
-}
-
-#[test]
-fn resolution_1_dep() {
- let result = resolve_versions(
- make_remote(),
- "app".to_string(),
- vec![("gleam_stdlib".to_string(), Range::new("~> 0.1".to_string()))].into_iter(),
- &vec![].into_iter().collect(),
- )
- .unwrap();
- assert_eq!(
- result,
- vec![(
- "gleam_stdlib".to_string(),
- Version::try_from("0.3.0").unwrap()
- )]
- .into_iter()
- .collect()
- );
-}
-
-#[test]
-fn resolution_with_nested_deps() {
- let result = resolve_versions(
- make_remote(),
- "app".to_string(),
- vec![("gleam_otp".to_string(), Range::new("~> 0.1".to_string()))].into_iter(),
- &vec![].into_iter().collect(),
- )
- .unwrap();
- assert_eq!(
- result,
- vec![
- ("gleam_otp".to_string(), Version::try_from("0.2.0").unwrap()),
- (
- "gleam_stdlib".to_string(),
- Version::try_from("0.3.0").unwrap()
- )
- ]
- .into_iter()
- .collect()
- );
-}
-
-#[test]
-fn resolution_locked_to_older_version() {
- let result = resolve_versions(
- make_remote(),
- "app".to_string(),
- vec![("gleam_otp".to_string(), Range::new("~> 0.1.0".to_string()))].into_iter(),
- &vec![].into_iter().collect(),
- )
- .unwrap();
- assert_eq!(
- result,
- vec![
- ("gleam_otp".to_string(), Version::try_from("0.1.0").unwrap()),
- (
- "gleam_stdlib".to_string(),
- Version::try_from("0.3.0").unwrap()
- )
- ]
- .into_iter()
- .collect()
- );
-}
-
-#[test]
-fn resolution_retired_versions_not_used_by_default() {
- let result = resolve_versions(
- make_remote(),
- "app".to_string(),
- vec![(
- "package_with_retired".to_string(),
- Range::new("> 0.0.0".to_string()),
- )]
- .into_iter(),
- &vec![].into_iter().collect(),
- )
- .unwrap();
- assert_eq!(
- result,
- vec![(
- "package_with_retired".to_string(),
- // Uses the older version that hasn't been retired
- Version::try_from("0.1.0").unwrap()
- ),]
- .into_iter()
- .collect()
- );
-}
-
-#[test]
-fn resolution_retired_versions_can_be_used_if_locked() {
- let result = resolve_versions(
- make_remote(),
- "app".to_string(),
- vec![(
- "package_with_retired".to_string(),
- Range::new("> 0.0.0".to_string()),
- )]
- .into_iter(),
- &vec![("package_with_retired".to_string(), Version::new(0, 2, 0))]
- .into_iter()
- .collect(),
- )
- .unwrap();
- assert_eq!(
- result,
- vec![(
- "package_with_retired".to_string(),
- // Uses the locked version even though it's retired
- Version::new(0, 2, 0)
- ),]
- .into_iter()
- .collect()
- );
-}
-
-#[test]
-fn resolution_prerelease_can_be_selected() {
- let result = resolve_versions(
- make_remote(),
- "app".to_string(),
- vec![(
- "gleam_otp".to_string(),
- Range::new("~> 0.3.0-rc1".to_string()),
- )]
- .into_iter(),
- &vec![].into_iter().collect(),
- )
- .unwrap();
- assert_eq!(
- result,
- vec![
- (
- "gleam_otp".to_string(),
- Version::try_from("0.3.0-rc1").unwrap()
- ),
- (
- "gleam_stdlib".to_string(),
- Version::try_from("0.3.0").unwrap()
- ),
- ]
- .into_iter()
- .collect(),
- );
-}
-
-#[test]
-fn resolution_not_found_dep() {
- resolve_versions(
- make_remote(),
- "app".to_string(),
- vec![("unknown".to_string(), Range::new("~> 0.1".to_string()))].into_iter(),
- &vec![].into_iter().collect(),
- )
- .unwrap_err();
-}
-
-#[test]
-fn resolution_no_matching_version() {
- resolve_versions(
- make_remote(),
- "app".to_string(),
- vec![(
- "gleam_stdlib".to_string(),
- Range::new("~> 99.0".to_string()),
- )]
- .into_iter(),
- &vec![].into_iter().collect(),
- )
- .unwrap_err();
-}
-
-#[test]
-fn resolution_locked_version_doesnt_satisfy_requirements() {
- let err = resolve_versions(
- make_remote(),
- "app".to_string(),
- vec![(
- "gleam_stdlib".to_string(),
- Range::new("~> 0.1.0".to_string()),
- )]
- .into_iter(),
- &vec![("gleam_stdlib".into(), Version::new(0, 2, 0))]
- .into_iter()
- .collect(),
- )
- .unwrap_err();
-
- match err {
- PubGrubError::Failure(msg) => assert_eq!(
- msg,
- "gleam_stdlib is specified with the requirement `~> 0.1.0`, but it is locked to 0.2.0, which is incompatible."
- ),
- _ => panic!("wrong error: {}", err),
- }
-}
-
#[test]
fn manifest_toml() {
let manifest = toml::to_string(
@@ -809,7 +417,7 @@ fn manifest_toml() {
),
]
.into_iter()
- .collect::(),
+ .collect::>(),
)
.unwrap();
let expected1 = r#"thingy = "0.1.0"
@@ -818,7 +426,7 @@ gleam_stdlib = "0.17.1"
let expected2 = r#"gleam_stdlib = "0.17.1"
thingy = "0.1.0"
"#;
- assert!(&manifest == expected1 || &manifest == expected2);
+ assert!(manifest == expected1 || manifest == expected2);
}
#[test]