nu_command/conversions/into/
cell_path.rs

1use 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}