nu_command/env/
source_env.rs1use nu_engine::{
2 command_prelude::*, find_in_dirs_env, get_dirs_var_from_call, get_eval_block_with_early_return,
3 redirect_env,
4};
5use nu_protocol::{
6 BlockId,
7 engine::CommandType,
8 shell_error::{self, io::IoError},
9};
10use std::path::PathBuf;
11
12#[derive(Clone)]
14pub struct SourceEnv;
15
16impl Command for SourceEnv {
17 fn name(&self) -> &str {
18 "source-env"
19 }
20
21 fn signature(&self) -> Signature {
22 Signature::build("source-env")
23 .input_output_types(vec![(Type::Any, Type::Any)])
24 .param(Parameter::Required(
25 PositionalArg::new(
26 "filename",
27 SyntaxShape::OneOf(vec![SyntaxShape::String, SyntaxShape::Nothing]),
29 )
30 .desc("The filepath to the script file to source the environment from (`null` for no-op).")
31 .completion(Completion::Builtin(BuiltinCompletion::NuFile {
32 std_virtual_path: false,
33 })),
34 ))
35 .category(Category::Core)
36 }
37
38 fn description(&self) -> &str {
39 "Source the environment from a source file into the current environment."
40 }
41
42 fn extra_description(&self) -> &str {
43 "This command is a parser keyword. For details, check:
44 https://www.nushell.sh/book/thinking_in_nu.html"
45 }
46
47 fn command_type(&self) -> CommandType {
48 CommandType::Keyword
49 }
50
51 fn run(
52 &self,
53 engine_state: &EngineState,
54 caller_stack: &mut Stack,
55 call: &Call,
56 input: PipelineData,
57 ) -> Result<PipelineData, ShellError> {
58 if call.get_parser_info(caller_stack, "noop").is_some() {
59 return Ok(PipelineData::empty());
60 }
61
62 let source_filename: Spanned<String> = call.req(engine_state, caller_stack, 0)?;
63
64 let block_id: i64 = call.req_parser_info(engine_state, caller_stack, "block_id")?;
67 let block_id = BlockId::new(block_id as usize);
68
69 let file_path = if let Some(path) = find_in_dirs_env(
71 &source_filename.item,
72 engine_state,
73 caller_stack,
74 get_dirs_var_from_call(caller_stack, call),
75 )? {
76 PathBuf::from(&path)
77 } else {
78 return Err(ShellError::Io(IoError::new(
79 shell_error::io::ErrorKind::FileNotFound,
80 source_filename.span,
81 PathBuf::from(source_filename.item),
82 )));
83 };
84
85 if let Some(parent) = file_path.parent() {
86 let file_pwd = Value::string(parent.to_string_lossy(), call.head);
87
88 caller_stack.add_env_var("FILE_PWD".to_string(), file_pwd);
89 }
90
91 caller_stack.add_env_var(
92 "CURRENT_FILE".to_string(),
93 Value::string(file_path.to_string_lossy(), call.head),
94 );
95
96 let block = engine_state.get_block(block_id).clone();
98 let mut callee_stack = caller_stack
99 .gather_captures(engine_state, &block.captures)
100 .reset_pipes();
101
102 let eval_block_with_early_return = get_eval_block_with_early_return(engine_state);
103
104 let result = eval_block_with_early_return(engine_state, &mut callee_stack, &block, input)
105 .map(|p| p.body);
106
107 redirect_env(engine_state, caller_stack, &callee_stack);
109
110 caller_stack.remove_env_var(engine_state, "FILE_PWD");
112 caller_stack.remove_env_var(engine_state, "CURRENT_FILE");
113
114 result
115 }
116
117 fn examples(&self) -> Vec<Example<'_>> {
118 vec![
119 Example {
120 description: "Sources the environment from foo.nu in the current context.",
121 example: "source-env foo.nu",
122 result: None,
123 },
124 Example {
125 description: "Sourcing `null` is a no-op.",
126 example: "source-env null",
127 result: None,
128 },
129 ]
130 }
131}