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
44 let src_span = match input {
45 PipelineData::Value(Value::CellPath { val, .. }, _) => {
47 return Ok(split_cell_path(val, head)?.into_pipeline_data())
48 }
49 PipelineData::Empty => return Err(ShellError::PipelineEmpty { dst_span: head }),
50
51 PipelineData::Value(other, _) => other.span(),
54 PipelineData::ListStream(stream, ..) => stream.span(),
55 PipelineData::ByteStream(stream, ..) => stream.span(),
56 };
57 Err(ShellError::PipelineMismatch {
58 exp_input_type: "cell-path".into(),
59 dst_span: head,
60 src_span,
61 })
62 }
63
64 fn examples(&self) -> Vec<Example> {
65 vec![
66 Example {
67 description: "Split a cell-path into its components",
68 example: "$.5?.c | split cell-path",
69 result: Some(Value::test_list(vec![
70 Value::test_record(record! {
71 "value" => Value::test_int(5),
72 "optional" => Value::test_bool(true),
73 }),
74 Value::test_record(record! {
75 "value" => Value::test_string("c"),
76 "optional" => Value::test_bool(false),
77 }),
78 ])),
79 },
80 Example {
81 description: "Split a complex cell-path",
82 example: r#"$.a.b?.1."2"."c.d" | split cell-path"#,
83 result: Some(Value::test_list(vec![
84 Value::test_record(record! {
85 "value" => Value::test_string("a"),
86 "optional" => Value::test_bool(false),
87 }),
88 Value::test_record(record! {
89 "value" => Value::test_string("b"),
90 "optional" => Value::test_bool(true),
91 }),
92 Value::test_record(record! {
93 "value" => Value::test_int(1),
94 "optional" => Value::test_bool(false),
95 }),
96 Value::test_record(record! {
97 "value" => Value::test_string("2"),
98 "optional" => Value::test_bool(false),
99 }),
100 Value::test_record(record! {
101 "value" => Value::test_string("c.d"),
102 "optional" => Value::test_bool(false),
103 }),
104 ])),
105 },
106 ]
107 }
108}
109
110fn split_cell_path(val: CellPath, span: Span) -> Result<Value, ShellError> {
111 #[derive(IntoValue)]
112 struct PathMemberRecord {
113 value: Value,
114 optional: bool,
115 }
116
117 impl PathMemberRecord {
118 fn from_path_member(pm: PathMember) -> Self {
119 let (optional, internal_span) = match pm {
120 PathMember::String { optional, span, .. }
121 | PathMember::Int { optional, span, .. } => (optional, span),
122 };
123 let value = match pm {
124 PathMember::String { val, .. } => Value::string(val, internal_span),
125 PathMember::Int { val, .. } => Value::int(val as i64, internal_span),
126 };
127 Self { value, optional }
128 }
129 }
130
131 let members = val
132 .members
133 .into_iter()
134 .map(|pm| {
135 let span = match pm {
136 PathMember::String { span, .. } | PathMember::Int { span, .. } => span,
137 };
138 PathMemberRecord::from_path_member(pm).into_value(span)
139 })
140 .collect();
141
142 Ok(Value::list(members, span))
143}
144
145#[cfg(test)]
146mod test {
147 use super::*;
148
149 #[test]
150 fn test_examples() {
151 use crate::test_examples;
152
153 test_examples(SplitCellPath {})
154 }
155}