usage/docs/markdown/
cmd.rs1use crate::docs::markdown::renderer::MarkdownRenderer;
2use crate::docs::models::SpecCommand;
3use crate::error::UsageErr;
4
5impl MarkdownRenderer {
6 pub fn render_cmd(&self, cmd: &crate::SpecCommand) -> Result<String, UsageErr> {
7 let mut cmd = SpecCommand::from(cmd);
8 cmd.render_md(self);
9 self.render_with("cmd_template.md.tera", |ctx| ctx.insert("cmd", &cmd))
10 }
11}
12
13#[cfg(test)]
14mod tests {
15 use crate::docs::markdown::renderer::MarkdownRenderer;
16 use crate::test::SPEC_KITCHEN_SINK;
17 use crate::Spec;
18 use insta::assert_snapshot;
19
20 #[test]
21 fn test_render_markdown_cmd() {
22 let ctx = MarkdownRenderer::new(SPEC_KITCHEN_SINK.clone())
23 .with_multi(true)
24 .with_replace_pre_with_code_fences(true);
25 assert_snapshot!(ctx.render_cmd(&SPEC_KITCHEN_SINK.cmd).unwrap(), @r"
26 # `mycli`
27
28 - **Usage**: `mycli [FLAGS] <ARGS>… <SUBCOMMAND>`
29
30 ## Arguments
31
32 ### `<arg1>`
33
34 arg1 description
35
36 ### `[arg2]`
37
38 arg2 description
39
40 **Choices:**
41
42 - `choice1`
43 - `choice2`
44 - `choice3`
45
46 **Default:** `default value`
47
48 ### `<arg3>`
49
50 arg3 long description
51
52 ### `<argrest>…`
53
54 ### `[with-default]`
55
56 **Default:** `default value`
57
58 ## Flags
59
60 ### `--flag1`
61
62 flag1 description
63
64 ### `--flag2`
65
66 flag2 long description
67
68 includes a code block:
69
70 ```
71 $ echo hello world
72 hello world
73
74 more code
75 ```
76
77 Examples:
78
79 ```
80 # run with no arguments to use the interactive selector
81 $ mise use
82
83 # set the current version of node to 20.x in mise.toml of current directory
84 # will write the fuzzy version (e.g.: 20)
85 ```
86
87 some docs
88
89 ```
90 $ echo hello world
91 hello world
92 ```
93
94 ### `--flag3`
95
96 flag3 description
97
98 ### `--with-default`
99
100 **Default:** `default value`
101
102 ### `--shell <shell>`
103
104 **Choices:**
105
106 - `bash`
107 - `zsh`
108 - `fish`
109
110 ## Subcommands
111
112 - [`mycli plugin <SUBCOMMAND>`](/plugin.md)
113 ");
114 }
115
116 #[test]
117 fn test_render_markdown_cmd_effect() {
118 let spec: Spec = r#"
119name "mise"
120bin "mise"
121cmd "ls" effect="read" help="List installed tools"
122cmd "use" effect="write" help="Install a tool"
123cmd "uninstall" effect="destructive" help="Remove a tool"
124cmd "version" help="Show the version"
125 "#
126 .parse()
127 .unwrap();
128 let ctx = MarkdownRenderer::new(spec.clone()).with_multi(true);
129 let rendered = spec
130 .cmd
131 .subcommands
132 .values()
133 .map(|cmd| ctx.render_cmd(cmd).unwrap())
134 .collect::<Vec<_>>()
135 .join("\n\n");
136
137 assert_snapshot!(rendered, @r"
140 # `mise ls`
141
142 - **Usage**: `mise ls`
143 - **Effect**: read-only
144
145 List installed tools
146
147 # `mise use`
148
149 - **Usage**: `mise use`
150 - **Effect**: modifies state
151
152 Install a tool
153
154 # `mise uninstall`
155
156 - **Usage**: `mise uninstall`
157 - **Effect**: destructive — may delete or irreversibly overwrite
158
159 Remove a tool
160
161 # `mise version`
162
163 - **Usage**: `mise version`
164
165 Show the version
166 ");
167 }
168
169 #[test]
170 fn test_render_markdown_groups_by_heading() {
171 let spec: Spec = r#"
172bin "mycli"
173flag "--verbose" help="Verbose output"
174flag "--filter <pattern>" help="Only matching" help_heading="Filtering"
175flag "--hidden-one" help="Not shown" help_heading="Filtering" hide=#true
176arg "<file>" help="The file"
177arg "<mode>" help="How to run" help_heading="Behaviour"
178"#
179 .parse()
180 .unwrap();
181 let ctx = MarkdownRenderer::new(spec.clone()).with_multi(true);
182
183 assert_snapshot!(ctx.render_cmd(&spec.cmd).unwrap(), @"
186 # `mycli`
187
188 - **Usage**: `mycli [--verbose] [--filter <pattern>] <file> <mode>`
189
190 ## Arguments
191
192 ### `<file>`
193
194 The file
195
196 ## Behaviour
197
198 ### `<mode>`
199
200 How to run
201
202 ## Flags
203
204 ### `--verbose`
205
206 Verbose output
207
208 ## Filtering
209
210 ### `--filter <pattern>`
211
212 Only matching
213 ");
214 }
215
216 #[test]
217 fn test_render_markdown_groups_global_flags_too() {
218 let spec: Spec = r#"
221bin "mycli"
222flag "--verbose" help="Verbose output" global=#true
223flag "--filter <pattern>" help="Only matching" help_heading="Filtering" global=#true
224flag "--local-one" help="Not global"
225cmd "sub" help="a subcommand"
226"#
227 .parse()
228 .unwrap();
229 let ctx = MarkdownRenderer::new(spec.clone()).with_multi(true);
230
231 assert_snapshot!(ctx.render_cmd(&spec.cmd).unwrap(), @"
232 # `mycli`
233
234 - **Usage**: `mycli [FLAGS] <SUBCOMMAND>`
235
236 ## Global Flags
237
238 ### `--verbose`
239
240 Verbose output
241
242 ## Filtering
243
244 ### `--filter <pattern>`
245
246 Only matching
247
248 ## Flags
249
250 ### `--local-one`
251
252 Not global
253
254 ## Subcommands
255
256 - [`mycli sub`](/sub.md)
257 ");
258 }
259
260 #[test]
261 fn generated_reference_separates_visible_flag_aliases() {
262 let spec: Spec = r#"
263bin "mycli"
264flag "-t -f --tail --follow" help="Follow output"
265"#
266 .parse()
267 .unwrap();
268 let ctx = MarkdownRenderer::new(spec.clone()).with_multi(true);
269 let rendered = ctx.render_cmd(&spec.cmd).unwrap();
270 assert!(rendered.contains("### `-t --tail`"), "{rendered}");
271 assert!(
272 rendered.contains("**Aliases:** `-f`, `--follow`"),
273 "{rendered}"
274 );
275 assert!(
276 !rendered.contains("### `-t -f --tail --follow`"),
277 "{rendered}"
278 );
279 }
280}