nu_cmd_lang/core_commands/overlay/
use_.rs1use nu_engine::{
2 command_prelude::*, find_in_dirs_env, get_dirs_var_from_call, get_eval_block, redirect_env,
3};
4use nu_parser::trim_quotes_str;
5use nu_protocol::{ModuleId, ast::Expr, engine::CommandType};
6
7use std::path::Path;
8
9#[derive(Clone)]
10pub struct OverlayUse;
11
12impl Command for OverlayUse {
13 fn name(&self) -> &str {
14 "overlay use"
15 }
16
17 fn description(&self) -> &str {
18 "Use definitions from a module as an overlay."
19 }
20
21 fn signature(&self) -> nu_protocol::Signature {
22 Signature::build("overlay use")
23 .input_output_types(vec![(Type::Nothing, Type::Nothing)])
24 .allow_variants_without_examples(true)
25 .required(
26 "name",
27 SyntaxShape::OneOf(vec![SyntaxShape::String, SyntaxShape::Nothing]),
28 "Module name to use overlay for (`null` for no-op).",
29 )
30 .optional(
31 "as",
32 SyntaxShape::Keyword(b"as".to_vec(), Box::new(SyntaxShape::String)),
33 "`as` keyword followed by a new name.",
34 )
35 .switch(
36 "prefix",
37 "Prepend module name to the imported commands and aliases",
38 Some('p'),
39 )
40 .switch(
41 "reload",
42 "If the overlay already exists, reload its definitions and environment.",
43 Some('r'),
44 )
45 .category(Category::Core)
46 }
47
48 fn extra_description(&self) -> &str {
49 r#"This command is a parser keyword. For details, check:
50 https://www.nushell.sh/book/thinking_in_nu.html"#
51 }
52
53 fn command_type(&self) -> CommandType {
54 CommandType::Keyword
55 }
56
57 fn run(
58 &self,
59 engine_state: &EngineState,
60 caller_stack: &mut Stack,
61 call: &Call,
62 input: PipelineData,
63 ) -> Result<PipelineData, ShellError> {
64 let noop = call.get_parser_info(caller_stack, "noop");
65 if noop.is_some() {
66 return Ok(PipelineData::empty());
67 }
68
69 let name_arg: Spanned<String> = call.req(engine_state, caller_stack, 0)?;
70 let name_arg_item = trim_quotes_str(&name_arg.item);
71
72 let maybe_origin_module_id: Option<ModuleId> =
73 if let Some(overlay_expr) = call.get_parser_info(caller_stack, "overlay_expr") {
74 if let Expr::Overlay(module_id) = &overlay_expr.expr {
75 *module_id
76 } else {
77 return Err(ShellError::NushellFailedSpanned {
78 msg: "Not an overlay".to_string(),
79 label: "requires an overlay (path or a string)".to_string(),
80 span: overlay_expr.span,
81 });
82 }
83 } else {
84 return Err(ShellError::NushellFailedSpanned {
85 msg: "Missing positional".to_string(),
86 label: "missing required overlay".to_string(),
87 span: call.head,
88 });
89 };
90
91 let overlay_name = if let Some(name) = call.opt(engine_state, caller_stack, 1)? {
92 name
93 } else if engine_state
94 .find_overlay(name_arg_item.as_bytes())
95 .is_some()
96 {
97 name_arg_item.to_string()
98 } else if let Some(os_str) = Path::new(name_arg_item).file_stem() {
99 if let Some(name) = os_str.to_str() {
100 name.to_string()
101 } else {
102 return Err(ShellError::NonUtf8 {
103 span: name_arg.span,
104 });
105 }
106 } else {
107 return Err(ShellError::OverlayNotFoundAtRuntime {
108 overlay_name: (name_arg_item.to_string()),
109 span: name_arg.span,
110 });
111 };
112
113 if let Some(module_id) = maybe_origin_module_id {
114 let module = engine_state.get_module(module_id);
119 let cwd = caller_stack.get_env_var(engine_state, "PWD").cloned();
121
122 if let Some(block_id) = module.env_block {
124 let maybe_file_path_or_dir = find_in_dirs_env(
125 name_arg_item,
126 engine_state,
127 caller_stack,
128 get_dirs_var_from_call(caller_stack, call),
129 )?;
130 let block = engine_state.get_block(block_id);
131 let mut callee_stack = caller_stack
132 .gather_captures(engine_state, &block.captures)
133 .reset_pipes();
134
135 if let Some(path) = &maybe_file_path_or_dir {
136 let parent = if path.is_dir() {
138 path.clone()
139 } else {
140 let mut parent = path.clone();
141 parent.pop();
142 parent
143 };
144 let file_pwd = Value::string(parent.to_string_lossy(), call.head);
145
146 callee_stack.add_env_var("FILE_PWD".to_string(), file_pwd);
147 }
148
149 if let Some(path) = &maybe_file_path_or_dir {
150 let module_file_path = if path.is_dir() {
151 Value::string(path.join("mod.nu").to_string_lossy(), call.head)
154 } else {
155 Value::string(path.to_string_lossy(), call.head)
156 };
157 callee_stack.add_env_var("CURRENT_FILE".to_string(), module_file_path);
158 }
159
160 let eval_block = get_eval_block(engine_state);
161 let _ = eval_block(engine_state, &mut callee_stack, block, input)?;
162
163 caller_stack.add_overlay(overlay_name);
165 if let Some(cwd) = cwd {
167 caller_stack.add_env_var("PWD".to_string(), cwd);
168 }
169
170 redirect_env(engine_state, caller_stack, &callee_stack);
172 } else {
173 caller_stack.add_overlay(overlay_name);
174 if let Some(cwd) = cwd {
176 caller_stack.add_env_var("PWD".to_string(), cwd);
177 }
178 }
179 } else {
180 caller_stack.add_overlay(overlay_name);
181 caller_stack.update_config(engine_state)?;
182 }
183
184 Ok(PipelineData::empty())
185 }
186
187 fn examples(&self) -> Vec<Example<'_>> {
188 vec![
189 Example {
190 description: "Create an overlay from a module",
191 example: r#"module spam { export def foo [] { "foo" } }
192 overlay use spam
193 foo"#,
194 result: None,
195 },
196 Example {
197 description: "Create an overlay from a module and rename it",
198 example: r#"module spam { export def foo [] { "foo" } }
199 overlay use spam as spam_new
200 foo"#,
201 result: None,
202 },
203 Example {
204 description: "Create an overlay with a prefix",
205 example: r#"'export def foo { "foo" }'
206 overlay use --prefix spam
207 spam foo"#,
208 result: None,
209 },
210 Example {
211 description: "Create an overlay from a file",
212 example: r#"'export-env { $env.FOO = "foo" }' | save spam.nu
213 overlay use spam.nu
214 $env.FOO"#,
215 result: None,
216 },
217 ]
218 }
219}
220
221#[cfg(test)]
222mod test {
223 use super::*;
224
225 #[test]
226 fn test_examples() {
227 use crate::test_examples;
228
229 test_examples(OverlayUse {})
230 }
231}