From 0aa2659072280dea2d501c2aa0505329d9383d13 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Sun, 9 Feb 2020 02:26:21 +0200 Subject: [PATCH] meli: add cli-docs feature Optionally build manpages to text with mandoc and print them from the command line. --- Cargo.toml | 1 + build.rs | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/bin.rs | 65 ++++++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 127 insertions(+), 5 deletions(-) create mode 100644 build.rs diff --git a/Cargo.toml b/Cargo.toml index e90947b46..5f3a5cf99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,6 +58,7 @@ default = ["sqlite3"] notmuch = ["melib/notmuch_backend", ] jmap = ["melib/jmap_backend",] sqlite3 = ["rusqlite"] +cli-docs = [] # Print tracing logs as meli runs in stderr # enable for debug tracing logs: build with --features=debug-tracing diff --git a/build.rs b/build.rs new file mode 100644 index 000000000..03465d76a --- /dev/null +++ b/build.rs @@ -0,0 +1,66 @@ +/* + * meli - build.rs + * + * Copyright 2020 Manos Pitsidianakis + * + * This file is part of meli. + * + * meli is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * meli is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with meli. If not, see . + */ + +fn main() { + #[cfg(feature = "cli-docs")] + { + const MANDOC_OPTS: &[&'static str] = &["-T", "utf8", "-I", "os=Generated by mandoc(1)"]; + use std::env; + use std::fs::File; + use std::io::prelude::*; + use std::path::Path; + use std::process::Command; + let out_dir = env::var("OUT_DIR").unwrap(); + let mut out_dir_path = Path::new(&out_dir).to_path_buf(); + + // Note that there are a number of downsides to this approach, the comments + // below detail how to improve the portability of these commands. + let output = Command::new("mandoc") + .args(MANDOC_OPTS) + .arg("meli.1") + .output() + .unwrap(); + + out_dir_path.push("meli.txt"); + let mut file = File::create(&out_dir_path).unwrap(); + file.write_all(&output.stdout).unwrap(); + out_dir_path.pop(); + + out_dir_path.push("meli.conf.txt"); + let output = Command::new("mandoc") + .args(MANDOC_OPTS) + .arg("meli.conf.5") + .output() + .unwrap(); + let mut file = File::create(&out_dir_path).unwrap(); + file.write_all(&output.stdout).unwrap(); + out_dir_path.pop(); + + out_dir_path.push("meli-themes.txt"); + let output = Command::new("mandoc") + .args(MANDOC_OPTS) + .arg("meli-themes.5") + .output() + .unwrap(); + let mut file = File::create(&out_dir_path).unwrap(); + file.write_all(&output.stdout).unwrap(); + } +} diff --git a/src/bin.rs b/src/bin.rs index 7f2d857cb..750256fee 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -134,8 +134,14 @@ macro_rules! error_and_exit { }} } -#[derive(Debug)] +enum ManPages { + Main = 0, + Conf = 1, + Themes = 2, +} + struct CommandLineArguments { + print_manpage: Option, create_config: Option, test_config: Option, config: Option, @@ -155,6 +161,7 @@ fn main() { fn run_app() -> Result<()> { enum CommandLineFlags { + PrintManPage, CreateConfig, TestConfig, Config, @@ -162,6 +169,7 @@ fn run_app() -> Result<()> { use CommandLineFlags::*; let mut prev: Option = None; let mut args = CommandLineArguments { + print_manpage: None, create_config: None, test_config: None, config: None, @@ -176,12 +184,18 @@ fn run_app() -> Result<()> { Some(CreateConfig) => error_and_exit!("invalid value for flag `--create-config`"), Some(Config) => error_and_exit!("invalid value for flag `--config`"), Some(TestConfig) => error_and_exit!("invalid value for flag `--test-config`"), + Some(PrintManPage) => { + error_and_exit!("invalid value for flag `--print-documentation`") + } }, "--create-config" => match prev { None => prev = Some(CreateConfig), Some(CreateConfig) => error_and_exit!("invalid value for flag `--create-config`"), Some(TestConfig) => error_and_exit!("invalid value for flag `--test-config`"), Some(Config) => error_and_exit!("invalid value for flag `--config`"), + Some(PrintManPage) => { + error_and_exit!("invalid value for flag `--print-documentation`") + } }, "--config" | "-c" => match prev { None => prev = Some(Config), @@ -192,6 +206,9 @@ fn run_app() -> Result<()> { Some(CreateConfig) => error_and_exit!("invalid value for flag `--create-config`"), Some(Config) => error_and_exit!("invalid value for flag `--config`"), Some(TestConfig) => error_and_exit!("invalid value for flag `--test-config`"), + Some(PrintManPage) => { + error_and_exit!("invalid value for flag `--print-documentation`") + } }, "--help" | "-h" => { args.help = true; @@ -208,6 +225,13 @@ fn run_app() -> Result<()> { print!("{}", conf::Theme::default().key_to_string("dark", false)); return Ok(()); } + "--print-documentation" => { + if args.print_manpage.is_some() { + error_and_exit!("Multiple invocations of --print-documentation"); + } + prev = Some(PrintManPage); + args.print_manpage = Some(ManPages::Main); + } e => match prev { None => error_and_exit!("error: value without command {}", e), Some(CreateConfig) if args.create_config.is_none() => { @@ -222,6 +246,18 @@ fn run_app() -> Result<()> { args.test_config = Some(i); prev = None; } + Some(PrintManPage) => { + match e { + "meli" | "main" => { /* This is the default */ } + "meli.conf" | "conf" | "config" | "configuration" => { + args.print_manpage = Some(ManPages::Conf); + } + "meli-themes" | "themes" | "theming" | "theme" => { + args.print_manpage = Some(ManPages::Themes); + } + _ => error_and_exit!("Invalid documentation page: {}", e), + } + } Some(TestConfig) => error_and_exit!("Duplicate value for flag `--test-config`"), Some(CreateConfig) => error_and_exit!("Duplicate value for flag `--create-config`"), Some(Config) => error_and_exit!("Duplicate value for flag `--config`"), @@ -230,10 +266,11 @@ fn run_app() -> Result<()> { } if args.help { - println!("usage:\tmeli [--create-config[ PATH]] [--config[ PATH]|-c[ PATH]]"); + println!("usage:\tmeli [--config PATH|-c PATH]"); println!("\tmeli --help"); println!("\tmeli --version"); println!(""); + println!("Other options:"); println!("\t--help, -h\t\tshow this message and exit"); println!("\t--version, -v\t\tprint version and exit"); println!("\t--create-config[ PATH]\tcreate a sample configuration file with available configuration options. If PATH is not specified, meli will try to create it in $XDG_CONFIG_HOME/meli/config.toml"); @@ -243,6 +280,10 @@ fn run_app() -> Result<()> { println!("\t--config PATH, -c PATH\tuse specified configuration file"); println!("\t--print-loaded-themes\tprint loaded themes in full to stdout and exit."); println!("\t--print-default-theme\tprint default theme in full to stdout and exit."); + #[cfg(feature = "cli-docs")] + { + println!("\t--print-documentation [meli conf themes]\n\t\t\t\tprint documentation page and exit (Piping to a pager is recommended.)."); + } return Ok(()); } @@ -257,14 +298,13 @@ fn run_app() -> Result<()> { Some(CreateConfig) => error_and_exit!("Duplicate value for flag `--create-config`"), Some(Config) => error_and_exit!("error: flag without value: `--config`"), Some(TestConfig) => error_and_exit!("error: flag without value: `--test-config`"), + Some(PrintManPage) => {} }; if let Some(config_path) = args.test_config.as_ref() { conf::FileSettings::validate(config_path)?; return Ok(()); - } - - if let Some(config_path) = args.create_config.as_mut() { + } else if let Some(config_path) = args.create_config.as_mut() { let config_path: PathBuf = if config_path.is_empty() { let xdg_dirs = xdg::BaseDirectories::with_prefix("meli").unwrap(); xdg_dirs.place_config_file("config.toml").map_err(|e| { @@ -282,6 +322,21 @@ fn run_app() -> Result<()> { } conf::create_config_file(&config_path)?; return Ok(()); + } else if let Some(_page) = args.print_manpage { + #[cfg(feature = "cli-docs")] + { + const MANPAGES: [&'static str; 3] = [ + include_str!(concat!(env!("OUT_DIR"), "/meli.txt")), + include_str!(concat!(env!("OUT_DIR"), "/meli.conf.txt")), + include_str!(concat!(env!("OUT_DIR"), "/meli-themes.txt")), + ]; + println!("{}", MANPAGES[_page as usize]); + return Ok(()); + } + #[cfg(not(feature = "cli-docs"))] + { + error_and_exit!("error: this version of meli was not build with embedded documentation. You might have it installed as manpages (eg `man meli`), otherwise check https://meli.delivery"); + } } if let Some(config_location) = args.config.as_ref() {