nu_command/conversions/
split_cell_path.rs1use nu_engine::command_prelude::*;
2use nu_protocol::{ast::PathMember, IntoValue};
3
4#[derive(Clone)]
5pub struct SplitCellPath;
6
7impl Command for SplitCellPath {
8 fn name(&self) -> &str {
9 "split cell-path"
10 }
11
12 fn signature(&self) -> Signature {
13 Signature::build(self.name())
14 .input_output_types(vec![
15 (Type::CellPath, Type::List(Box::new(Type::Any))),
16 (
17 Type::CellPath,
18 Type::List(Box::new(Type::Record(
19 [("value".into(), Type::Any), ("optional".into(), Type::Bool)].into(),
20 ))),
21 ),
22 ])
23 .category(Category::Conversions)
24 .allow_variants_without_examples(true)
25 }
26
27 fn description(&self) -> &str {
28 "Split a cell-path into its components."
29 }
30
31 fn search_terms(&self) -> Vec<&str> {
32 vec!["convert"]
33 }
34
35 fn run(
36 &self,
37 _engine_state: &EngineState,
38 _stack: &mut Stack,
39 call: &Call,
40 input: PipelineData,
41 ) -> Result<PipelineData, ShellError> {
42 let head = call.head;
43 let input_type = input.get_type();
44
45 let src_span = match input {
46 PipelineData::Value(Value::CellPath { val, .. }, _) => {
48 return Ok(split_cell_path(val, head)?.into_pipeline_data())
49 }
50 PipelineData::Empty => return Err(ShellError::PipelineEmpty { dst_span: head }),
51
52 PipelineData::Value(other, _) => other.span(),
55 PipelineData::ListStream(stream, ..) => stream.span(),
56 PipelineData::ByteStream(stream, ..) => stream.span(),
57 };
58 Err(ShellError::OnlySupportsThisInputType {
59 exp_input_type: "cell-path".into(),
60 wrong_type: input_type.to_string(),
61 dst_span: head,
62 src_span,
63 })
64 }
65
66 fn examples(&self) -> Vec<Example> {
67 vec![
68 Example {
69 description: "Split a cell-path into its components",
70 example: "$.5?.c | split cell-path",
71 result: Some(Value::test_list(vec![
72 Value::test_record(record! {
73 "value" => Value::test_int(5),
74 "optional" => Value::test_bool(true),
75 }),
76 Value::test_record(record! {
77 "value" => Value::test_string("c"),
78 "optional" => Value::test_bool(false),
79 }),
80 ])),
81 },
82 Example {
83 description: "Split a complex cell-path",
84 example: r#"$.a.b?.1."2"."c.d" | split cell-path"#,
85 result: Some(Value::test_list(vec![
86 Value::test_record(record! {
87 "value" => Value::test_string("a"),
88 "optional" => Value::test_bool(false),
89 }),
90 Value::test_record(record! {
91 "value" => Value::test_string("b"),
92 "optional" => Value::test_bool(true),
93 }),
94 Value::test_record(record! {
95 "value" => Value::test_int(1),
96 "optional" => Value::test_bool(false),
97 }),
98 Value::test_record(record! {
99 "value" => Value::test_string("2"),
100 "optional" => Value::test_bool(false),
101 }),
102 Value::test_record(record! {
103 "value" => Value::test_string("c.d"),
104 "optional" => Value::test_bool(false),
105 }),
106 ])),
107 },
108 ]
109 }
110}
111
112fn split_cell_path(val: CellPath, span: Span) -> Result<Value, ShellError> {
113 #[derive(IntoValue)]
114 struct PathMemberRecord {
115 value: Value,
116 optional: bool,
117 }
118
119 impl PathMemberRecord {
120 fn from_path_member(pm: PathMember) -> Self {
121 let (optional, internal_span) = match pm {
122 PathMember::String { optional, span, .. }
123 | PathMember::Int { optional, span, .. } => (optional, span),
124 };
125 let value = match pm {
126 PathMember::String { val, .. } => Value::string(val, internal_span),
127 PathMember::Int { val, .. } => Value::int(val as i64, internal_span),
128 };
129 Self { value, optional }
130 }
131 }
132
133 let members = val
134 .members
135 .into_iter()
136 .map(|pm| {
137 let span = match pm {
138 PathMember::String { span, .. } | PathMember::Int { span, .. } => span,
139 };
140 PathMemberRecord::from_path_member(pm).into_value(span)
141 })
142 .collect();
143
144 Ok(Value::list(members, span))
145}
146
147#[cfg(test)]
148mod test {
149 use super::*;
150
151 #[test]
152 fn test_examples() {
153 use crate::test_examples;
154
155 test_examples(SplitCellPath {})
156 }
157}