Skip to main content

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::Table(
20                        vec![
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::CantConvert {
146            to_type: "cell path".into(),
147            from_type: "negative number".into(),
148            span,
149            help: None,
150        });
151    };
152
153    Ok(PathMember::int(val, false, span))
154}
155
156fn list_to_cell_path(vals: &[Value], span: Span) -> Result<Value, ShellError> {
157    let mut members = vec![];
158
159    for val in vals {
160        members.push(value_to_path_member(val, span)?);
161    }
162
163    let path = CellPath { members };
164
165    Ok(Value::cell_path(path, span))
166}
167
168fn record_to_path_member(
169    record: &Record,
170    val_span: Span,
171    span: Span,
172) -> Result<PathMember, ShellError> {
173    let Some(value) = record.get("value") else {
174        return Err(ShellError::CantFindColumn {
175            col_name: "value".into(),
176            span: Some(val_span),
177            src_span: span,
178        });
179    };
180
181    let mut member = value_to_path_member(value, span)?;
182
183    if let Some(optional) = record.get("optional")
184        && optional.as_bool()?
185    {
186        member.make_optional();
187    };
188
189    if let Some(insensitive) = record.get("insensitive")
190        && insensitive.as_bool()?
191    {
192        member.make_insensitive();
193    };
194
195    Ok(member)
196}
197
198fn value_to_cell_path(value: Value, span: Span) -> Result<Value, ShellError> {
199    match value {
200        Value::CellPath { .. } => Ok(value),
201        Value::Int { val, .. } => Ok(int_to_cell_path(val, span)),
202        Value::List { vals, .. } => list_to_cell_path(&vals, span),
203        other => Err(ShellError::OnlySupportsThisInputType {
204            exp_input_type: "int, list".into(),
205            wrong_type: other.get_type().to_string(),
206            dst_span: span,
207            src_span: other.span(),
208        }),
209    }
210}
211
212fn value_to_path_member(val: &Value, span: Span) -> Result<PathMember, ShellError> {
213    let val_span = val.span();
214    let member = match val {
215        Value::Int { val, .. } => int_to_path_member(*val, val_span)?,
216        Value::String { val, .. } => {
217            PathMember::string(val.into(), false, Casing::Sensitive, val_span)
218        }
219        Value::Record { val, .. } => record_to_path_member(val, val_span, span)?,
220        other => {
221            return Err(ShellError::CantConvert {
222                to_type: "int or string".to_string(),
223                from_type: other.get_type().to_string(),
224                span: val.span(),
225                help: None,
226            });
227        }
228    };
229
230    Ok(member)
231}
232
233#[cfg(test)]
234mod test {
235    use super::*;
236
237    #[test]
238    fn test_examples() -> nu_test_support::Result {
239        nu_test_support::test().examples(IntoCellPath)
240    }
241}