Skip to content

Commit

Permalink
Add IndexUrl::for_registry_name (#61)
Browse files Browse the repository at this point in the history
Resolves: #60
  • Loading branch information
Jake-Shadle authored May 3, 2024
1 parent eacff00 commit 130fb77
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 5 deletions.
7 changes: 7 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,16 @@ pub enum Error {
/// was not valid utf-8
#[error("unable to use non-utf8 path {:?}", .0)]
NonUtf8Path(std::path::PathBuf),
/// An environment variable was located, but had a non-utf8 value
#[error("environment variable {} has a non-utf8 value", .0)]
NonUtf8EnvVar(std::borrow::Cow<'static, str>),
/// A user-provided string was not a valid crate name
#[error(transparent)]
InvalidKrateName(#[from] InvalidKrateName),
/// The user specified a registry name that did not exist in any searched
/// .cargo/config.toml
#[error("registry '{}' was not located in any .cargo/config.toml", .0)]
UnknownRegistry(String),
/// An I/O error
#[error(transparent)]
Io(#[from] std::io::Error),
Expand Down
144 changes: 140 additions & 4 deletions src/index/location.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ impl<'iu> IndexUrl<'iu> {
) -> Result<Self, Error> {
// If the crates.io registry has been replaced it doesn't matter what
// the protocol for it has been changed to
if let Some(replacement) = get_crates_io_replacement(config_root.clone(), cargo_home)? {
if let Some(replacement) =
get_source_replacement(config_root.clone(), cargo_home, "crates-io")?
{
return Ok(replacement);
}

Expand Down Expand Up @@ -99,6 +101,64 @@ impl<'iu> IndexUrl<'iu> {
Self::CratesIoGit
})
}

/// Creates an [`IndexUrl`] for the specified registry name
///
/// 1. Checks if [`CARGO_REGISTRIES_<name>_INDEX`](https://doc.rust-lang.org/cargo/reference/config.html#registriesnameindex) is set
/// 2. Checks if the source for the registry has been [replaced](https://doc.rust-lang.org/cargo/reference/source-replacement.html)
/// 3. Uses the value of [`registries.<name>.index`](https://doc.rust-lang.org/cargo/reference/config.html#registriesnameindex) otherwise
pub fn for_registry_name(
config_root: Option<PathBuf>,
cargo_home: Option<&Path>,
registry_name: &str,
) -> Result<Self, Error> {
// Check if the index was explicitly specified
let mut env = String::with_capacity(17 + registry_name.len() + 6);
env.push_str("CARGO_REGISTRIES_");

if registry_name.is_ascii() {
for c in registry_name.chars() {
if c == '-' {
env.push('_');
} else {
env.push(c.to_ascii_uppercase());
}
}
} else {
let mut upper = registry_name.to_uppercase();
if upper.contains('-') {
upper = upper.replace('-', "_");
}

env.push_str(&upper);
}

env.push_str("_INDEX");

match std::env::var(&env) {
Ok(index) => return Ok(Self::NonCratesIo(index.into())),
Err(err) => {
if let std::env::VarError::NotUnicode(_nu) = err {
return Err(Error::NonUtf8EnvVar(env.into()));
}
}
}

if let Some(replacement) =
get_source_replacement(config_root.clone(), cargo_home, registry_name)?
{
return Ok(replacement);
}

read_cargo_config(config_root, cargo_home, |config| {
let path = format!("/registries/{registry_name}/index");
config
.pointer(&path)?
.as_str()
.map(|si| Self::NonCratesIo(si.to_owned().into()))
})?
.ok_or_else(|| Error::UnknownRegistry(registry_name.into()))
}
}

impl<'iu> From<&'iu str> for IndexUrl<'iu> {
Expand Down Expand Up @@ -242,16 +302,18 @@ pub(crate) fn read_cargo_config<T>(
Ok(None)
}

/// Gets the url of a replacement registry for crates.io if one has been configured
/// Gets the url of a replacement registry for the specified registry if one has been configured
///
/// See <https://doc.rust-lang.org/cargo/reference/source-replacement.html>
#[inline]
pub(crate) fn get_crates_io_replacement<'iu>(
pub(crate) fn get_source_replacement<'iu>(
root: Option<PathBuf>,
cargo_home: Option<&Path>,
registry_name: &str,
) -> Result<Option<IndexUrl<'iu>>, Error> {
read_cargo_config(root, cargo_home, |config| {
let repw = config.pointer("/source/crates-io/replace-with")?.as_str()?;
let path = format!("/source/{registry_name}/replace-with");
let repw = config.pointer(&path)?.as_str()?;
let sources = config.pointer("/source")?.as_table()?;
let replace_src = sources.get(&repw.into())?.as_table()?;

Expand Down Expand Up @@ -324,4 +386,78 @@ protocol = "git"
assert_eq!(iurl.as_str(), *url);
}
}

#[test]
fn custom() {
assert!(std::env::var_os("CARGO_REGISTRIES_TAME_INDEX_TEST_INDEX").is_none());

let td = tempfile::tempdir().unwrap();
let root = crate::PathBuf::from_path_buf(td.path().to_owned()).unwrap();
let cfg_toml = td.path().join(".cargo/config.toml");

std::fs::create_dir_all(cfg_toml.parent().unwrap()).unwrap();

const SPARSE: &str = r#"[registries.tame-index-test]
index = "sparse+https://some-url.com"
"#;

const GIT: &str = r#"[registries.tame-index-test]
index = "https://some-url.com"
"#;

{
std::fs::write(&cfg_toml, SPARSE).unwrap();

let iurl =
super::IndexUrl::for_registry_name(Some(root.clone()), None, "tame-index-test")
.unwrap();
assert_eq!(iurl.as_str(), "sparse+https://some-url.com");
assert!(iurl.is_sparse());

std::env::set_var(
"CARGO_REGISTRIES_TAME_INDEX_TEST_INDEX",
"sparse+https://some-other-url.com",
);

let iurl =
super::IndexUrl::for_registry_name(Some(root.clone()), None, "tame-index-test")
.unwrap();
assert_eq!(iurl.as_str(), "sparse+https://some-other-url.com");
assert!(iurl.is_sparse());

std::env::remove_var("CARGO_REGISTRIES_TAME_INDEX_TEST_INDEX");
}

{
std::fs::write(&cfg_toml, GIT).unwrap();

let iurl =
super::IndexUrl::for_registry_name(Some(root.clone()), None, "tame-index-test")
.unwrap();
assert_eq!(iurl.as_str(), "https://some-url.com");
assert!(!iurl.is_sparse());

std::env::set_var(
"CARGO_REGISTRIES_TAME_INDEX_TEST_INDEX",
"https://some-other-url.com",
);

let iurl =
super::IndexUrl::for_registry_name(Some(root.clone()), None, "tame-index-test")
.unwrap();
assert_eq!(iurl.as_str(), "https://some-other-url.com");
assert!(!iurl.is_sparse());

std::env::remove_var("CARGO_REGISTRIES_TAME_INDEX_TEST_INDEX");
}

#[allow(unused_variables)]
{
let err = crate::Error::UnknownRegistry("non-existant".to_owned());
assert!(matches!(
super::IndexUrl::for_registry_name(Some(root.clone()), None, "non-existant"),
Err(err),
));
}
}
}
2 changes: 1 addition & 1 deletion src/utils/flock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub enum LockError {
#[error("failed to create parent directories for lock path")]
CreateDir(std::io::Error),
/// Locking is not supported if the lock file is on an NFS, though note this
/// is a bit more nuanced as NFSv4 _does_ support file locking, but is out
/// is a bit more nuanced as `NFSv4` _does_ support file locking, but is out
/// of scope, at least for now
#[error("NFS do not support locking")]
Nfs,
Expand Down

0 comments on commit 130fb77

Please sign in to comment.