-
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathlib.rs
More file actions
154 lines (145 loc) · 5.72 KB
/
Copy pathlib.rs
File metadata and controls
154 lines (145 loc) · 5.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
//! This exposes a function in Python, so an mkdocs plugin can use it to generate the CLI document.
//! For actual library/binary source code look in cpp-linter folder.
use std::{any::TypeId, collections::HashMap};
use clap::CommandFactory;
use cpp_linter::cli::Cli;
use pyo3::{exceptions::PyValueError, prelude::*};
const GROUPS_ORDER: [&str; 5] = [
"General options",
"Source options",
"Clang-format options",
"Clang-tidy options",
"Feedback options",
];
#[pyfunction]
fn generate_cli_doc(metadata: HashMap<String, HashMap<String, Py<PyAny>>>) -> PyResult<String> {
let mut out = String::new();
let mut command = Cli::command();
out.push_str(
format!(
"```text title=\"Usage\"\n{}\n```\n",
command
.render_usage()
.to_string()
.trim_start_matches("Usage: ")
)
.as_str(),
);
out.push_str("\n## Commands\n");
for cmd in command.get_subcommands() {
out.push_str(format!("\n### `{}`\n\n", cmd.get_name()).as_str());
out.push_str(
format!(
"{}\n",
cmd.get_about()
.ok_or(PyValueError::new_err(format!(
"{} command has no help message",
cmd.get_name()
)))?
.to_string()
.trim()
)
.as_str(),
);
}
out.push_str("## Arguments\n");
for arg in command.get_positionals() {
out.push_str(format!("\n### `{}`\n\n", arg.get_id().as_str()).as_str());
let short_help = &arg.get_help();
if let Some(help) = arg.get_long_help().or(*short_help) {
out.push_str(format!("{}\n", help.to_string().trim()).as_str());
}
}
// reorganize groups according to GROUPS_ORDER
let mut ordered = Vec::with_capacity(command.get_groups().count());
for group in GROUPS_ORDER {
let group_obj = command
.get_groups()
.find(|arg_group| arg_group.get_id().as_str() == group)
.ok_or(PyValueError::new_err(format!(
"{} not found in command's groups",
group
)))?;
ordered.push(group_obj.to_owned());
}
for group in ordered {
out.push_str(format!("\n## {}\n", group.get_id()).as_str());
for arg_id in group.get_args() {
let arg = command
.get_arguments()
.find(|a| *a.get_id() == *arg_id)
.ok_or(PyValueError::new_err(format!(
"arg {} in group {} not found in command",
arg_id.as_str(),
group.get_id().as_str()
)))?;
let long_name = arg.get_long().ok_or(PyValueError::new_err(format!(
"Failed to get long name of argument with id {}",
arg_id.as_str()
)))?;
let short_name = arg
.get_short()
.map(|c| format!("-{}, ", c))
.unwrap_or_default();
out.push_str(format!("\n### `{}--{}`\n\n", short_name, long_name).as_str());
if let Some(map) = metadata.get(long_name) {
if let Some(val) = map.get("minimum-version") {
out.push_str(format!("<!-- md:version {} -->\n", val).as_str());
}
if let Some(val) = map.get("required-permission") {
out.push_str(format!("<!-- md:permission {} -->\n", val).as_str());
}
if map.contains_key("experimental") {
out.push_str("<!-- md:flag experimental -->\n");
}
}
let default = arg.get_default_values();
if let Some(default_value) = default.first() {
out.push_str(format!("<!-- md:default {:?} -->\n\n", default_value).as_str());
} else {
out.push('\n');
}
let short_help = arg.get_help();
if let Some(help) = &arg.get_long_help().or(short_help) {
out.push_str(format!("{}\n", help.to_string().trim()).as_str());
}
let possible_vals = arg.get_possible_values();
let is_boolean = arg.get_value_parser().type_id() == TypeId::of::<bool>();
if !possible_vals.is_empty() {
out.push_str("\nPossible values:\n\n");
if is_boolean {
out.push_str("- `true` | `on` | `1`\n");
out.push_str("- `false` | `off` | `0`\n");
} else {
for val in possible_vals {
let name = format!(
"`{}`",
val.get_name_and_aliases()
.collect::<Vec<&str>>()
.join("` | `")
);
let help = val
.get_help()
.map(|v| {
let v = v.to_string();
let trimmed = v.trim();
if !trimmed.is_empty() {
format!(": {trimmed}")
} else {
trimmed.to_string()
}
})
.unwrap_or_default();
out.push_str(format!("- {name}{help}\n").as_str());
}
}
}
}
}
Ok(out)
}
#[pymodule(gil_used = false)]
pub fn cli_gen(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(generate_cli_doc, m)?)?;
Ok(())
}