nu_data/config/
local_config.rs1use nu_errors::ShellError;
2use std::path::{Path, PathBuf};
3
4static LOCAL_CFG_FILE_NAME: &str = ".nu-env";
5
6pub struct LocalConfigDiff {
7 pub cfgs_to_load: Vec<PathBuf>,
8 pub cfgs_to_unload: Vec<PathBuf>,
9}
10
11impl LocalConfigDiff {
21 pub fn between(from: PathBuf, to: PathBuf) -> (LocalConfigDiff, Vec<ShellError>) {
22 let common_prefix = common_path::common_path(&from, &to);
23 let (cfgs_to_unload, err_down) = walk_down(&from, &common_prefix);
24 let (cfgs_to_load, err_up) = walk_up(&common_prefix, &to);
25
26 (
27 LocalConfigDiff {
28 cfgs_to_load,
29 cfgs_to_unload,
30 },
31 err_down.into_iter().chain(err_up).collect(),
32 )
33 }
34}
35
36fn walk_down(
42 from_inclusive: &Path,
43 to_exclusive: &Option<PathBuf>,
44) -> (Vec<PathBuf>, Vec<ShellError>) {
45 let mut all_err = vec![];
46 let mut all_cfgs_to_unload = vec![];
47 for dir in from_inclusive.ancestors().take_while(|cur_path| {
48 if let Some(until_path) = to_exclusive {
49 *cur_path != until_path
51 } else {
52 true
54 }
55 }) {
56 match local_cfg_should_be_unloaded(dir.to_path_buf()) {
57 Ok(Some(cfg)) => all_cfgs_to_unload.push(cfg),
58 Err(e) => all_err.push(e),
59 _ => {}
60 }
61 }
62
63 (all_cfgs_to_unload, all_err)
64}
65
66fn walk_up(
72 from_exclusive: &Option<PathBuf>,
73 to_inclusive: &Path,
74) -> (Vec<PathBuf>, Vec<ShellError>) {
75 let mut all_err = vec![];
76 let mut all_cfgs_to_load = vec![];
77
78 let skip_ahead = from_exclusive
80 .as_ref()
81 .map(|p| p.ancestors().count())
82 .unwrap_or(0);
83 let dirs: Vec<_> = to_inclusive.ancestors().map(Path::to_path_buf).collect();
87 for dir in dirs.iter().rev().skip(skip_ahead) {
88 match loadable_cfg_exists_in_dir(dir.clone()) {
89 Ok(Some(cfg)) => all_cfgs_to_load.push(cfg),
90 Err(e) => all_err.push(e),
91 _ => {}
92 }
93 }
94
95 (all_cfgs_to_load, all_err)
96}
97
98fn is_existent_local_cfg(cfg_file_path: &Path) -> Result<bool, ShellError> {
99 if !cfg_file_path.exists() || cfg_file_path.parent() == super::default_path()?.parent() {
100 Ok(false)
102 } else {
103 Ok(true)
104 }
105}
106
107fn is_trusted_local_cfg_content(cfg_file_path: &Path, content: &[u8]) -> Result<bool, ShellError> {
108 if !super::is_file_trusted(cfg_file_path, content)? {
110 Err(ShellError::untagged_runtime_error(
112 format!("{:?} is untrusted. Run 'autoenv trust {:?}' to trust it.\nThis needs to be done after each change to the file.",
113 cfg_file_path, cfg_file_path.parent().unwrap_or_else(|| Path::new("")))))
114 } else {
115 Ok(true)
116 }
117}
118
119fn local_cfg_should_be_unloaded<P: AsRef<Path>>(cfg_dir: P) -> Result<Option<PathBuf>, ShellError> {
120 let mut cfg = cfg_dir.as_ref().to_path_buf();
121 cfg.push(LOCAL_CFG_FILE_NAME);
122 if is_existent_local_cfg(&cfg)? {
123 Ok(Some(cfg))
126 } else {
127 Ok(None)
128 }
129}
130
131pub fn loadable_cfg_exists_in_dir(mut cfg_dir: PathBuf) -> Result<Option<PathBuf>, ShellError> {
136 cfg_dir.push(LOCAL_CFG_FILE_NAME);
137 let cfg_path = cfg_dir;
138
139 if !is_existent_local_cfg(&cfg_path)? {
140 return Ok(None);
141 }
142
143 let content = std::fs::read(&cfg_path)?;
144
145 if !is_trusted_local_cfg_content(&cfg_path, &content)? {
146 return Ok(None);
147 }
148
149 Ok(Some(cfg_path))
150}