1use std::borrow::Cow;
2
3use bexpand::Expression;
4use nu_plugin::{EngineInterface, EvaluatedCall, Plugin, PluginCommand, SimplePluginCommand};
5use nu_protocol::{Category, ErrorLabel, Example, LabeledError, Signature, Span, Type, Value};
6
7fn bexpand(call: &EvaluatedCall, input: &Value) -> Result<Value, LabeledError> {
8 let span = input.span();
9 let output: Result<Vec<Cow<str>>, _> = match input {
10 Value::String { val, .. } => {
11 let val = val.as_str();
12 let expression: Expression = match val.try_into() {
13 Ok(e) => e,
14 Err(s) => {
15 return Err(LabeledError {
16 labels: Box::new(vec![ErrorLabel {
17 text: "Brace expression failed to parse".into(),
18 span: span,
19 }]),
20 msg: s,
21 code: None,
22 url: None,
23 help: None,
24 inner: Box::new(vec![]),
25 });
26 }
27 };
28
29 expression
30 .into_iter()
31 .map(|value| value.map_err(move |e| (e, span)))
32 .collect()
33 }
34 Value::List { vals, .. } => {
35 let exprs: Result<Vec<_>, _> = vals
36 .into_iter()
37 .map(|val| match val {
38 Value::String {
39 val, internal_span, ..
40 } => {
41 let val = val.as_str();
42 let expression: Expression = match val.try_into() {
43 Ok(e) => e,
44 Err(s) => {
45 return Err(LabeledError {
46 labels: Box::new(vec![ErrorLabel {
47 text: "Brace expression failed to parse".into(),
48 span: *internal_span,
49 }]),
50 msg: s,
51 code: None,
52 url: None,
53 help: None,
54 inner: Box::new(vec![]),
55 });
56 }
57 };
58
59 Ok(expression
60 .into_iter()
61 .map(move |value| value.map_err(move |e| (e, *internal_span))))
62 }
63 v => {
64 return Err(LabeledError {
65 labels: Box::new(vec![ErrorLabel {
66 text: "Input must be string".into(),
67 span: v.span(),
68 }]),
69 msg: format!("Input type was {}", input.get_type()),
70 code: None,
71 url: None,
72 help: None,
73 inner: Box::new(vec![]),
74 });
75 }
76 })
77 .collect();
78 exprs?.into_iter().flatten().collect()
79 }
80
81 v => {
82 return Err(LabeledError {
83 labels: Box::new(vec![ErrorLabel {
84 text: "Input must be string".into(),
85 span: v.span(),
86 }]),
87 msg: format!("Input type was {}", input.get_type()),
88 code: None,
89 url: None,
90 help: None,
91 inner: Box::new(vec![]),
92 });
93 }
94 };
95
96 let output = match output {
97 Ok(o) => o
98 .into_iter()
99 .map(|s| Value::string(s.into_owned(), call.head))
100 .collect(),
101 Err((e, span)) => {
102 return Err(LabeledError {
103 labels: Box::new(vec![ErrorLabel {
104 text: "Expression failed to generate".into(),
105 span,
106 }]),
107 msg: e.to_string(),
108 code: None,
109 url: None,
110 help: None,
111 inner: Box::new(vec![]),
112 });
113 }
114 };
115
116 Ok(Value::list(output, call.head))
117}
118
119pub struct BexpandPlugin;
120
121impl Plugin for BexpandPlugin {
122 fn commands(&self) -> Vec<Box<dyn PluginCommand<Plugin = Self>>> {
123 vec![Box::new(Bexpand)]
124 }
125
126 fn version(&self) -> String {
127 env!("CARGO_PKG_VERSION").into()
128 }
129}
130
131pub struct Bexpand;
132
133impl SimplePluginCommand for Bexpand {
134 type Plugin = BexpandPlugin;
135
136 fn signature(&self) -> Signature {
137 Signature::new("str bexpand")
138 .input_output_types(vec![
139 (Type::String, Type::List(Box::new(Type::String))),
140 (
141 Type::List(Box::new(Type::String)),
142 Type::List(Box::new(Type::String)),
143 ),
144 ])
145 .description("Bash-style brace expansion")
146 .category(Category::Strings)
147 }
148
149 fn run(
150 &self,
151 _plugin: &Self::Plugin,
152 _engine: &EngineInterface,
153 call: &EvaluatedCall,
154 input: &Value,
155 ) -> Result<Value, LabeledError> {
156 bexpand(call, input)
158 }
159
160 fn name(&self) -> &str {
161 "str bexpand"
162 }
163
164 fn description(&self) -> &str {
165 "Does bash-style brace expansion"
166 }
167 fn examples(&self) -> Vec<Example<'_>> {
168 vec![Example {
169 example: "'~/.config/nushell/{env,config,plugin}.nu' | str bexpand".into(),
170 description: "Get a list of standard nushell config items".into(),
171 result: Some(Value::list(
172 vec![
173 Value::string("~/.config/nushell/env.nu", Span::new(0, 0)),
174 Value::string("~/.config/nushell/config.nu", Span::new(0, 0)),
175 Value::string("~/.config/nushell/plugin.nu", Span::new(0, 0)),
176 ],
177 Span::new(0, 0),
178 )),
179 }]
180 }
181}