nu_cmd_lang/core_commands/
if_.rs1use nu_engine::command_prelude::*;
2use nu_protocol::{
3 engine::{CommandType, StateWorkingSet},
4 eval_const::{eval_const_subexpression, eval_constant, eval_constant_with_input},
5};
6
7#[derive(Clone)]
8pub struct If;
9
10impl Command for If {
11 fn name(&self) -> &str {
12 "if"
13 }
14
15 fn description(&self) -> &str {
16 "Conditionally run a block."
17 }
18
19 fn signature(&self) -> nu_protocol::Signature {
20 Signature::build("if")
21 .input_output_types(vec![(Type::Any, Type::Any)])
22 .required("cond", SyntaxShape::MathExpression, "Condition to check.")
23 .required(
24 "then_block",
25 SyntaxShape::Block,
26 "Block to run if check succeeds.",
27 )
28 .optional(
29 "else_expression",
30 SyntaxShape::Keyword(
31 b"else".to_vec(),
32 Box::new(SyntaxShape::OneOf(vec![
33 SyntaxShape::Block,
34 SyntaxShape::Expression,
35 ])),
36 ),
37 "Expression or block to run when the condition is false.",
38 )
39 .category(Category::Core)
40 }
41
42 fn extra_description(&self) -> &str {
43 r#"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 is_const(&self) -> bool {
52 true
53 }
54
55 fn run_const(
56 &self,
57 working_set: &StateWorkingSet,
58 call: &Call,
59 input: PipelineData,
60 ) -> Result<PipelineData, ShellError> {
61 let call = call.assert_ast_call()?;
62 let cond = call.positional_nth(0).expect("checked through parser");
63 let then_expr = call.positional_nth(1).expect("checked through parser");
64 let then_block = then_expr
65 .as_block()
66 .ok_or_else(|| ShellError::TypeMismatch {
67 err_message: "expected block".into(),
68 span: then_expr.span,
69 })?;
70 let else_case = call.positional_nth(2);
71
72 if eval_constant(working_set, cond)?.as_bool()? {
73 let block = working_set.get_block(then_block);
74 eval_const_subexpression(working_set, block, input, block.span.unwrap_or(call.head))
75 } else if let Some(else_case) = else_case {
76 if let Some(else_expr) = else_case.as_keyword() {
77 if let Some(block_id) = else_expr.as_block() {
78 let block = working_set.get_block(block_id);
79 eval_const_subexpression(
80 working_set,
81 block,
82 input,
83 block.span.unwrap_or(call.head),
84 )
85 } else {
86 eval_constant_with_input(working_set, else_expr, input)
87 }
88 } else {
89 eval_constant_with_input(working_set, else_case, input)
90 }
91 } else {
92 Ok(PipelineData::empty())
93 }
94 }
95
96 fn run(
97 &self,
98 _engine_state: &EngineState,
99 _stack: &mut Stack,
100 _call: &Call,
101 _input: PipelineData,
102 ) -> Result<PipelineData, ShellError> {
103 eprintln!(
106 "Tried to execute 'run' for the 'if' command: this code path should never be reached in IR mode"
107 );
108 unreachable!()
109 }
110
111 fn search_terms(&self) -> Vec<&str> {
112 vec!["else", "conditional"]
113 }
114
115 fn examples(&self) -> Vec<Example<'_>> {
116 vec![
117 Example {
118 description: "Output a value if a condition matches, otherwise return nothing",
119 example: "if 2 < 3 { 'yes!' }",
120 result: Some(Value::test_string("yes!")),
121 },
122 Example {
123 description: "Output a value if a condition matches, else return another value",
124 example: "if 5 < 3 { 'yes!' } else { 'no!' }",
125 result: Some(Value::test_string("no!")),
126 },
127 Example {
128 description: "Chain multiple if's together",
129 example: "if 5 < 3 { 'yes!' } else if 4 < 5 { 'no!' } else { 'okay!' }",
130 result: Some(Value::test_string("no!")),
131 },
132 ]
133 }
134}
135
136#[cfg(test)]
137mod test {
138 use super::*;
139
140 #[test]
141 fn test_examples() {
142 use crate::test_examples;
143
144 test_examples(If {})
145 }
146}