add value names for flags in md and man

This commit is contained in:
Terts Diepraam
2023-12-09 16:57:44 +01:00
parent c4cb26cab8
commit cab0fb2cc9
8 changed files with 157 additions and 60 deletions
+27 -15
View File
@@ -1,19 +1,22 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
use crate::{Command, ValueHint};
use crate::{Command, Flag, ValueHint};
/// Create completion script for `fish`
///
/// Short and long options are combined into single `complete` calls, even if
/// they differ in whether they take arguments or not.
pub fn render(c: &Command) -> String {
let mut out = String::new();
let name = &c.name;
for arg in &c.args {
let mut line = format!("complete -c {name}");
for short in &arg.short {
line.push_str(&format!(" -s {short}"));
for Flag { flag, .. } in &arg.short {
line.push_str(&format!(" -s {flag}"));
}
for long in &arg.long {
line.push_str(&format!(" -l {long}"));
for Flag { flag, .. } in &arg.long {
line.push_str(&format!(" -l {flag}"));
}
line.push_str(&format!(" -d '{}'", arg.help));
if let Some(value) = &arg.value {
@@ -42,15 +45,18 @@ fn render_value_hint(value: &ValueHint) -> String {
#[cfg(test)]
mod test {
use super::render;
use crate::{Arg, Command, ValueHint};
use crate::{Arg, Command, Flag, Value, ValueHint};
#[test]
fn short() {
let c = Command {
name: "test".into(),
name: "test",
args: vec![Arg {
short: vec!["a".into()],
help: "some flag".into(),
short: vec![Flag {
flag: "a",
value: Value::No,
}],
help: "some flag",
..Arg::default()
}],
..Command::default()
@@ -61,10 +67,13 @@ mod test {
#[test]
fn long() {
let c = Command {
name: "test".into(),
name: "test",
args: vec![Arg {
long: vec!["all".into()],
help: "some flag".into(),
long: vec![Flag {
flag: "all",
value: Value::No,
}],
help: "some flag",
..Arg::default()
}],
..Command::default()
@@ -92,11 +101,14 @@ mod test {
];
for (hint, expected) in args {
let c = Command {
name: "test".into(),
name: "test",
args: vec![Arg {
short: vec!["a".into()],
short: vec![Flag {
flag: "a",
value: Value::No,
}],
long: vec![],
help: "some flag".into(),
help: "some flag",
value: Some(hint),
}],
..Command::default()
+40 -10
View File
@@ -1,28 +1,58 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
//! Generation of completion and documentation
//!
//! All formats use the [`Command`] struct as input, which specifies all
//! information needed. This struct is similar to some structs in the derive
//! crate for uutils-args, but there are some key differences:
//!
//! - This is meant to be more general.
//! - Some information is added (such as fields for the summary)
//! - We have [`ValueHint`] in this crate.
//! - Some information is removed because it is irrelevant for completion and documentation
//! - This struct is meant to exist at runtime of the program
//!
mod fish;
mod man;
mod md;
mod zsh;
/// A description of a CLI command
///
/// The completions and documentation will be generated based on this struct.
#[derive(Default)]
pub struct Command {
pub name: String,
pub summary: String,
pub version: String,
pub after_options: String,
pub args: Vec<Arg>,
pub struct Command<'a> {
pub name: &'a str,
pub summary: &'a str,
pub version: &'a str,
pub after_options: &'a str,
pub args: Vec<Arg<'a>>,
}
/// Description of an argument
///
/// An argument may consist of several flags. In completions and documentation
/// formats that support it, these flags will be grouped.
#[derive(Default)]
pub struct Arg {
pub short: Vec<String>,
pub long: Vec<String>,
pub help: String,
pub struct Arg<'a> {
pub short: Vec<Flag<'a>>,
pub long: Vec<Flag<'a>>,
pub help: &'a str,
pub value: Option<ValueHint>,
}
pub struct Flag<'a> {
pub flag: &'a str,
pub value: Value<'a>,
}
pub enum Value<'a> {
Required(&'a str),
Optional(&'a str),
No,
}
// Modelled after claps ValueHint
pub enum ValueHint {
Strings(Vec<String>),
+34 -9
View File
@@ -1,36 +1,61 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
use crate::Command;
use roff::{bold, roman, Roff};
use crate::{Command, Flag, Value};
use roff::{bold, italic, roman, Roff};
pub fn render(c: &Command) -> String {
let mut page = Roff::new();
page.control("TH", [&c.name.to_uppercase(), "1"]);
page.control("SH", ["NAME"]);
page.text([roman(&c.name)]);
page.text([roman(c.name)]);
page.control("SH", ["DESCRIPTION"]);
page.text([roman(&c.summary)]);
page.text([roman(c.summary)]);
page.control("SH", ["OPTIONS"]);
for arg in &c.args {
page.control("TP", []);
let mut flags = Vec::new();
for l in &arg.long {
for Flag { flag, value } in &arg.long {
if !flags.is_empty() {
flags.push(roman(", "));
}
flags.push(bold(format!("--{l}")));
flags.push(bold(format!("--{flag}")));
match value {
Value::Required(name) => {
flags.push(roman("="));
flags.push(italic(*name));
}
Value::Optional(name) => {
flags.push(roman("["));
flags.push(roman("="));
flags.push(italic(*name));
flags.push(roman("]"));
}
Value::No => {}
}
}
for s in &arg.short {
for Flag { flag, value } in &arg.short {
if !flags.is_empty() {
flags.push(roman(", "));
}
flags.push(bold(format!("-{s}")));
flags.push(bold(format!("-{flag}")));
match value {
Value::Required(name) => {
flags.push(roman(" "));
flags.push(italic(*name));
}
Value::Optional(name) => {
flags.push(roman("["));
flags.push(italic(*name));
flags.push(roman("]"));
}
Value::No => {}
}
}
page.text(flags);
page.text([roman(&arg.help)]);
page.text([roman(arg.help)]);
}
page.render()
}
+17 -7
View File
@@ -1,18 +1,18 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
use crate::Command;
use crate::{Command, Flag, Value};
/// Render command to a markdown file for mdbook
pub fn render(c: &Command) -> String {
let mut out = String::new();
out.push_str(&title(c));
out.push_str(&additional(c));
out.push_str(&c.summary);
out.push_str(c.summary);
out.push_str("\n\n");
out.push_str(&options(c));
out.push_str("\n\n");
out.push_str(&c.after_options);
out.push_str(c.after_options);
out.push('\n');
out
}
@@ -40,12 +40,22 @@ fn options(c: &Command) -> String {
let mut flags = Vec::new();
for long in &arg.long {
flags.push(format!("<code>--{long}</code>"));
for Flag { flag, value } in &arg.long {
let value_str = match value {
Value::Required(name) => format!("={name}"),
Value::Optional(name) => format!("[={name}]"),
Value::No => String::new(),
};
flags.push(format!("<code>--{flag}{value_str}</code>"));
}
for short in &arg.short {
flags.push(format!("<code>-{short}</code>"))
for Flag { flag, value } in &arg.short {
let value_str = match value {
Value::Required(name) => format!(" {name}"),
Value::Optional(name) => format!("[{name}]"),
Value::No => String::new(),
};
flags.push(format!("<code>-{flag}{value_str}</code>"));
}
out.push_str(&flags.join(", "));
+6 -6
View File
@@ -1,11 +1,11 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
use crate::{Arg, Command};
use crate::{Arg, Command, Flag};
/// Create completion script for `zsh`
pub fn render(c: &Command) -> String {
template(&c.name, &render_args(&c.args))
template(c.name, &render_args(&c.args))
}
fn render_args(args: &[Arg]) -> String {
@@ -13,11 +13,11 @@ fn render_args(args: &[Arg]) -> String {
let indent = " ".repeat(8);
for arg in args {
let help = &arg.help;
for short in &arg.short {
out.push_str(&format!("{indent}'-{short}[{help}]' \\\n"));
for Flag { flag, .. } in &arg.short {
out.push_str(&format!("{indent}'-{flag}[{help}]' \\\n"));
}
for long in &arg.long {
out.push_str(&format!("{indent}'--{long}[{help}]' \\\n"));
for Flag { flag, .. } in &arg.long {
out.push_str(&format!("{indent}'--{flag}[{help}]' \\\n"));
}
}
out