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::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}