mirror of
https://github.com/cargo-bins/cargo-binstall.git
synced 2025-04-24 22:30:03 +00:00
Use install.root
in $CARGO_HOME/config.toml
(#884)
Fixed #859 * Impl `cargo_config` * Use `install.root` in `$CARGO_HOME/.cargo/config.toml` before fallback to `cargo_home`. * Improve logging in `get_cargo_roots_path` Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com>
This commit is contained in:
parent
3e830c9fad
commit
58b7044e98
3 changed files with 182 additions and 10 deletions
|
@ -4,23 +4,33 @@ use std::{
|
|||
};
|
||||
|
||||
use binstalk::home::cargo_home;
|
||||
use binstalk_manifests::cargo_config::Config;
|
||||
use tracing::debug;
|
||||
|
||||
pub fn get_cargo_roots_path(cargo_roots: Option<PathBuf>) -> Option<PathBuf> {
|
||||
if let Some(p) = cargo_roots {
|
||||
return Some(p);
|
||||
}
|
||||
|
||||
// Environmental variables
|
||||
if let Some(p) = var_os("CARGO_INSTALL_ROOT") {
|
||||
Some(p)
|
||||
} else if let Some(p) = var_os("CARGO_INSTALL_ROOT") {
|
||||
// Environmental variables
|
||||
let p = PathBuf::from(p);
|
||||
debug!("using CARGO_INSTALL_ROOT ({})", p.display());
|
||||
return Some(p);
|
||||
}
|
||||
|
||||
if let Ok(p) = cargo_home() {
|
||||
debug!("using ({}) as cargo home", p.display());
|
||||
Some(p)
|
||||
} else if let Ok(cargo_home) = cargo_home() {
|
||||
let config_path = cargo_home.join("config.toml");
|
||||
if let Some(root) = Config::load_from_path(&config_path)
|
||||
.ok()
|
||||
.and_then(|config| config.install.root)
|
||||
{
|
||||
debug!(
|
||||
"using `install.root` {} from config {}",
|
||||
root.display(),
|
||||
config_path.display()
|
||||
);
|
||||
Some(root)
|
||||
} else {
|
||||
debug!("using ({}) as cargo home", cargo_home.display());
|
||||
Some(cargo_home)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
161
crates/binstalk-manifests/src/cargo_config.rs
Normal file
161
crates/binstalk-manifests/src/cargo_config.rs
Normal file
|
@ -0,0 +1,161 @@
|
|||
//! Cargo's `.cargo/config.toml`
|
||||
//!
|
||||
//! This manifest is used by Cargo to load configurations stored by users.
|
||||
//!
|
||||
//! Binstall reads from them to be compatible with `cargo-install`'s behavior.
|
||||
|
||||
use std::{
|
||||
fs::File,
|
||||
io,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use compact_str::CompactString;
|
||||
use fs_lock::FileLock;
|
||||
use home::cargo_home;
|
||||
use miette::Diagnostic;
|
||||
use serde::Deserialize;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
pub struct Install {
|
||||
/// `cargo install` destination directory
|
||||
pub root: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
pub struct Http {
|
||||
/// HTTP proxy in libcurl format: "host:port"
|
||||
pub proxy: Option<CompactString>,
|
||||
/// timeout for each HTTP request, in seconds
|
||||
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(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`.
|
||||
}
|
||||
|
||||
fn join_if_relative(path: &mut Option<PathBuf>, dir: &Path) {
|
||||
match path {
|
||||
Some(path) if path.is_relative() => *path = dir.join(&path),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn default_path() -> Result<PathBuf, ConfigLoadError> {
|
||||
Ok(cargo_home()?.join("config.toml"))
|
||||
}
|
||||
|
||||
pub fn load() -> Result<Self, ConfigLoadError> {
|
||||
Self::load_from_path(Self::default_path()?)
|
||||
}
|
||||
|
||||
/// * `dir` - path to the dir where the config.toml is located.
|
||||
/// For relative path in the config, `Config::load_from_reader`
|
||||
/// will join the `dir` and the relative path to form the final
|
||||
/// path.
|
||||
pub fn load_from_reader<R: io::Read>(
|
||||
mut reader: R,
|
||||
dir: &Path,
|
||||
) -> Result<Self, ConfigLoadError> {
|
||||
fn inner(reader: &mut dyn io::Read, dir: &Path) -> Result<Config, ConfigLoadError> {
|
||||
let mut vec = Vec::new();
|
||||
reader.read_to_end(&mut vec)?;
|
||||
|
||||
if vec.is_empty() {
|
||||
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);
|
||||
Ok(config)
|
||||
}
|
||||
}
|
||||
|
||||
inner(&mut reader, dir)
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Diagnostic, Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum ConfigLoadError {
|
||||
#[error("I/O Error: {0}")]
|
||||
Io(#[from] io::Error),
|
||||
|
||||
#[error("Failed to deserialize toml: {0}")]
|
||||
TomlParse(Box<toml_edit::de::Error>),
|
||||
}
|
||||
|
||||
impl From<toml_edit::de::Error> for ConfigLoadError {
|
||||
fn from(e: toml_edit::de::Error) -> Self {
|
||||
ConfigLoadError::TomlParse(Box::new(e))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use std::io::Cursor;
|
||||
|
||||
const CONFIG: &str = r#"
|
||||
[env]
|
||||
# Set ENV_VAR_NAME=value for any process run by Cargo
|
||||
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 }
|
||||
|
||||
[http]
|
||||
debug = false # HTTP debugging
|
||||
proxy = "host:port" # HTTP proxy in libcurl format
|
||||
timeout = 30 # timeout for each HTTP request, in seconds
|
||||
cainfo = "cert.pem" # path to Certificate Authority (CA) bundle
|
||||
|
||||
[install]
|
||||
root = "/some/path" # `cargo install` destination directory
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn test_loading() {
|
||||
let config = Config::load_from_reader(Cursor::new(&CONFIG), Path::new("/root")).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
config.install.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));
|
||||
assert_eq!(
|
||||
config.http.cainfo.as_deref().unwrap(),
|
||||
Path::new("/root/cert.pem")
|
||||
);
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@
|
|||
mod helpers;
|
||||
|
||||
pub mod binstall_crates_v1;
|
||||
pub mod cargo_config;
|
||||
pub mod cargo_crates_v1;
|
||||
|
||||
pub use binstalk_types::{cargo_toml_binstall, crate_info};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue