tome-scripts 0.12.3

Turn directories of shell scripts into full-blown CLIs
Documentation
use super::super::directory::scan_directory;
use super::super::finder::{self, FindResult};
use super::super::script::Script;
use super::builtins::BUILTIN_COMMANDS;
use std::path::PathBuf;

macro_rules! help_template {
    () => {
        r#"echo -e
'This is an instance of tome, running against the directory {}.
\nThe commands are namespaced by the directory structure.
\n
\n{}commands available are:
\n    {}
\n
\nBuiltin commands available to all instance of tome are:
\n    {}
';"#
    };
}

pub fn help(root: &str, args: &[String]) -> Result<String, String> {
    if args.is_empty() {
        return help_all(root, "Full list of ");
    }
    match finder::find_script(root, args) {
        Ok(FindResult::File(invocation)) => {
            let script = Script::load(invocation.target.to_str().unwrap_or_default())
                .map_err(|e| echo_error(&format!("IOError loading file: {:?}", e)))?;
            Ok(help_for_script(&invocation.target, &script))
        }
        Ok(FindResult::Directory(path)) => {
            let dir_str = path.to_str().unwrap_or_default();
            let scope_label = args.join(" ");
            help_all_in_dir(root, dir_str, &format!("The list of {} ", scope_label))
        }
        Err(err) => Ok(echo_error(&err)),
    }
}

fn echo_error(msg: &str) -> String {
    format!("echo '{}' >&2", escape_slashes(msg))
}

fn help_for_script(path: &PathBuf, script: &Script) -> String {
    let name = path
        .file_name()
        .and_then(|n| n.to_str())
        .unwrap_or_default();
    let summary = escape_slashes(&script.summary_string);
    let help = escape_slashes(&script.help_string);
    format!(
        "echo -e\n'{}: {}\n{}';",
        escape_slashes(name),
        summary,
        help
    )
}

fn help_all(root: &str, label: &str) -> Result<String, String> {
    help_all_in_dir(root, root, label)
}

fn help_all_in_dir(root: &str, dir: &str, list_label: &str) -> Result<String, String> {
    let builtins_with_help: Vec<_> = BUILTIN_COMMANDS
        .iter()
        .map(|(command, command_struct)| {
            format!(
                "    {}: {}",
                escape_slashes(command),
                escape_slashes(command_struct.help_text),
            )
        })
        .collect();

    let ignorer = super::super::directory::TomeIgnorer::new(std::path::Path::new(root));
    let commands_and_scripts =
        scan_directory(dir, &mut vec![], &ignorer).map_err(|e| echo_error(&format!("{}", e)))?;
    let commands_with_help: Vec<_> = commands_and_scripts
        .iter()
        .map(|(command, script)| {
            format!(
                "    {}: {}",
                escape_slashes(command),
                escape_slashes(&script.summary_string)
            )
        })
        .collect();

    Ok(format!(
        help_template!(),
        root,
        list_label,
        commands_with_help.join("\\n"),
        builtins_with_help.join("\\n"),
    ))
}

fn escape_slashes(s: &str) -> String {
    s.replace('\'', "'\\''")
}