getargs_derive/
completions.rs1mod bash;
6mod fish;
7mod man;
8mod zsh;
9
10use crate::{ArgInfo, Command, CommandInfo, ParseValueEnumError};
11use core::fmt;
12use core::str::FromStr;
13use std::io::{self, Write};
14use std::string::String;
15use std::vec::Vec;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19#[non_exhaustive]
20pub enum Shell {
21 Bash,
23 Zsh,
25 Fish,
27}
28
29impl Shell {
30 pub const ALL: &'static [Shell] = &[Shell::Bash, Shell::Zsh, Shell::Fish];
32
33 pub const NAMES: &'static [&'static str] = &["bash", "zsh", "fish"];
35
36 pub const fn name(self) -> &'static str {
38 match self {
39 Shell::Bash => "bash",
40 Shell::Zsh => "zsh",
41 Shell::Fish => "fish",
42 }
43 }
44
45 pub fn file_name(self, name: &str) -> String {
48 match self {
49 Shell::Bash => std::format!("{name}.bash"),
50 Shell::Zsh => std::format!("_{name}"),
51 Shell::Fish => std::format!("{name}.fish"),
52 }
53 }
54
55 pub fn render(self, command: &CommandInfo) -> String {
57 match self {
58 Shell::Bash => bash::render(command),
59 Shell::Zsh => zsh::render(command),
60 Shell::Fish => fish::render(command),
61 }
62 }
63}
64
65impl FromStr for Shell {
66 type Err = ParseValueEnumError;
67
68 fn from_str(value: &str) -> Result<Self, Self::Err> {
69 Self::ALL
70 .iter()
71 .copied()
72 .find(|shell| shell.name() == value)
73 .ok_or(ParseValueEnumError)
74 }
75}
76
77impl fmt::Display for Shell {
78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79 f.write_str(self.name())
80 }
81}
82
83pub fn generate<C: Command>(shell: Shell, output: &mut impl Write) -> io::Result<()> {
85 output.write_all(shell.render(&C::COMMAND).as_bytes())
86}
87
88pub fn man_page<C: Command>(output: &mut impl Write) -> io::Result<()> {
90 output.write_all(render_man_page(&C::COMMAND).as_bytes())
91}
92
93pub fn render_man_page(command: &CommandInfo) -> String {
95 man::render(command)
96}
97
98struct Node<'c> {
99 path: Vec<&'c CommandInfo>,
100 command: &'c CommandInfo,
101 args: Vec<&'c ArgInfo>,
102}
103
104impl<'c> Node<'c> {
105 fn root(command: &'c CommandInfo) -> Self {
106 Node {
107 path: std::vec![command],
108 command,
109 args: command.args.iter().collect(),
110 }
111 }
112
113 fn id(&self) -> String {
114 let names: Vec<String> = self
115 .path
116 .iter()
117 .map(|command| identifier(command.name))
118 .collect();
119 names.join("__")
120 }
121}
122
123fn tree(command: &CommandInfo) -> Vec<Node<'_>> {
124 fn visit<'c>(node: Node<'c>, nodes: &mut Vec<Node<'c>>) {
125 let globals: Vec<&ArgInfo> = node.args.iter().copied().filter(|arg| arg.global).collect();
126 let children: Vec<Node<'c>> = node
127 .command
128 .commands
129 .iter()
130 .map(|child| {
131 let mut path = node.path.clone();
132 path.push(child);
133 let mut args: Vec<&ArgInfo> = child.args.iter().collect();
134 let inherited: Vec<&ArgInfo> = globals
135 .iter()
136 .copied()
137 .filter(|global| !args.iter().any(|arg| arg.id == global.id))
138 .collect();
139 args.extend(inherited);
140 Node {
141 path,
142 command: child,
143 args,
144 }
145 })
146 .collect();
147 nodes.push(node);
148 for child in children {
149 visit(child, nodes);
150 }
151 }
152 let mut nodes = Vec::new();
153 visit(Node::root(command), &mut nodes);
154 nodes
155}
156
157fn visible<'n>(node: &'n Node<'_>) -> impl Iterator<Item = &'n ArgInfo> {
158 node.args.iter().copied().filter(|arg| !arg.hidden)
159}
160
161fn options<'n>(node: &'n Node<'_>) -> impl Iterator<Item = &'n ArgInfo> {
162 visible(node).filter(|arg| !arg.is_positional())
163}
164
165fn positionals<'n>(node: &'n Node<'_>) -> impl Iterator<Item = &'n ArgInfo> {
166 visible(node).filter(|arg| arg.is_positional())
167}
168
169fn subcommands(command: &CommandInfo) -> impl Iterator<Item = &CommandInfo> {
170 command.commands.iter().filter(|command| !command.hidden)
171}
172
173fn command_names(command: &CommandInfo) -> Vec<&'static str> {
174 core::iter::once(command.name)
175 .chain(command.visible_aliases.iter().copied())
176 .collect()
177}
178
179fn all_command_names(command: &CommandInfo) -> Vec<&'static str> {
180 core::iter::once(command.name)
181 .chain(command.visible_aliases.iter().copied())
182 .chain(command.aliases.iter().copied())
183 .collect()
184}
185
186fn shorts(arg: &ArgInfo) -> Vec<char> {
187 arg.short
188 .into_iter()
189 .chain(arg.visible_short_aliases.iter().copied())
190 .collect()
191}
192
193fn longs(arg: &ArgInfo) -> Vec<&'static str> {
194 arg.long
195 .into_iter()
196 .chain(arg.visible_aliases.iter().copied())
197 .collect()
198}
199
200fn flags(arg: &ArgInfo) -> Vec<String> {
201 let mut flags: Vec<String> = shorts(arg)
202 .into_iter()
203 .map(|short| std::format!("-{short}"))
204 .collect();
205 flags.extend(longs(arg).into_iter().map(|long| std::format!("--{long}")));
206 flags
207}
208
209fn summary(text: &str) -> &str {
210 text.lines().next().unwrap_or_default().trim()
211}
212
213fn identifier(name: &str) -> String {
214 name.chars()
215 .map(|character| {
216 if character.is_ascii_alphanumeric() {
217 character
218 } else {
219 '_'
220 }
221 })
222 .collect()
223}