nu_cmd_lang/core_commands/
mut_.rs

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
use nu_engine::{command_prelude::*, get_eval_block};
use nu_protocol::engine::CommandType;

#[derive(Clone)]
pub struct Mut;

impl Command for Mut {
    fn name(&self) -> &str {
        "mut"
    }

    fn description(&self) -> &str {
        "Create a mutable variable and give it a value."
    }

    fn signature(&self) -> nu_protocol::Signature {
        Signature::build("mut")
            .input_output_types(vec![(Type::Any, Type::Nothing)])
            .allow_variants_without_examples(true)
            .required("var_name", SyntaxShape::VarWithOptType, "Variable name.")
            .required(
                "initial_value",
                SyntaxShape::Keyword(b"=".to_vec(), Box::new(SyntaxShape::MathExpression)),
                "Equals sign followed by value.",
            )
            .category(Category::Core)
    }

    fn extra_description(&self) -> &str {
        r#"This command is a parser keyword. For details, check:
  https://www.nushell.sh/book/thinking_in_nu.html"#
    }

    fn command_type(&self) -> CommandType {
        CommandType::Keyword
    }

    fn search_terms(&self) -> Vec<&str> {
        vec!["set", "mutable"]
    }

    fn run(
        &self,
        engine_state: &EngineState,
        stack: &mut Stack,
        call: &Call,
        input: PipelineData,
    ) -> Result<PipelineData, ShellError> {
        // This is compiled specially by the IR compiler. The code here is never used when
        // running in IR mode.
        let call = call.assert_ast_call()?;
        let var_id = call
            .positional_nth(0)
            .expect("checked through parser")
            .as_var()
            .expect("internal error: missing variable");

        let block_id = call
            .positional_nth(1)
            .expect("checked through parser")
            .as_block()
            .expect("internal error: missing right hand side");

        let block = engine_state.get_block(block_id);
        let eval_block = get_eval_block(engine_state);
        let stack = &mut stack.start_collect_value();
        let pipeline_data = eval_block(engine_state, stack, block, input)?;
        let value = pipeline_data.into_value(call.head)?;

        // if given variable type is Glob, and our result is string
        // then nushell need to convert from Value::String to Value::Glob
        // it's assigned by demand, then it's not quoted, and it's required to expand
        // if we pass it to other commands.
        let var_type = &engine_state.get_var(var_id).ty;
        let val_span = value.span();
        let value = match value {
            Value::String { val, .. } if var_type == &Type::Glob => {
                Value::glob(val, false, val_span)
            }
            value => value,
        };

        stack.add_var(var_id, value);
        Ok(PipelineData::empty())
    }

    fn examples(&self) -> Vec<Example> {
        vec![
            Example {
                description: "Set a mutable variable to a value, then update it",
                example: "mut x = 10; $x = 12",
                result: None,
            },
            Example {
                description: "Upsert a value inside a mutable data structure",
                example: "mut a = {b:{c:1}}; $a.b.c = 2",
                result: None,
            },
            Example {
                description: "Set a mutable variable to the result of an expression",
                example: "mut x = 10 + 100",
                result: None,
            },
            Example {
                description: "Set a mutable variable based on the condition",
                example: "mut x = if false { -1 } else { 1 }",
                result: None,
            },
        ]
    }
}

#[cfg(test)]
mod test {
    use nu_protocol::engine::CommandType;

    use super::*;

    #[test]
    fn test_examples() {
        use crate::test_examples;

        test_examples(Mut {})
    }

    #[test]
    fn test_command_type() {
        assert!(matches!(Mut.command_type(), CommandType::Keyword));
    }
}