Skip to main content

usage/docs/markdown/
spec.rs

1use crate::docs::markdown::renderer::MarkdownRenderer;
2use crate::error::UsageErr;
3
4impl MarkdownRenderer {
5    pub fn render_spec(&self) -> Result<String, UsageErr> {
6        let all_commands = self.spec.cmd.all_subcommands();
7        // From the raw block for the same reason `render_config` does: the copy on `self.spec`
8        // was rendered by `new` before the builder options existed, and rendering happens once.
9        let mut config = crate::docs::models::SpecConfig::from(&self.raw_config);
10        config.render_md(self);
11        self.render_with("spec_template.md.tera", |ctx| {
12            ctx.insert("all_commands", &all_commands);
13            ctx.insert("config", &config);
14        })
15    }
16
17    pub fn render_index(&self) -> Result<String, UsageErr> {
18        let all_commands = self.spec.cmd.all_subcommands();
19        // So the index can link the settings page it sits beside. Without this the page was
20        // written and nothing pointed at it, which for a reader who starts at the index is
21        // the same as not writing it.
22        let config = crate::docs::models::SpecConfig::from(&self.raw_config);
23        // The name the page will actually be written under, so the link cannot point somewhere
24        // else than the file — including when a `settings` command has taken `settings.md`.
25        self.render_with("index_template.md.tera", |ctx| {
26            // An index links separate pages even when the renderer's ordinary mode is single-file.
27            ctx.insert("multi", &false);
28            ctx.insert("all_commands", &all_commands);
29            ctx.insert("config", &config);
30            ctx.insert("config_page", &self.config_page());
31        })
32    }
33}
34
35#[cfg(test)]
36mod tests {
37    use crate::docs::markdown::renderer::MarkdownRenderer;
38    use crate::test::SPEC_KITCHEN_SINK;
39    use crate::Spec;
40    use insta::assert_snapshot;
41
42    #[test]
43    fn test_render_markdown_spec() {
44        let ctx = MarkdownRenderer::new(SPEC_KITCHEN_SINK.clone());
45        assert_snapshot!(ctx.render_spec().unwrap(), @r"
46        # `mycli`
47
48        - **Usage**: `mycli [FLAGS] <ARGS>… <SUBCOMMAND>`
49
50        ## Arguments
51
52        ### `<arg1>`
53
54        arg1 description
55
56        ### `[arg2]`
57
58        arg2 description
59
60        **Choices:**
61
62        - `choice1`
63        - `choice2`
64        - `choice3`
65
66        **Default:** `default value`
67
68        ### `<arg3>`
69
70        arg3 long description
71
72        ### `<argrest>…`
73
74        ### `[with-default]`
75
76        **Default:** `default value`
77
78        ## Flags
79
80        ### `--flag1`
81
82        flag1 description
83
84        ### `--flag2`
85
86        flag2 long description
87
88        includes a code block:
89
90            $ echo hello world
91            hello world
92
93            more code
94
95        Examples:
96
97            # run with no arguments to use the interactive selector
98            $ mise use
99
100            # set the current version of node to 20.x in mise.toml of current directory
101            # will write the fuzzy version (e.g.: 20)
102
103        some docs
104
105            $ echo hello world
106            hello world
107
108        ### `--flag3`
109
110        flag3 description
111
112        ### `--with-default`
113
114        **Default:** `default value`
115
116        ### `--shell <shell>`
117
118        **Choices:**
119
120        - `bash`
121        - `zsh`
122        - `fish`
123
124        ## `mycli plugin`
125
126        - **Usage**: `mycli plugin <SUBCOMMAND>`
127        - **Source code**: [`src/cli/plugin.rs`](https://github.com/jdx/mise/blob/main/src/cli/plugin.rs)
128
129        ## `mycli plugin install`
130
131        - **Usage**: `mycli plugin install [FLAGS] <plugin> <version>`
132        - **Source code**: [`src/cli/plugin/install.rs`](https://github.com/jdx/mise/blob/main/src/cli/plugin/install.rs)
133
134        install a plugin
135
136        ### Arguments
137
138        #### `<plugin>`
139
140        #### `<version>`
141
142        ### Flags
143
144        #### `-g --global`
145
146        #### `-d --dir <dir>`
147
148        #### `-f --force`
149        ");
150    }
151
152    #[test]
153    fn package_metadata_reaches_markdown() {
154        let spec: Spec = r#"
155            name "metadata"
156            bin "metadata"
157            author "Example Maintainers"
158            license "MIT OR Apache-2.0"
159            repository "https://example.com/tool"
160        "#
161        .parse()
162        .unwrap();
163        let output = MarkdownRenderer::new(spec).render_spec().unwrap();
164
165        assert!(
166            output.contains("**author**: Example Maintainers"),
167            "{output}"
168        );
169        assert!(
170            output.contains("**license**: MIT OR Apache-2.0"),
171            "{output}"
172        );
173        assert!(
174            output.contains("**repository**: https://example.com/tool"),
175            "{output}"
176        );
177    }
178}