pub use std::env::*;
use std::{path::PathBuf, sync::LazyLock};
use indexmap::IndexSet;
use crate::git::StashMethod;
pub static HOME_DIR: LazyLock<PathBuf> = LazyLock::new(|| dirs::home_dir().unwrap_or_default());
pub static HK_STATE_DIR: LazyLock<PathBuf> = LazyLock::new(|| {
var_path("HK_STATE_DIR").unwrap_or(
dirs::state_dir()
.unwrap_or(HOME_DIR.join(".local").join("state"))
.join("hk"),
)
});
pub static HK_FILE: LazyLock<Option<String>> = LazyLock::new(|| var("HK_FILE").ok());
pub static HK_CACHE_DIR: LazyLock<PathBuf> = LazyLock::new(|| {
var_path("HK_CACHE_DIR").unwrap_or(
dirs::cache_dir()
.unwrap_or(HOME_DIR.join(".cache"))
.join("hk"),
)
});
pub static XDG_CONFIG_HOME: LazyLock<PathBuf> =
LazyLock::new(|| var_path("XDG_CONFIG_HOME").unwrap_or_else(|| HOME_DIR.join(".config")));
pub static HK_CONFIG_DIR: LazyLock<PathBuf> =
LazyLock::new(|| var_path("HK_CONFIG_DIR").unwrap_or_else(|| XDG_CONFIG_HOME.join("hk")));
pub static HK_LOG: LazyLock<log::LevelFilter> = LazyLock::new(|| {
var_log_level("HK_LOG")
.or(var_log_level("HK_LOG_LEVEL"))
.unwrap_or(log::LevelFilter::Info)
});
pub static HK_LOG_FILE_LEVEL: LazyLock<log::LevelFilter> =
LazyLock::new(|| var_log_level("HK_LOG_FILE_LEVEL").unwrap_or(*HK_LOG));
pub static HK_LOG_FILE: LazyLock<PathBuf> =
LazyLock::new(|| var_path("HK_LOG_FILE").unwrap_or(HK_STATE_DIR.join("hk.log")));
pub static HK_TIMING_JSON: LazyLock<Option<PathBuf>> = LazyLock::new(|| var_path("HK_TIMING_JSON"));
pub static HK_LIBGIT2: LazyLock<bool> = LazyLock::new(|| !var_false("HK_LIBGIT2"));
pub static HK_HIDE_WHEN_DONE: LazyLock<bool> = LazyLock::new(|| var_true("HK_HIDE_WHEN_DONE"));
pub static HK_CHECK_FIRST: LazyLock<bool> = LazyLock::new(|| !var_false("HK_CHECK_FIRST"));
pub static HK_STASH: LazyLock<Option<StashMethod>> = LazyLock::new(|| {
if var_false("HK_STASH") {
Some(StashMethod::None)
} else {
var("HK_STASH")
.map(|v| Some(v.parse().expect("invalid HK_STASH value")))
.unwrap_or(None)
}
});
pub static HK_STASH_UNTRACKED: LazyLock<bool> = LazyLock::new(|| !var_false("HK_STASH_UNTRACKED"));
pub static HK_FIX: LazyLock<bool> = LazyLock::new(|| !var_false("HK_FIX"));
pub static HK_MISE: LazyLock<bool> = LazyLock::new(|| var_true("HK_MISE"));
pub static HK_SKIP_STEPS: LazyLock<IndexSet<String>> = LazyLock::new(|| {
var_csv("HK_SKIP_STEPS")
.or(var_csv("HK_SKIP_STEP"))
.unwrap_or_default()
});
pub static HK_SUMMARY_TEXT: LazyLock<bool> = LazyLock::new(|| var_true("HK_SUMMARY_TEXT"));
pub static HK_CACHE: LazyLock<bool> = LazyLock::new(|| {
var("HK_CACHE")
.map(|_| !var_false("HK_CACHE"))
.unwrap_or(!cfg!(debug_assertions)) });
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum TraceMode {
Off,
Text,
Json,
}
pub static HK_TRACE: LazyLock<TraceMode> =
LazyLock::new(|| match var("HK_TRACE").map(|v| v.to_lowercase()) {
Ok(v) if v == "json" => TraceMode::Json,
Ok(v) if v == "1" || v == "true" => TraceMode::Text,
_ => TraceMode::Off,
});
pub static HK_JSON: LazyLock<bool> = LazyLock::new(|| var_true("HK_JSON"));
pub static GIT_INDEX_FILE: LazyLock<Option<PathBuf>> = LazyLock::new(|| var_path("GIT_INDEX_FILE"));
pub static HK_PKL_HTTP_REWRITE: LazyLock<Option<String>> =
LazyLock::new(|| var("HK_PKL_HTTP_REWRITE").ok());
pub static HK_PKL_CA_CERTIFICATES: LazyLock<Option<PathBuf>> =
LazyLock::new(|| var_path("HK_PKL_CA_CERTIFICATES"));
pub static HK_PKL_BACKEND: LazyLock<Option<String>> = LazyLock::new(|| var("HK_PKL_BACKEND").ok());
pub static ARG_MAX: LazyLock<usize> = LazyLock::new(|| {
#[cfg(unix)]
{
unsafe {
let value = libc::sysconf(libc::_SC_ARG_MAX);
if value != -1 && value > 0 {
return value as usize;
}
}
}
128 * 1024
});
fn var_path(name: &str) -> Option<PathBuf> {
var(name).map(PathBuf::from).ok()
}
fn var_csv(name: &str) -> Option<IndexSet<String>> {
var(name)
.map(|val| val.split(',').map(|s| s.trim().to_string()).collect())
.ok()
}
fn var_log_level(name: &str) -> Option<log::LevelFilter> {
var(name).ok().and_then(|level| level.parse().ok())
}
fn var_true(name: &str) -> bool {
var(name)
.map(|val| val.to_lowercase())
.map(|val| val == "true" || val == "1")
.unwrap_or(false)
}
fn var_false(name: &str) -> bool {
var(name)
.map(|val| val.to_lowercase())
.map(|val| val == "false" || val == "0")
.unwrap_or(false)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_arg_max_is_valid() {
let arg_max = *ARG_MAX;
assert!(arg_max >= 128 * 1024, "ARG_MAX too small: {}", arg_max);
assert!(
arg_max <= 10 * 1024 * 1024,
"ARG_MAX suspiciously large (possible -1 cast): {}",
arg_max
);
}
#[test]
#[cfg(unix)]
fn test_arg_max_unix_sysconf() {
unsafe {
let value = libc::sysconf(libc::_SC_ARG_MAX);
if value == -1 {
assert_eq!(*ARG_MAX, 128 * 1024);
} else {
assert_eq!(*ARG_MAX, value as usize);
}
}
}
}