nu_command/conversions/into/
bool.rs1use nu_cmd_base::input_handler::{CmdArgument, operate};
2use nu_engine::command_prelude::*;
3
4#[derive(Clone)]
5pub struct IntoBool;
6
7impl Command for IntoBool {
8 fn name(&self) -> &str {
9 "into bool"
10 }
11
12 fn signature(&self) -> Signature {
13 Signature::build("into bool")
14 .input_output_types(vec![
15 (Type::Int, Type::Bool),
16 (Type::Number, Type::Bool),
17 (Type::String, Type::Bool),
18 (Type::Bool, Type::Bool),
19 (Type::Nothing, Type::Bool),
20 (Type::List(Box::new(Type::Any)), Type::table()),
21 (Type::table(), Type::table()),
22 (Type::record(), Type::record()),
23 ])
24 .switch(
25 "relaxed",
26 "Relaxes conversion to also allow null and any strings.",
27 None,
28 )
29 .allow_variants_without_examples(true)
30 .rest(
31 "rest",
32 SyntaxShape::CellPath,
33 "For a data structure input, convert data at the given cell paths.",
34 )
35 .category(Category::Conversions)
36 }
37
38 fn description(&self) -> &str {
39 "Convert value to boolean."
40 }
41
42 fn search_terms(&self) -> Vec<&str> {
43 vec!["convert", "boolean", "true", "false", "1", "0"]
44 }
45
46 fn run(
47 &self,
48 engine_state: &EngineState,
49 stack: &mut Stack,
50 call: &Call,
51 input: PipelineData,
52 ) -> Result<PipelineData, ShellError> {
53 let relaxed = call
54 .has_flag(engine_state, stack, "relaxed")
55 .unwrap_or(false);
56 into_bool(engine_state, stack, call, input, relaxed)
57 }
58
59 fn examples(&self) -> Vec<Example<'_>> {
60 vec![
61 Example {
62 description: "Convert value to boolean in table",
63 example: "[[value]; ['false'] ['1'] [0] [1.0] [true]] | into bool value",
64 result: Some(Value::test_list(vec![
65 Value::test_record(record! {
66 "value" => Value::test_bool(false),
67 }),
68 Value::test_record(record! {
69 "value" => Value::test_bool(true),
70 }),
71 Value::test_record(record! {
72 "value" => Value::test_bool(false),
73 }),
74 Value::test_record(record! {
75 "value" => Value::test_bool(true),
76 }),
77 Value::test_record(record! {
78 "value" => Value::test_bool(true),
79 }),
80 ])),
81 },
82 Example {
83 description: "Convert bool to boolean",
84 example: "true | into bool",
85 result: Some(Value::test_bool(true)),
86 },
87 Example {
88 description: "convert int to boolean",
89 example: "1 | into bool",
90 result: Some(Value::test_bool(true)),
91 },
92 Example {
93 description: "convert float to boolean",
94 example: "0.3 | into bool",
95 result: Some(Value::test_bool(true)),
96 },
97 Example {
98 description: "convert float string to boolean",
99 example: "'0.0' | into bool",
100 result: Some(Value::test_bool(false)),
101 },
102 Example {
103 description: "convert string to boolean",
104 example: "'true' | into bool",
105 result: Some(Value::test_bool(true)),
106 },
107 Example {
108 description: "interpret a null as false",
109 example: "null | into bool --relaxed",
110 result: Some(Value::test_bool(false)),
111 },
112 Example {
113 description: "interpret any non-false, non-zero string as true",
114 example: "'something' | into bool --relaxed",
115 result: Some(Value::test_bool(true)),
116 },
117 ]
118 }
119}
120
121struct IntoBoolCmdArgument {
122 cell_paths: Option<Vec<CellPath>>,
123 relaxed: bool,
124}
125
126impl CmdArgument for IntoBoolCmdArgument {
127 fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
128 self.cell_paths.take()
129 }
130}
131
132fn into_bool(
133 engine_state: &EngineState,
134 stack: &mut Stack,
135 call: &Call,
136 input: PipelineData,
137 relaxed: bool,
138) -> Result<PipelineData, ShellError> {
139 let cell_paths = Some(call.rest(engine_state, stack, 0)?).filter(|v| !v.is_empty());
140 let args = IntoBoolCmdArgument {
141 cell_paths,
142 relaxed,
143 };
144 operate(action, args, input, call.head, engine_state.signals())
145}
146
147fn strict_string_to_boolean(s: &str, span: Span) -> Result<bool, ShellError> {
148 match s.trim().to_ascii_lowercase().as_str() {
149 "true" => Ok(true),
150 "false" => Ok(false),
151 o => {
152 let val = o.parse::<f64>();
153 match val {
154 Ok(f) => Ok(f != 0.0),
155 Err(_) => Err(ShellError::CantConvert {
156 to_type: "boolean".to_string(),
157 from_type: "string".to_string(),
158 span,
159 help: Some(
160 r#"the strings "true" and "false" can be converted into a bool"#
161 .to_string(),
162 ),
163 }),
164 }
165 }
166 }
167}
168
169fn action(input: &Value, args: &IntoBoolCmdArgument, span: Span) -> Value {
170 let err = || {
171 Value::error(
172 ShellError::OnlySupportsThisInputType {
173 exp_input_type: "bool, int, float or string".into(),
174 wrong_type: input.get_type().to_string(),
175 dst_span: span,
176 src_span: input.span(),
177 },
178 span,
179 )
180 };
181
182 match (input, args.relaxed) {
183 (Value::Error { .. } | Value::Bool { .. }, _) => input.clone(),
184 (Value::Nothing { .. }, false) => err(),
186 (Value::String { val, .. }, false) => match strict_string_to_boolean(val, span) {
187 Ok(val) => Value::bool(val, span),
188 Err(error) => Value::error(error, span),
189 },
190 _ => match input.coerce_bool() {
191 Ok(val) => Value::bool(val, span),
192 Err(_) => err(),
193 },
194 }
195}
196
197#[cfg(test)]
198mod test {
199 use super::*;
200
201 #[test]
202 fn test_examples() {
203 use crate::test_examples;
204
205 test_examples(IntoBool {})
206 }
207
208 #[test]
209 fn test_strict_handling() {
210 let span = Span::test_data();
211 let args = IntoBoolCmdArgument {
212 cell_paths: vec![].into(),
213 relaxed: false,
214 };
215
216 assert!(action(&Value::test_nothing(), &args, span).is_error());
217 assert!(action(&Value::test_string("abc"), &args, span).is_error());
218 assert!(action(&Value::test_string("true"), &args, span).is_true());
219 assert!(action(&Value::test_string("FALSE"), &args, span).is_false());
220 }
221
222 #[test]
223 fn test_relaxed_handling() {
224 let span = Span::test_data();
225 let args = IntoBoolCmdArgument {
226 cell_paths: vec![].into(),
227 relaxed: true,
228 };
229
230 assert!(action(&Value::test_nothing(), &args, span).is_false());
231 assert!(action(&Value::test_string("abc"), &args, span).is_true());
232 assert!(action(&Value::test_string("true"), &args, span).is_true());
233 assert!(action(&Value::test_string("FALSE"), &args, span).is_false());
234 }
235}