Skip to content

Commit e93cce3

Browse files
committed
Support alternative registries.
cargo-add gains a new registry flag. cargo-upgrade gets any alternative registry URLs from cargo metadata.
1 parent ca3462e commit e93cce3

13 files changed

+832
-475
lines changed

Cargo.lock

+417-403
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ git2 = "0.10.1"
6969
hex = "0.3.2"
7070
url = "2.1.0"
7171
toml = "0.5.1"
72+
itertools = "0.8.0"
7273

7374
[dependencies.semver]
7475
features = ["serde"]

src/bin/add/args.rs

+20-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Handle `cargo add` arguments
22
3-
use cargo_edit::{find, Dependency};
3+
use cargo_edit::{find, registry_url, Dependency};
44
use cargo_edit::{get_latest_dependency, CrateName};
55
use semver;
66
use std::path::PathBuf;
@@ -104,6 +104,10 @@ pub struct Args {
104104
/// Keep dependencies sorted
105105
#[structopt(long = "sort", short = "s")]
106106
pub sort: bool,
107+
108+
/// Registry to use
109+
#[structopt(long = "registry", conflicts_with = "git", conflicts_with = "path")]
110+
pub registry: Option<String>,
107111
}
108112

109113
fn parse_version_req(s: &str) -> Result<&str> {
@@ -153,6 +157,8 @@ impl Args {
153157
} else {
154158
assert_eq!(self.git.is_some() && self.vers.is_some(), false);
155159
assert_eq!(self.git.is_some() && self.path.is_some(), false);
160+
assert_eq!(self.git.is_some() && self.registry.is_some(), false);
161+
assert_eq!(self.path.is_some() && self.registry.is_some(), false);
156162

157163
let mut dependency = Dependency::new(crate_name.name());
158164

@@ -165,12 +171,18 @@ impl Args {
165171
if let Some(version) = &self.vers {
166172
dependency = dependency.set_version(parse_version_req(version)?);
167173
}
174+
let registry_url = if let Some(registry) = &self.registry {
175+
Some(registry_url(&find(&self.manifest_path)?, Some(registry))?)
176+
} else {
177+
None
178+
};
168179

169180
if self.git.is_none() && self.path.is_none() && self.vers.is_none() {
170181
let dep = get_latest_dependency(
171182
crate_name.name(),
172183
self.allow_prerelease,
173184
&find(&self.manifest_path)?,
185+
&registry_url,
174186
)?;
175187
let v = format!(
176188
"{prefix}{version}",
@@ -182,6 +194,12 @@ impl Args {
182194
dependency = dep.set_version(&v);
183195
}
184196

197+
// Set the registry after getting the latest version as
198+
// get_latest_dependency returns a registry-less Dependency
199+
if let Some(registry) = &self.registry {
200+
dependency = dependency.set_registry(registry);
201+
}
202+
185203
Ok(dependency)
186204
}
187205
}
@@ -236,6 +254,7 @@ impl Default for Args {
236254
quiet: false,
237255
offline: true,
238256
sort: false,
257+
registry: None,
239258
}
240259
}
241260
}

src/bin/add/main.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@
1515
extern crate error_chain;
1616

1717
use crate::args::{Args, Command};
18-
use cargo_edit::{find, update_registry_index, Dependency, Manifest};
18+
use cargo_edit::{find, registry_url, update_registry_index, Dependency, Manifest};
1919
use std::io::Write;
2020
use std::process;
2121
use structopt::StructOpt;
22-
use toml_edit::Item as TomlItem;
2322
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
23+
use toml_edit::Item as TomlItem;
2424

2525
mod args;
2626

@@ -83,7 +83,11 @@ fn handle_add(args: &Args) -> Result<()> {
8383
let deps = &args.parse_dependencies()?;
8484

8585
if !args.offline {
86-
update_registry_index(&find(manifest_path)?)?;
86+
let url = registry_url(
87+
&find(&manifest_path)?,
88+
args.registry.as_ref().map(String::as_ref),
89+
)?;
90+
update_registry_index(&url)?;
8791
}
8892

8993
deps.iter()

src/bin/upgrade/main.rs

+67-30
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,18 @@ extern crate error_chain;
1616

1717
use crate::errors::*;
1818
use cargo_edit::{
19-
find, get_latest_dependency, update_registry_index, CrateName, Dependency, LocalManifest,
19+
find, get_latest_dependency, registry_url, update_registry_index, CrateName, Dependency,
20+
LocalManifest,
2021
};
2122
use failure::Fail;
23+
use itertools::Itertools;
2224
use std::collections::HashMap;
2325
use std::io::Write;
2426
use std::path::{Path, PathBuf};
2527
use std::process;
2628
use structopt::StructOpt;
2729
use termcolor::{BufferWriter, Color, ColorChoice, ColorSpec, WriteColor};
30+
use url::Url;
2831

2932
mod errors {
3033
error_chain! {
@@ -153,37 +156,49 @@ impl Manifests {
153156
}
154157
}
155158

156-
Ok(DesiredUpgrades(if only_update.is_empty() {
157-
// User hasn't asked for any specific dependencies to be upgraded, so upgrade all the
158-
// dependencies.
159+
// Map the names of user-specified dependencies to the (optionally) requested version.
160+
let selected_dependencies = only_update
161+
.into_iter()
162+
.map(|name| {
163+
if let Some(dependency) = CrateName::new(&name.clone()).parse_as_version()? {
164+
Ok((
165+
dependency.name.clone(),
166+
dependency.version().map(String::from),
167+
))
168+
} else {
169+
Ok((name, None))
170+
}
171+
})
172+
.collect::<Result<HashMap<_, _>>>()?;
173+
174+
Ok(DesiredUpgrades(
159175
self.0
160176
.iter()
161177
.flat_map(|&(_, ref package)| package.dependencies.clone())
162178
.filter(is_version_dep)
163-
.map(|dependency| {
164-
let mut dep = Dependency::new(&dependency.name);
165-
if let Some(rename) = dependency.rename {
166-
dep = dep.set_rename(&rename);
167-
}
168-
(dep, None)
169-
})
170-
.collect()
171-
} else {
172-
only_update
173-
.into_iter()
174-
.map(|name| {
175-
if let Some(dependency) = CrateName::new(&name.clone()).parse_as_version()? {
176-
Ok((
177-
dependency.name.clone(),
178-
dependency.version().map(String::from),
179-
))
179+
.filter_map(|dependency| {
180+
if selected_dependencies.is_empty() {
181+
// User hasn't asked for any specific dependencies to be upgraded,
182+
// so upgrade all the dependencies.
183+
let mut dep = Dependency::new(&dependency.name);
184+
if let Some(rename) = dependency.rename {
185+
dep = dep.set_rename(&rename);
186+
}
187+
Some((dep, (dependency.registry, None)))
180188
} else {
181-
Ok((name, None))
189+
// User has asked for specific dependencies. Check if this dependency
190+
// was specified, populating the registry from the lockfile metadata.
191+
match selected_dependencies.get(&dependency.name) {
192+
Some(version) => Some((
193+
Dependency::new(&dependency.name),
194+
(dependency.registry, version.clone()),
195+
)),
196+
None => None,
197+
}
182198
}
183-
.map(move |(name, version)| (Dependency::new(&name), version))
184199
})
185-
.collect::<Result<_>>()?
186-
}))
200+
.collect(),
201+
))
187202
}
188203

189204
/// Upgrade the manifests on disk following the previously-determined upgrade schema.
@@ -222,8 +237,8 @@ impl Manifests {
222237
}
223238
}
224239

225-
/// The set of dependencies to be upgraded, alongside desired versions, if specified by the user.
226-
struct DesiredUpgrades(HashMap<Dependency, Option<String>>);
240+
/// The set of dependencies to be upgraded, alongside the registries returned from cargo metadata, and
241+
struct DesiredUpgrades(HashMap<Dependency, (Option<String>, Option<String>)>);
227242

228243
/// The complete specification of the upgrades that will be performed. Map of the dependency names
229244
/// to the new versions.
@@ -235,11 +250,17 @@ impl DesiredUpgrades {
235250
fn get_upgraded(self, allow_prerelease: bool, manifest_path: &Path) -> Result<ActualUpgrades> {
236251
self.0
237252
.into_iter()
238-
.map(|(dep, version)| {
253+
.map(|(dep, (registry, version))| {
239254
if let Some(v) = version {
240255
Ok((dep, v))
241256
} else {
242-
get_latest_dependency(&dep.name, allow_prerelease, manifest_path)
257+
let registry_url = match registry {
258+
Some(x) => Some(Url::parse(&x).map_err(|_| {
259+
ErrorKind::CargoEditLib(::cargo_edit::ErrorKind::InvalidCargoConfig)
260+
})?),
261+
None => None,
262+
};
263+
get_latest_dependency(&dep.name, allow_prerelease, manifest_path, &registry_url)
243264
.map(|new_dep| {
244265
(
245266
dep,
@@ -270,7 +291,8 @@ fn process(args: Args) -> Result<()> {
270291
} = args;
271292

272293
if !args.offline {
273-
update_registry_index(&find(&manifest_path)?)?;
294+
let url = registry_url(&find(&manifest_path)?, None)?;
295+
update_registry_index(&url)?;
274296
}
275297

276298
let manifests = if all {
@@ -281,6 +303,21 @@ fn process(args: Args) -> Result<()> {
281303

282304
let existing_dependencies = manifests.get_dependencies(dependency)?;
283305

306+
// Update indices for any alternative registries, unless
307+
// we're offline.
308+
if !args.offline {
309+
for registry_url in existing_dependencies
310+
.0
311+
.values()
312+
.filter_map(|(registry, _)| registry.as_ref())
313+
.unique()
314+
{
315+
update_registry_index(&Url::parse(registry_url).map_err(|_| {
316+
ErrorKind::CargoEditLib(::cargo_edit::ErrorKind::InvalidCargoConfig)
317+
})?)?;
318+
}
319+
}
320+
284321
let upgraded_dependencies =
285322
existing_dependencies.get_upgraded(allow_prerelease, &find(&manifest_path)?)?;
286323

src/dependency.rs

+42-4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ enum DependencySource {
55
Version {
66
version: Option<String>,
77
path: Option<String>,
8+
registry: Option<String>,
89
},
910
Git(String),
1011
}
@@ -32,6 +33,7 @@ impl Default for Dependency {
3233
source: DependencySource::Version {
3334
version: None,
3435
path: None,
36+
registry: None,
3537
},
3638
}
3739
}
@@ -53,13 +55,14 @@ impl Dependency {
5355
// ("version requirement […] includes semver metadata which will be ignored")
5456
let version = version.split('+').next().unwrap();
5557
let old_source = self.source;
56-
let old_path = match old_source {
57-
DependencySource::Version { path, .. } => path,
58-
_ => None,
58+
let (old_path, old_registry) = match old_source {
59+
DependencySource::Version { path, registry, .. } => (path, registry),
60+
_ => (None, None),
5961
};
6062
self.source = DependencySource::Version {
6163
version: Some(version.into()),
6264
path: old_path,
65+
registry: old_registry,
6366
};
6467
self
6568
}
@@ -80,6 +83,7 @@ impl Dependency {
8083
self.source = DependencySource::Version {
8184
version: old_version,
8285
path: Some(path.into()),
86+
registry: None,
8387
};
8488
self
8589
}
@@ -109,6 +113,21 @@ impl Dependency {
109113
&self.rename().unwrap_or(&self.name)
110114
}
111115

116+
/// Set the value of registry for the dependency
117+
pub fn set_registry(mut self, registry: &str) -> Dependency {
118+
let old_source = self.source;
119+
let old_version = match old_source {
120+
DependencySource::Version { version, .. } => version,
121+
_ => None,
122+
};
123+
self.source = DependencySource::Version {
124+
version: old_version,
125+
path: None,
126+
registry: Some(registry.into()),
127+
};
128+
self
129+
}
130+
112131
/// Get version of dependency
113132
pub fn version(&self) -> Option<&str> {
114133
if let DependencySource::Version {
@@ -150,6 +169,7 @@ impl Dependency {
150169
DependencySource::Version {
151170
version: Some(v),
152171
path: None,
172+
registry: None,
153173
},
154174
None,
155175
) => toml_edit::value(v),
@@ -158,13 +178,20 @@ impl Dependency {
158178
let mut data = toml_edit::InlineTable::default();
159179

160180
match source {
161-
DependencySource::Version { version, path } => {
181+
DependencySource::Version {
182+
version,
183+
path,
184+
registry,
185+
} => {
162186
if let Some(v) = version {
163187
data.get_or_insert("version", v);
164188
}
165189
if let Some(p) = path {
166190
data.get_or_insert("path", p);
167191
}
192+
if let Some(r) = registry {
193+
data.get_or_insert("registry", r);
194+
}
168195
}
169196
DependencySource::Git(v) => {
170197
data.get_or_insert("git", v);
@@ -268,6 +295,17 @@ mod tests {
268295
assert_eq!(dep.get("package").unwrap().as_str(), Some("dep"));
269296
}
270297

298+
#[test]
299+
fn to_toml_dep_from_alt_registry() {
300+
let toml = Dependency::new("dep").set_registry("alternative").to_toml();
301+
302+
assert_eq!(toml.0, "dep".to_owned());
303+
assert!(toml.1.is_inline_table());
304+
305+
let dep = toml.1.as_inline_table().unwrap();
306+
assert_eq!(dep.get("registry").unwrap().as_str(), Some("alternative"));
307+
}
308+
271309
#[test]
272310
fn to_toml_complex_dep() {
273311
let toml = Dependency::new("dep")

src/errors.rs

+4
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,9 @@ error_chain! {
6565
description("Unable to find the source specified by 'replace-with'")
6666
display("The source '{}' could not be found", name)
6767
}
68+
/// Unable to find the specified registry
69+
NoSuchRegistryFound(name: String) {
70+
display("The registry '{}' could not be found", name)
71+
}
6872
}
6973
}

0 commit comments

Comments
 (0)