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 =
90 call.assert_ast_call()?
91 .positional_nth(1)
92 .ok_or(ShellError::MissingParameter {
93 param_name: "example".into(),
94 span: call.head,
95 })?;
96
97 attr_example_impl(
98 example_expr,
99 example_string,
100 working_set,
101 call,
102 description,
103 result,
104 )
105 }
106
107 fn is_const(&self) -> bool {
108 true
109 }
110
111 fn requires_ast_for_arguments(&self) -> bool {
112 true
113 }
114
115 fn examples(&self) -> Vec<Example> {
116 vec![Example {
117 description: "Add examples to custom command",
118 example: r###"# Double numbers
119 @example "double an int" { 2 | double } --result 4
120 @example "double a float" { 0.25 | double } --result 0.5
121 def double []: [number -> number] { $in * 2 }"###,
122 result: None,
123 }]
124 }
125}
126
127fn attr_example_impl(
128 example_expr: &nu_protocol::ast::Expression,
129 example_string: Result<String, ShellError>,
130 working_set: &StateWorkingSet<'_>,
131 call: &Call<'_>,
132 description: Spanned<String>,
133 result: Option<Value>,
134) -> Result<PipelineData, ShellError> {
135 let example_content = match example_expr.as_block() {
136 Some(block_id) => {
137 let block = working_set.get_block(block_id);
138 let contents =
139 working_set.get_span_contents(block.span.expect("a block must have a span"));
140 let contents = contents
141 .strip_prefix(b"{")
142 .and_then(|x| x.strip_suffix(b"}"))
143 .unwrap_or(contents)
144 .trim_ascii();
145 String::from_utf8_lossy(contents).into_owned()
146 }
147 None => example_string?,
148 };
149
150 let mut rec = record! {
151 "description" => Value::string(description.item, description.span),
152 "example" => Value::string(example_content, example_expr.span),
153 };
154 if let Some(result) = result {
155 rec.push("result", result);
156 }
157
158 Ok(Value::record(rec, call.head).into_pipeline_data())
159}