use nu_errors::ShellError;
use std::path::{Path, PathBuf};
static LOCAL_CFG_FILE_NAME: &str = ".nu-env";
pub struct LocalConfigDiff {
pub cfgs_to_load: Vec<PathBuf>,
pub cfgs_to_unload: Vec<PathBuf>,
}
impl LocalConfigDiff {
pub fn between(from: PathBuf, to: PathBuf) -> (LocalConfigDiff, Vec<ShellError>) {
let common_prefix = common_path::common_path(&from, &to);
let (cfgs_to_unload, err_down) = walk_down(&from, &common_prefix);
let (cfgs_to_load, err_up) = walk_up(&common_prefix, &to);
(
LocalConfigDiff {
cfgs_to_load,
cfgs_to_unload,
},
err_down.into_iter().chain(err_up).collect(),
)
}
}
fn walk_down(
from_inclusive: &Path,
to_exclusive: &Option<PathBuf>,
) -> (Vec<PathBuf>, Vec<ShellError>) {
let mut all_err = vec![];
let mut all_cfgs_to_unload = vec![];
for dir in from_inclusive.ancestors().take_while(|cur_path| {
if let Some(until_path) = to_exclusive {
*cur_path != until_path
} else {
true
}
}) {
match local_cfg_should_be_unloaded(dir.to_path_buf()) {
Ok(Some(cfg)) => all_cfgs_to_unload.push(cfg),
Err(e) => all_err.push(e),
_ => {}
}
}
(all_cfgs_to_unload, all_err)
}
fn walk_up(
from_exclusive: &Option<PathBuf>,
to_inclusive: &Path,
) -> (Vec<PathBuf>, Vec<ShellError>) {
let mut all_err = vec![];
let mut all_cfgs_to_load = vec![];
let skip_ahead = from_exclusive
.as_ref()
.map(|p| p.ancestors().count())
.unwrap_or(0);
let dirs: Vec<_> = to_inclusive.ancestors().map(Path::to_path_buf).collect();
for dir in dirs.iter().rev().skip(skip_ahead) {
match loadable_cfg_exists_in_dir(dir.clone()) {
Ok(Some(cfg)) => all_cfgs_to_load.push(cfg),
Err(e) => all_err.push(e),
_ => {}
}
}
(all_cfgs_to_load, all_err)
}
fn is_existent_local_cfg(cfg_file_path: &Path) -> Result<bool, ShellError> {
if !cfg_file_path.exists() || cfg_file_path.parent() == super::default_path()?.parent() {
Ok(false)
} else {
Ok(true)
}
}
fn is_trusted_local_cfg_content(cfg_file_path: &Path, content: &[u8]) -> Result<bool, ShellError> {
if !super::is_file_trusted(cfg_file_path, content)? {
Err(ShellError::untagged_runtime_error(
format!("{:?} is untrusted. Run 'autoenv trust {:?}' to trust it.\nThis needs to be done after each change to the file.",
cfg_file_path, cfg_file_path.parent().unwrap_or_else(|| Path::new("")))))
} else {
Ok(true)
}
}
fn local_cfg_should_be_unloaded<P: AsRef<Path>>(cfg_dir: P) -> Result<Option<PathBuf>, ShellError> {
let mut cfg = cfg_dir.as_ref().to_path_buf();
cfg.push(LOCAL_CFG_FILE_NAME);
if is_existent_local_cfg(&cfg)? {
Ok(Some(cfg))
} else {
Ok(None)
}
}
pub fn loadable_cfg_exists_in_dir(mut cfg_dir: PathBuf) -> Result<Option<PathBuf>, ShellError> {
cfg_dir.push(LOCAL_CFG_FILE_NAME);
let cfg_path = cfg_dir;
if !is_existent_local_cfg(&cfg_path)? {
return Ok(None);
}
let content = std::fs::read(&cfg_path)?;
if !is_trusted_local_cfg_content(&cfg_path, &content)? {
return Ok(None);
}
Ok(Some(cfg_path))
}