meli/args: add --gzipped flag to man subcommand

Add --gzipped flag to print a manpage without decompressing it, for
piping the output to a file. Because why not.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
pull/438/head
Manos Pitsidianakis 2 months ago
parent 738f7c4695
commit 814af0e94d
No known key found for this signature in database
GPG Key ID: 7729C7707F7E09D0

@ -91,17 +91,18 @@ pub enum SubCommand {
#[derive(Debug, StructOpt)]
pub struct ManOpt {
/// If set, output text in stdout instead of spawning `$PAGER`.
#[cfg(feature = "cli-docs")]
#[cfg_attr(feature = "cli-docs", structopt(long = "no-raw", alias = "no-raw"))]
pub no_raw: bool,
/// If set, output compressed gzip manpage in binary form in stdout.
#[cfg(feature = "cli-docs")]
#[cfg_attr(feature = "cli-docs", structopt(long = "gzipped"))]
pub gzipped: bool,
#[cfg(feature = "cli-docs")]
#[cfg_attr(feature = "cli-docs", structopt(default_value = "meli", possible_values=manpages::POSSIBLE_VALUES, value_name="PAGE", parse(try_from_str = manpages::parse_manpage)))]
/// Name of manual page.
pub page: manpages::ManPages,
/// If true, output text in stdout instead of spawning `$PAGER`.
#[cfg(feature = "cli-docs")]
#[cfg_attr(
feature = "cli-docs",
structopt(long = "no-raw", alias = "no-raw", value_name = "bool")
)]
pub no_raw: Option<Option<bool>>,
}
#[derive(Debug, StructOpt)]
@ -161,6 +162,22 @@ impl Opt {
SubCommand::Man(ManOpt {
page,
no_raw,
gzipped: true,
}) => {
use std::io::Write;
ret_err!(std::io::stdout().write_all(if no_raw {
page.text_gz()
} else {
page.mdoc_gz()
}));
Ok(())
}
#[cfg(feature = "cli-docs")]
SubCommand::Man(ManOpt {
page,
no_raw,
gzipped: false,
}) => {
subcommands::man(page, false).and_then(|s| subcommands::pager(s, no_raw))
}

@ -84,6 +84,19 @@ impl std::fmt::Display for ManPages {
}
impl ManPages {
const MANPAGES: [&'static [u8]; 4] = [
include_bytes!(concat!(env!("OUT_DIR"), "/meli.txt.gz")),
include_bytes!(concat!(env!("OUT_DIR"), "/meli.conf.txt.gz")),
include_bytes!(concat!(env!("OUT_DIR"), "/meli-themes.txt.gz")),
include_bytes!(concat!(env!("OUT_DIR"), "/meli.7.txt.gz")),
];
const MANPAGES_MDOC: [&'static [u8]; 4] = [
include_bytes!(concat!(env!("OUT_DIR"), "/meli.mdoc.gz")),
include_bytes!(concat!(env!("OUT_DIR"), "/meli.conf.mdoc.gz")),
include_bytes!(concat!(env!("OUT_DIR"), "/meli-themes.mdoc.gz")),
include_bytes!(concat!(env!("OUT_DIR"), "/meli.7.mdoc.gz")),
];
pub fn install(destination: Option<PathBuf>) -> Result<PathBuf> {
fn path_valid(p: &Path, tries: &mut Vec<PathBuf>) -> bool {
tries.push(p.into());
@ -145,24 +158,19 @@ impl ManPages {
Ok(path)
}
pub fn read(self, source: bool) -> Result<String> {
const MANPAGES: [&[u8]; 4] = [
include_bytes!(concat!(env!("OUT_DIR"), "/meli.txt.gz")),
include_bytes!(concat!(env!("OUT_DIR"), "/meli.conf.txt.gz")),
include_bytes!(concat!(env!("OUT_DIR"), "/meli-themes.txt.gz")),
include_bytes!(concat!(env!("OUT_DIR"), "/meli.7.txt.gz")),
];
const MANPAGES_MDOC: [&[u8]; 4] = [
include_bytes!(concat!(env!("OUT_DIR"), "/meli.mdoc.gz")),
include_bytes!(concat!(env!("OUT_DIR"), "/meli.conf.mdoc.gz")),
include_bytes!(concat!(env!("OUT_DIR"), "/meli-themes.mdoc.gz")),
include_bytes!(concat!(env!("OUT_DIR"), "/meli.7.mdoc.gz")),
];
pub fn mdoc_gz(self) -> &'static [u8] {
Self::MANPAGES_MDOC[self as usize]
}
pub fn text_gz(self) -> &'static [u8] {
Self::MANPAGES[self as usize]
}
pub fn read(self, source: bool) -> Result<String> {
let mut gz = GzDecoder::new(if source {
MANPAGES_MDOC[self as usize]
self.mdoc_gz()
} else {
MANPAGES[self as usize]
self.text_gz()
});
let mut v = String::with_capacity(
str::parse::<usize>(unsafe {

@ -114,11 +114,68 @@ fn test_cli_subcommands() {
}
}
fn test_subcommand_man() {
for (man, title) in [
("meli.1", "MELI(1)"),
("meli.conf.5", "MELI.CONF(5)"),
("meli-themes.5", "MELI-THEMES(5)"),
("meli.7", "MELI(7)"),
] {
for gzipped in [true, false] {
for no_raw in [true, false] {
let mut cmd = Command::cargo_bin("meli").unwrap();
let args = match (no_raw, gzipped) {
(true, true) => &["man", "--no-raw", "--gzipped", man][..],
(true, false) => &["man", "--no-raw", man],
(false, false) => &["man", man],
(false, true) => &["man", "--gzipped", man],
};
let output = cmd.args(args).output().unwrap().assert();
output.code(0).stdout(predicate::function(|x: &[u8]| {
use std::io::Read;
use flate2::bufread::GzDecoder;
let mut gz = GzDecoder::new(x);
let content = if gzipped {
let size = gz.header().unwrap().comment().unwrap();
let mut v = String::with_capacity(
str::parse::<usize>(
std::str::from_utf8(size)
.expect("was not compressed with size comment header"),
)
.expect("was not compressed with size comment header"),
);
gz.read_to_string(&mut v)
.expect("expected gzipped output but could not decode it.");
v
} else {
assert_eq!(gz.header(), None);
let mut v = String::with_capacity(0);
gz.read_to_string(&mut v).unwrap_err();
String::from_utf8(x.to_vec()).expect("invalid utf-8 content")
};
if !no_raw && gzipped {
assert!(content.contains(man));
} else {
assert!(content.contains('\u{8}'));
assert!(content.contains(title));
}
true
}));
}
}
}
}
version();
help();
test_subcommand_succeeds("help");
test_subcommand_succeeds("compiled-with");
test_subcommand_succeeds("man");
test_subcommand_man();
let tmp_dir = TempDir::new().unwrap();

Loading…
Cancel
Save