nu_cmd_lang/core_commands/attr/
example.rs1use nu_engine::command_prelude::*;
2
3#[derive(Clone)]
4pub struct AttrExample;
5
6impl Command for AttrExample {
7 fn name(&self) -> &str {
8 "attr example"
9 }
10
11 fn signature(&self) -> Signature {
14 Signature::build("attr example")
15 .input_output_types(vec![(
16 Type::Nothing,
17 Type::Record(
18 [
19 ("description".into(), Type::String),
20 ("example".into(), Type::String),
21 ]
22 .into(),
23 ),
24 )])
25 .allow_variants_without_examples(true)
26 .required(
27 "description",
28 SyntaxShape::String,
29 "Description of the example.",
30 )
31 .required(
32 "example",
33 SyntaxShape::OneOf(vec![SyntaxShape::Block, SyntaxShape::String]),
34 "Example code snippet.",
35 )
36 .named(
37 "result",
38 SyntaxShape::Any,
39 "Expected output of example.",
40 None,
41 )
42 .category(Category::Core)
43 }
44
45 fn description(&self) -> &str {
46 "Attribute for adding examples to custom commands."
47 }
48
49 fn run(
50 &self,
51 engine_state: &EngineState,
52 stack: &mut Stack,
53 call: &Call,
54 _input: PipelineData,
55 ) -> Result<PipelineData, ShellError> {
56 let description: Spanned<String> = call.req(engine_state, stack, 0)?;
57 let result: Option<Value> = call.get_flag(engine_state, stack, "result")?;
58
59 let example_string: Result<String, _> = call.req(engine_state, stack, 1);
60 let example_expr = call
61 .positional_nth(stack, 1)
62 .ok_or(ShellError::MissingParameter {
63 param_name: "example".into(),
64 span: call.head,
65 })?;
66
67 let working_set = StateWorkingSet::new(engine_state);
68
69 attr_example_impl(
70 example_expr,
71 example_string,
72 &working_set,
73 call,
74 description,
75 result,
76 )
77 }
78
79 fn run_const(
80 &self,
81 working_set: &StateWorkingSet,
82 call: &Call,
83 _input: PipelineData,
84 ) -> Result<PipelineData, ShellError> {
85 let description: Spanned<String> = call.req_const(working_set, 0)?;
86 let result: Option<Value> = call.get_flag_const(working_set, "result")?;
87
88 let example_string: Result<String, _> = call.req_const(working_set, 1);
89 let example_expr = call.assert_ast_call()?.positional_iter().nth(1).ok_or(
90 ShellError::MissingParameter {
91 param_name: "example".into(),
92 span: call.head,
93 },
94 )?;
95
96 attr_example_impl(
97 example_expr,
98 example_string,
99 working_set,
100 call,
101 description,
102 result,
103 )
104 }
105
106 fn is_const(&self) -> bool {
107 true
108 }
109
110 fn requires_ast_for_arguments(&self) -> bool {
111 true
112 }
113
114 fn examples(&self) -> Vec<Example<'_>> {
115 vec![Example {
116 description: "Add examples to custom command.",
117 example: r###"# Double numbers
118 @example "double an int" { 2 | double } --result 4
119 @example "double a float" { 0.25 | double } --result 0.5
120 def double []: [number -> number] { $in * 2 }"###,
121 result: None,
122 }]
123 }
124}
125
126fn attr_example_impl(
127 example_expr: &nu_protocol::ast::Expression,
128 example_string: Result<String, ShellError>,
129 working_set: &StateWorkingSet<'_>,
130 call: &Call<'_>,
131 description: Spanned<String>,
132 result: Option<Value>,
133) -> Result<PipelineData, ShellError> {
134 let example_content = match example_expr.as_block() {
135 Some(block_id) => {
136 let block = working_set.get_block(block_id);
137 let contents =
138 working_set.get_span_contents(block.span.expect("a block must have a span"));
139 let contents = contents
140 .strip_prefix(b"{")
141 .and_then(|x| x.strip_suffix(b"}"))
142 .unwrap_or(contents)
143 .trim_ascii();
144 String::from_utf8_lossy(contents).into_owned()
145 }
146 None => example_string?,
147 };
148
149 let mut rec = record! {
150 "description" => Value::string(description.item, description.span),
151 "example" => Value::string(example_content, example_expr.span),
152 };
153 if let Some(result) = result {
154 rec.push("result", result);
155 }
156
157 Ok(Value::record(rec, call.head).into_pipeline_data())
158}