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