mirror of
https://github.com/cargo-bins/cargo-binstall.git
synced 2025-04-24 14:28:42 +00:00
Support --registry
and more options from .cargo/config.toml
(#1195)
Fixed #885 Now we can take advantage of new argument `--registry` and env overrides: - `CARGO_REGISTRIES_DEFAULT` if `--registry` is not specified - `CARGO_REGISTRIES_{registry_name}_INDEX` for the registry index url We can also read from `.cargo/config.toml` for: - default registry and registries configurations - additional CA bundle `http.cainfo` Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com>
This commit is contained in:
parent
d280e122ca
commit
7dea40a99a
7 changed files with 297 additions and 63 deletions
|
@ -5,6 +5,8 @@
|
|||
//! Binstall reads from them to be compatible with `cargo-install`'s behavior.
|
||||
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
collections::BTreeMap,
|
||||
fs::File,
|
||||
io,
|
||||
path::{Path, PathBuf},
|
||||
|
@ -17,39 +19,57 @@ use miette::Diagnostic;
|
|||
use serde::Deserialize;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Install {
|
||||
/// `cargo install` destination directory
|
||||
pub root: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Http {
|
||||
/// HTTP proxy in libcurl format: "host:port"
|
||||
///
|
||||
/// env: CARGO_HTTP_PROXY or HTTPS_PROXY or https_proxy or http_proxy
|
||||
pub proxy: Option<CompactString>,
|
||||
/// timeout for each HTTP request, in seconds
|
||||
///
|
||||
/// env: CARGO_HTTP_TIMEOUT or HTTP_TIMEOUT
|
||||
pub timeout: Option<u64>,
|
||||
/// path to Certificate Authority (CA) bundle
|
||||
pub cainfo: Option<PathBuf>,
|
||||
// TODO:
|
||||
// Support field ssl-version, ssl-version.max, ssl-version.min,
|
||||
// which needs `toml_edit::Item`.
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Debug, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum Env {
|
||||
Value(CompactString),
|
||||
WithOptions {
|
||||
value: CompactString,
|
||||
force: Option<bool>,
|
||||
relative: Option<bool>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Registry {
|
||||
pub index: CompactString,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct DefaultRegistry {
|
||||
pub default: CompactString,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
pub struct Config {
|
||||
pub install: Install,
|
||||
pub http: Http,
|
||||
// TODO:
|
||||
// Add support for section patch, source and registry for alternative
|
||||
// crates.io registry.
|
||||
|
||||
// TODO:
|
||||
// Add field env for specifying env vars
|
||||
// which needs `toml_edit::Item`.
|
||||
pub install: Option<Install>,
|
||||
pub http: Option<Http>,
|
||||
pub env: Option<BTreeMap<CompactString, Env>>,
|
||||
pub registries: Option<BTreeMap<CompactString, Registry>>,
|
||||
pub registry: Option<DefaultRegistry>,
|
||||
}
|
||||
|
||||
fn join_if_relative(path: &mut Option<PathBuf>, dir: &Path) {
|
||||
fn join_if_relative(path: Option<&mut PathBuf>, dir: &Path) {
|
||||
match path {
|
||||
Some(path) if path.is_relative() => *path = dir.join(&path),
|
||||
_ => (),
|
||||
|
@ -81,8 +101,32 @@ impl Config {
|
|||
Ok(Default::default())
|
||||
} else {
|
||||
let mut config: Config = toml_edit::de::from_slice(&vec)?;
|
||||
join_if_relative(&mut config.install.root, dir);
|
||||
join_if_relative(&mut config.http.cainfo, dir);
|
||||
join_if_relative(
|
||||
config
|
||||
.install
|
||||
.as_mut()
|
||||
.and_then(|install| install.root.as_mut()),
|
||||
dir,
|
||||
);
|
||||
join_if_relative(
|
||||
config.http.as_mut().and_then(|http| http.cainfo.as_mut()),
|
||||
dir,
|
||||
);
|
||||
if let Some(envs) = config.env.as_mut() {
|
||||
for env in envs.values_mut() {
|
||||
if let Env::WithOptions {
|
||||
value,
|
||||
relative: Some(true),
|
||||
..
|
||||
} = env
|
||||
{
|
||||
let path = Cow::Borrowed(Path::new(&value));
|
||||
if path.is_relative() {
|
||||
*value = dir.join(&path).to_string_lossy().into();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(config)
|
||||
}
|
||||
}
|
||||
|
@ -91,10 +135,19 @@ impl Config {
|
|||
}
|
||||
|
||||
pub fn load_from_path(path: impl AsRef<Path>) -> Result<Self, ConfigLoadError> {
|
||||
let path = path.as_ref();
|
||||
let file = FileLock::new_shared(File::open(path)?)?;
|
||||
// Any regular file must have a parent dir
|
||||
Self::load_from_reader(file, path.parent().unwrap())
|
||||
fn inner(path: &Path) -> Result<Config, ConfigLoadError> {
|
||||
match File::open(path) {
|
||||
Ok(file) => {
|
||||
let file = FileLock::new_shared(file)?;
|
||||
// Any regular file must have a parent dir
|
||||
Config::load_from_reader(file, path.parent().unwrap())
|
||||
}
|
||||
Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(Default::default()),
|
||||
Err(err) => Err(err.into()),
|
||||
}
|
||||
}
|
||||
|
||||
inner(path.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,11 +167,19 @@ impl From<toml_edit::de::Error> for ConfigLoadError {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<toml_edit::TomlError> for ConfigLoadError {
|
||||
fn from(e: toml_edit::TomlError) -> Self {
|
||||
ConfigLoadError::TomlParse(Box::new(e.into()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use std::io::Cursor;
|
||||
use std::{io::Cursor, path::MAIN_SEPARATOR};
|
||||
|
||||
use compact_str::format_compact;
|
||||
|
||||
const CONFIG: &str = r#"
|
||||
[env]
|
||||
|
@ -127,7 +188,7 @@ ENV_VAR_NAME = "value"
|
|||
# Set even if already present in environment
|
||||
ENV_VAR_NAME_2 = { value = "value", force = true }
|
||||
# Value is relative to .cargo directory containing `config.toml`, make absolute
|
||||
ENV_VAR_NAME_3 = { value = "relative/path", relative = true }
|
||||
ENV_VAR_NAME_3 = { value = "relative-path", relative = true }
|
||||
|
||||
[http]
|
||||
debug = false # HTTP debugging
|
||||
|
@ -141,21 +202,39 @@ root = "/some/path" # `cargo install` destination directory
|
|||
|
||||
#[test]
|
||||
fn test_loading() {
|
||||
let config = Config::load_from_reader(Cursor::new(&CONFIG), Path::new("/root")).unwrap();
|
||||
let config = Config::load_from_reader(Cursor::new(&CONFIG), Path::new("root")).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
config.install.root.as_deref().unwrap(),
|
||||
config.install.unwrap().root.as_deref().unwrap(),
|
||||
Path::new("/some/path")
|
||||
);
|
||||
assert_eq!(
|
||||
config.http.proxy,
|
||||
Some(CompactString::new_inline("host:port"))
|
||||
);
|
||||
|
||||
assert_eq!(config.http.timeout, Some(30));
|
||||
let http = config.http.unwrap();
|
||||
assert_eq!(http.proxy.unwrap(), CompactString::new_inline("host:port"));
|
||||
assert_eq!(http.timeout.unwrap(), 30);
|
||||
assert_eq!(http.cainfo.unwrap(), Path::new("root").join("cert.pem"));
|
||||
|
||||
let env = config.env.unwrap();
|
||||
assert_eq!(env.len(), 3);
|
||||
assert_eq!(
|
||||
config.http.cainfo.as_deref().unwrap(),
|
||||
Path::new("/root/cert.pem")
|
||||
env.get("ENV_VAR_NAME").unwrap(),
|
||||
&Env::Value(CompactString::new("value"))
|
||||
);
|
||||
assert_eq!(
|
||||
env.get("ENV_VAR_NAME_2").unwrap(),
|
||||
&Env::WithOptions {
|
||||
value: CompactString::new("value"),
|
||||
force: Some(true),
|
||||
relative: None,
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
env.get("ENV_VAR_NAME_3").unwrap(),
|
||||
&Env::WithOptions {
|
||||
value: format_compact!("root{MAIN_SEPARATOR}relative-path"),
|
||||
force: None,
|
||||
relative: Some(true),
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue