1use std::path::{Path, PathBuf};
2
3#[derive(Debug, Clone, Default)]
12pub struct CliOverrides {
13 pub config_home: Option<PathBuf>,
15
16 pub config_file: Option<PathBuf>,
18
19 pub env_file: Option<PathBuf>,
21
22 #[cfg(feature = "plugin")]
24 pub plugin_file: Option<PathBuf>,
25}
26
27impl CliOverrides {
28 pub fn from_path_strings(
37 config_home: Option<&str>,
38 config_file: Option<&str>,
39 env_file: Option<&str>,
40 #[cfg(feature = "plugin")] plugin_file: Option<&str>,
41 cwd: &Path,
42 ) -> Self {
43 Self {
44 config_home: config_home.map(|s| absolutize_cli_path(s, cwd)),
45 config_file: config_file.map(|s| absolutize_cli_path(s, cwd)),
46 env_file: env_file.map(|s| absolutize_cli_path(s, cwd)),
47 #[cfg(feature = "plugin")]
48 plugin_file: plugin_file.map(|s| absolutize_cli_path(s, cwd)),
49 }
50 }
51}
52
53fn absolutize_cli_path(path: &str, cwd: &Path) -> PathBuf {
60 nu_path::expand_path_with(path, cwd, true)
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66 use std::path::Component;
67
68 #[test]
69 fn relative_paths_join_cwd() {
70 let cwd = PathBuf::from("/tmp/work");
71 let cli = CliOverrides::from_path_strings(
72 Some("cfg-home"),
73 Some("config.nu"),
74 Some("env.nu"),
75 #[cfg(feature = "plugin")]
76 Some("plugin.msgpackz"),
77 &cwd,
78 );
79 assert_eq!(
80 cli.config_home.as_deref(),
81 Some(Path::new("/tmp/work/cfg-home"))
82 );
83 assert_eq!(
84 cli.config_file.as_deref(),
85 Some(Path::new("/tmp/work/config.nu"))
86 );
87 assert_eq!(cli.env_file.as_deref(), Some(Path::new("/tmp/work/env.nu")));
88 #[cfg(feature = "plugin")]
89 assert_eq!(
90 cli.plugin_file.as_deref(),
91 Some(Path::new("/tmp/work/plugin.msgpackz"))
92 );
93 }
94
95 #[test]
96 fn absolute_paths_kept() {
97 let cwd = PathBuf::from("/tmp/work");
98 let abs = if cfg!(windows) {
99 r"C:\nushell\config.nu"
100 } else {
101 "/etc/nushell/config.nu"
102 };
103 let cli = CliOverrides::from_path_strings(
104 None,
105 Some(abs),
106 None,
107 #[cfg(feature = "plugin")]
108 None,
109 &cwd,
110 );
111 assert_eq!(cli.config_file.as_deref(), Some(Path::new(abs)));
112 }
113
114 #[test]
115 fn dot_config_home_is_cwd_without_trailing_curdir() {
116 let cwd = PathBuf::from("/tmp/work");
117 let cli = CliOverrides::from_path_strings(
118 Some("."),
119 None,
120 None,
121 #[cfg(feature = "plugin")]
122 None,
123 &cwd,
124 );
125 assert_eq!(cli.config_home.as_deref(), Some(cwd.as_path()));
126 assert!(
127 !cli.config_home
128 .as_ref()
129 .unwrap()
130 .components()
131 .any(|c| matches!(c, Component::CurDir))
132 );
133 }
134
135 #[test]
136 fn relative_dot_slash_subdir_normalized() {
137 let cwd = PathBuf::from("/tmp/work");
138 let cli = CliOverrides::from_path_strings(
139 Some("./subdir"),
140 Some("./cfg.nu"),
141 None,
142 #[cfg(feature = "plugin")]
143 None,
144 &cwd,
145 );
146 assert_eq!(
147 cli.config_home.as_deref(),
148 Some(Path::new("/tmp/work/subdir"))
149 );
150 assert_eq!(
151 cli.config_file.as_deref(),
152 Some(Path::new("/tmp/work/cfg.nu"))
153 );
154 }
155
156 #[test]
157 fn parent_dir_components_resolved_lexically() {
158 let cwd = PathBuf::from("/tmp/work/nested");
159 assert_eq!(
160 absolutize_cli_path("../sibling", &cwd),
161 PathBuf::from("/tmp/work/sibling")
162 );
163 }
164
165 #[test]
166 fn tilde_config_home_expands_to_user_home_without_cwd_join() {
167 let Some(home) = dirs::home_dir() else {
168 return; };
170 let cwd = PathBuf::from("/tmp/unrelated/cwd");
171 let cli = CliOverrides::from_path_strings(
172 Some("~/.config/nushell"),
173 None,
174 None,
175 #[cfg(feature = "plugin")]
176 None,
177 &cwd,
178 );
179 let expected = home.join(".config").join("nushell");
180 assert_eq!(
181 cli.config_home.as_deref(),
182 Some(expected.as_path()),
183 "tilde should expand via $HOME, not join under cwd"
184 );
185 assert!(
187 !cli.config_home.as_ref().unwrap().starts_with(&cwd),
188 "must not resolve under cwd"
189 );
190 }
191
192 #[test]
193 fn bare_tilde_expands_to_home() {
194 let Some(home) = dirs::home_dir() else {
195 return;
196 };
197 let cwd = PathBuf::from("/tmp/unrelated");
198 assert_eq!(absolutize_cli_path("~", &cwd), home);
199 }
200}