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