intermodal/bin/gen/src/subcommand.rs

163 lines
3.2 KiB
Rust
Raw Normal View History

use crate::common::*;
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)]
pub(crate) struct Subcommand {
pub(crate) bin: PathBuf,
pub(crate) command: Vec<String>,
pub(crate) subcommands: Vec<String>,
}
impl Subcommand {
#[throws]
pub(crate) fn new(bin: &Path, command: Vec<String>) -> Self {
let wide_help = Command::new(bin)
.args(command.as_slice())
.env("IMDL_TERM_WIDTH", "200")
.arg("--help")
.out()?;
const MARKER: &str = "\nSUBCOMMANDS:\n";
let mut subcommands = Vec::new();
if let Some(marker) = wide_help.find(MARKER) {
let block = &wide_help[marker + MARKER.len()..];
for line in block.lines() {
let name = line.trim().split_whitespace().next().unwrap();
subcommands.push(name.into());
}
}
Self {
bin: bin.into(),
command,
subcommands,
}
}
#[throws]
fn help(&self) -> String {
eprintln!("Getting help for `{}`", self.command_line());
Command::new(&self.bin)
.args(self.command.as_slice())
.env("IMDL_TERM_WIDTH", "80")
.arg("--help")
.out()?
}
#[throws]
pub(crate) fn man(&self) -> String {
let command_line = self.command_line();
eprintln!("Generating man page for `{}`", command_line);
let name = command_line.replace(" ", "\\ ");
let help = self.help()?;
let description = if self.command.is_empty() {
"A 40' shipping container for the Internet".to_string()
} else {
help.lines().nth(1).unwrap().into()
};
let include = format!(
"\
[NAME]
\\fB{}\\fR
- {}
",
name, description
);
let tmp = tempfile::tempdir()?;
let include_path = tmp.path().join("include");
fs::write(&include_path, include)?;
let version = cmd!(&self.bin, "--version")
.out()?
.split_whitespace()
.nth(1)
.unwrap()
.to_owned();
eprintln!("Running help2man for `{}`", command_line);
let mut command = self.bin.as_os_str().to_owned();
for arg in &self.command {
command.push(" ");
command.push(arg);
}
let output = cmd!(
"help2man",
"--include",
&include_path,
"--manual",
"Intermodal Manual",
"--no-info",
"--source",
&format!("Intermodal {}", version),
command
)
.out()?;
let man = output
.replace("📦 ", "\n")
.replace("\n.SS ", "\n.SH ")
.replace("\"USAGE:\"", "\"SYNOPSIS:\"");
let re = Regex::new(r"(?ms).SH DESCRIPTION.*?.SH").unwrap();
let man = re.replace(&man, ".SH").into_owned();
man
}
pub(crate) fn slug(&self) -> String {
let mut slug = self
.bin
.display()
.to_string()
.split('/')
.last()
.unwrap()
.to_owned();
for name in &self.command {
slug.push('-');
slug.push_str(&name);
}
slug
}
pub(crate) fn command_line(&self) -> String {
let mut line = self
.bin
.display()
.to_string()
.split('/')
.last()
.unwrap()
.to_owned();
for name in &self.command {
line.push(' ');
line.push_str(&name);
}
line
}
#[throws]
pub(crate) fn page(&self) -> String {
let help = self.help()?;
format!("# `{}`\n```\n{}\n```", self.command_line(), help)
}
}