1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
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))
}