nu_command/conversions/into/
cell_path.rs1use nu_engine::command_prelude::*;
2use nu_protocol::{ast::PathMember, casing::Casing};
3
4#[derive(Clone)]
5pub struct IntoCellPath;
6
7impl Command for IntoCellPath {
8 fn name(&self) -> &str {
9 "into cell-path"
10 }
11
12 fn signature(&self) -> nu_protocol::Signature {
13 Signature::build("into cell-path")
14 .input_output_types(vec![
15 (Type::CellPath, Type::CellPath),
16 (Type::Int, Type::CellPath),
17 (Type::List(Box::new(Type::Any)), Type::CellPath),
18 (
19 Type::List(Box::new(Type::Record(
20 [
21 ("value".into(), Type::Any),
22 ("optional".into(), Type::Bool),
23 ("insensitive".into(), Type::Bool),
24 ]
25 .into(),
26 ))),
27 Type::CellPath,
28 ),
29 ])
30 .category(Category::Conversions)
31 .allow_variants_without_examples(true)
32 }
33
34 fn description(&self) -> &str {
35 "Convert value to a cell-path."
36 }
37
38 fn search_terms(&self) -> Vec<&str> {
39 vec!["convert"]
40 }
41
42 fn extra_description(&self) -> &str {
43 "Converting a string directly into a cell path is intentionally not supported."
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 into_cell_path(call, input)
54 }
55
56 fn examples(&self) -> Vec<Example> {
57 vec![
58 Example {
59 description: "Convert integer into cell path",
60 example: "5 | into cell-path",
61 result: Some(Value::test_cell_path(CellPath {
62 members: vec![PathMember::test_int(5, false)],
63 })),
64 },
65 Example {
66 description: "Convert cell path into cell path",
67 example: "5 | into cell-path | into cell-path",
68 result: Some(Value::test_cell_path(CellPath {
69 members: vec![PathMember::test_int(5, false)],
70 })),
71 },
72 Example {
73 description: "Convert string into cell path",
74 example: "'some.path' | split row '.' | into cell-path",
75 result: Some(Value::test_cell_path(CellPath {
76 members: vec![
77 PathMember::test_string("some".into(), false, Casing::Sensitive),
78 PathMember::test_string("path".into(), false, Casing::Sensitive),
79 ],
80 })),
81 },
82 Example {
83 description: "Convert list into cell path",
84 example: "[5 c 7 h] | into cell-path",
85 result: Some(Value::test_cell_path(CellPath {
86 members: vec![
87 PathMember::test_int(5, false),
88 PathMember::test_string("c".into(), false, Casing::Sensitive),
89 PathMember::test_int(7, false),
90 PathMember::test_string("h".into(), false, Casing::Sensitive),
91 ],
92 })),
93 },
94 Example {
95 description: "Convert table into cell path",
96 example: "[[value, optional, insensitive]; [5 true false] [c false false] [d false true]] | into cell-path",
97 result: Some(Value::test_cell_path(CellPath {
98 members: vec![
99 PathMember::test_int(5, true),
100 PathMember::test_string("c".into(), false, Casing::Sensitive),
101 PathMember::test_string("d".into(), false, Casing::Insensitive),
102 ],
103 })),
104 },
105 ]
106 }
107}
108
109fn into_cell_path(call: &Call, input: PipelineData) -> Result<PipelineData, ShellError> {
110 let head = call.head;
111
112 match input {
113 PipelineData::Value(value, _) => Ok(value_to_cell_path(value, head)?.into_pipeline_data()),
114 PipelineData::ListStream(stream, ..) => {
115 let list: Vec<_> = stream.into_iter().collect();
116 Ok(list_to_cell_path(&list, head)?.into_pipeline_data())
117 }
118 PipelineData::ByteStream(stream, ..) => Err(ShellError::OnlySupportsThisInputType {
119 exp_input_type: "list, int".into(),
120 wrong_type: stream.type_().describe().into(),
121 dst_span: head,
122 src_span: stream.span(),
123 }),
124 PipelineData::Empty => Err(ShellError::PipelineEmpty { dst_span: head }),
125 }
126}
127
128fn int_to_cell_path(val: i64, span: Span) -> Value {
129 let member = match int_to_path_member(val, span) {
130 Ok(m) => m,
131 Err(e) => {
132 return Value::error(e, span);
133 }
134 };
135
136 let path = CellPath {
137 members: vec![member],
138 };
139
140 Value::cell_path(path, span)
141}
142
143fn int_to_path_member(val: i64, span: Span) -> Result<PathMember, ShellError> {
144 let Ok(val) = val.try_into() else {
145 return Err(ShellError::NeedsPositiveValue { span });
146 };
147
148 Ok(PathMember::int(val, false, span))
149}
150
151fn list_to_cell_path(vals: &[Value], span: Span) -> Result<Value, ShellError> {
152 let mut members = vec![];
153
154 for val in vals {
155 members.push(value_to_path_member(val, span)?);
156 }
157
158 let path = CellPath { members };
159
160 Ok(Value::cell_path(path, span))
161}
162
163fn record_to_path_member(
164 record: &Record,
165 val_span: Span,
166 span: Span,
167) -> Result<PathMember, ShellError> {
168 let Some(value) = record.get("value") else {
169 return Err(ShellError::CantFindColumn {
170 col_name: "value".into(),
171 span: Some(val_span),
172 src_span: span,
173 });
174 };
175
176 let mut member = value_to_path_member(value, span)?;
177
178 if let Some(optional) = record.get("optional") {
179 if optional.as_bool()? {
180 member.make_optional();
181 }
182 };
183
184 if let Some(insensitive) = record.get("insensitive") {
185 if insensitive.as_bool()? {
186 member.make_insensitive();
187 }
188 };
189
190 Ok(member)
191}
192
193fn value_to_cell_path(value: Value, span: Span) -> Result<Value, ShellError> {
194 match value {
195 Value::CellPath { .. } => Ok(value),
196 Value::Int { val, .. } => Ok(int_to_cell_path(val, span)),
197 Value::List { vals, .. } => list_to_cell_path(&vals, span),
198 other => Err(ShellError::OnlySupportsThisInputType {
199 exp_input_type: "int, list".into(),
200 wrong_type: other.get_type().to_string(),
201 dst_span: span,
202 src_span: other.span(),
203 }),
204 }
205}
206
207fn value_to_path_member(val: &Value, span: Span) -> Result<PathMember, ShellError> {
208 let val_span = val.span();
209 let member = match val {
210 Value::Int { val, .. } => int_to_path_member(*val, val_span)?,
211 Value::String { val, .. } => {
212 PathMember::string(val.into(), false, Casing::Sensitive, val_span)
213 }
214 Value::Record { val, .. } => record_to_path_member(val, val_span, span)?,
215 other => {
216 return Err(ShellError::CantConvert {
217 to_type: "int or string".to_string(),
218 from_type: other.get_type().to_string(),
219 span: val.span(),
220 help: None,
221 });
222 }
223 };
224
225 Ok(member)
226}
227
228#[cfg(test)]
229mod test {
230 use super::*;
231
232 #[test]
233 fn test_examples() {
234 use crate::test_examples;
235
236 test_examples(IntoCellPath {})
237 }
238}