Skip to content

Commit

Permalink
feature: allow reading RHACK_DIR from .cargo/config.toml files.
Browse files Browse the repository at this point in the history
Order: `RHACK_DIR` env variable, local `.cargo/config.toml`, `$CARGO_HOME/config.toml`.
  • Loading branch information
seivan committed Jul 18, 2024
1 parent 7476051 commit 42855e3
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 17 deletions.
3 changes: 3 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

[env]
RHACK_DIR = "./.rhack"
8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ pr-run-mode = "plan"
# see: https://nnethercote.github.io/perf-book/build-configuration.html
[profile.dev]
opt-level = 0
debug = 0 # try for faster builds, usually 'true'
strip = "debuginfo" # try for faster builds
debug = 0 # try for faster builds, usually 'true'
strip = "debuginfo" # try for faster builds
rpath = false
lto = false
debug-assertions = true
Expand All @@ -71,7 +71,7 @@ debug = true

[profile.release]
opt-level = 3
debug = false # true for profiling
debug = false # true for profiling
rpath = false
lto = "fat"
debug-assertions = false
Expand All @@ -89,7 +89,7 @@ codegen-units = 4

[profile.bench]
opt-level = 3
debug = true # true for profiling
debug = true # true for profiling
rpath = false
lto = true
debug-assertions = false
Expand Down
158 changes: 145 additions & 13 deletions src/cmd.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
mod edit;
mod undo;

use anyhow::{anyhow, Result};
use clap::{Parser, Subcommand};
use serde_json::Value;
use std::fs;
use std::path::PathBuf;
use std::process::Command;
use std::{env, str::FromStr};

use anyhow::{anyhow, Result};
use clap::{Parser, Subcommand};
use serde_json::Value;
use toml_edit::DocumentMut;

pub use edit::Edit;
Expand All @@ -18,6 +17,12 @@ const DEFAULT_RHACK_DIR_NAME: &str = ".rhack";
const RHACK_DIR_ENV_KEY: &str = "RHACK_DIR";
const PATCH_TABLE_NAME: &str = "patch";
const REGISTRY_TABLE_NAME: &str = "crates-io";
const CARGO_HOME_ENV_KEY: &str = if cfg!(test) {
"CARGO_HOME_TEST"
} else {
"CARGO_HOME"
};
const CARGO_CONFIG_FILE_NAME: &str = "config.toml";

pub trait Cmd {
fn run(&self) -> Result<()>;
Expand Down Expand Up @@ -54,18 +59,66 @@ impl Cmd for Commands {
}
}

pub fn load_path_from_config() -> Result<PathBuf> {
let get_path_from_config_env: Result<PathBuf> = {
let parse_config = |s: PathBuf| {
Result::Ok(s)
.map(|cargo_dir| cargo_dir.join(CARGO_CONFIG_FILE_NAME))
.and_then(fs::read_to_string)
.map_err(From::from)
.and_then(|config_text| Ok(config_text.parse::<DocumentMut>()?))
.and_then(|mut x| {
x.remove("env")
.ok_or_else(|| anyhow!("Failed to find 'env' key in config.toml."))
})
.and_then(|x| {
let p = x
.get("RHACK_DIR")
.filter(|x| x.is_str())
.ok_or_else(|| anyhow!("Failed to find 'RHACK_DIR' key in config.toml."))
.and_then(|x| {
x.as_str()
.filter(|s| s.is_empty() == false)
.ok_or_else(|| anyhow!("RHACK_DIR is empty."))
})
.map(|x| x.to_string());

p
})
.and_then(|p| Ok(PathBuf::from_str(&p)?))
};
let cwd_config = || {
env::current_dir()
.map_err(From::from)
.map(|cwd| cwd.join(".cargo"))
};
let global_config = || {
env::var(CARGO_HOME_ENV_KEY)
.map_err(From::from)
.and_then(|x| Ok(PathBuf::from_str(&x)?))
};

if cfg!(test) {
global_config().or_else(|_| cwd_config())
} else {
cwd_config().or_else(|_| global_config())
}
.and_then(parse_config)
};
get_path_from_config_env
}
// Gives back user-difined rhack dir path. If none, the default will be given.
#[must_use]
pub fn rhack_dir() -> PathBuf {
env::var(RHACK_DIR_ENV_KEY).map_or_else(
|_| {
let Some(home_dir) = home::home_dir() else {
panic!("failed to find home directory")
};
home_dir.join(DEFAULT_RHACK_DIR_NAME)
},
PathBuf::from,
)
env::var(RHACK_DIR_ENV_KEY)
.map(PathBuf::from)
.or_else(|_| load_path_from_config())
.or_else(|_| {
home::home_dir()
.map(|dir| dir.join(DEFAULT_RHACK_DIR_NAME))
.ok_or(anyhow!("Failed to find home directory."))
})
.expect("Failed to get directory path.")
}

// Gives back the the path to Cargo.toml reffered from the working directory.
Expand Down Expand Up @@ -112,3 +165,82 @@ pub fn load_manifest(manifest_path: &PathBuf) -> Result<DocumentMut> {
)),
}
}

#[cfg(test)]
mod tests {

use super::*;

fn run_tests_inside_path(run_test: impl Fn(PathBuf)) {
use std::time::{SystemTime, UNIX_EPOCH};

fn config_text(path: PathBuf) -> String {
format!(
r#"
[env]
RHACK_DIR = {path:?}
"#
)
}
fn random() -> Result<String> {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.subsec_nanos())
.map(|n| n.to_string())
.map_err(From::from)
}

let get_random_test_dir = random()
.map_err(anyhow::Error::from)
.map(|rand| env::temp_dir().join("rhack_tests").join(rand))
.and_then(|test_dir| {
fs::create_dir_all(&test_dir)?;
Ok(test_dir)
})
.and_then(|test_dir| {
fs::write(
&test_dir.join(CARGO_CONFIG_FILE_NAME),
config_text(test_dir.clone()),
)?;
Ok(test_dir)
});

let _b = get_random_test_dir
.inspect(|path| env::set_var(CARGO_HOME_ENV_KEY, path))
.inspect(|path| run_test(path.clone()))
.inspect(|path| {
let _ = fs::remove_dir_all(&path);
})
.inspect(|_| env::remove_var(CARGO_HOME_ENV_KEY))
.expect("run_tests_inside_path");
}
#[test]
fn local_file_test() {
env::remove_var(RHACK_DIR_ENV_KEY);

let result = load_path_from_config().expect("local_file_test");

assert_eq!(result, PathBuf::from("./").join(DEFAULT_RHACK_DIR_NAME));
}
#[test]
fn global_file_test() {
env::remove_var(RHACK_DIR_ENV_KEY);

run_tests_inside_path(|path| {
let result = load_path_from_config().expect("global_file_test");
assert_eq!(result, path);
});
}
#[test]
fn rhack_env_override_test() {
env::remove_var(RHACK_DIR_ENV_KEY);

run_tests_inside_path(|path| {
let modified_path = path.join("modified");
env::set_var(RHACK_DIR_ENV_KEY, modified_path.clone());
let result = rhack_dir();
assert_eq!(result, modified_path);
assert_ne!(result, load_path_from_config().expect("rhack_env_dir_test"));
});
}
}

0 comments on commit 42855e3

Please sign in to comment.