jan-cli 0.18.0

YAML-defined CLI trees with progressive help, optional exec aliases, merged extra specs, and SQLite audit logging keyed by git branch
Documentation
//! Shared helpers for `jan alias` and `jan config emit` (tree walk + shell headers).

use std::collections::BTreeMap;

use crate::CommandNode;

/// Depth-first visit of every command node with its chain from the root of `map`.
pub fn visit_command_tree(
    map: &BTreeMap<String, CommandNode>,
    prefix: &[String],
    visit: &mut dyn FnMut(&[String], &CommandNode),
) {
    for (name, node) in map {
        let mut chain = prefix.to_vec();
        chain.push(name.clone());
        visit(&chain, node);
        if !node.commands.is_empty() {
            visit_command_tree(&node.commands, &chain, visit);
        }
    }
}

/// Normalize `--shell` dialect for generated-file headers.
pub fn shell_dialect_label(shell: &str) -> &'static str {
    match shell {
        "zsh" => "zsh",
        "bash" => "bash",
        _ => "POSIX sh",
    }
}

/// Header lines for a sourceable artifact (`jan alias`, `jan config emit`, …).
pub fn generated_shell_header(tool: &str, shell: &str, requires_line: &str) -> String {
    format!(
        "# generated by `{tool}` ({})\n{requires_line}\n",
        shell_dialect_label(shell)
    )
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn header_matches_dialect() {
        assert_eq!(
            generated_shell_header("jan alias", "zsh", "# requires: jan use <DIR>"),
            "# generated by `jan alias` (zsh)\n# requires: jan use <DIR>\n"
        );
        assert!(generated_shell_header("jan config emit", "sh", "# x").contains("(POSIX sh)"));
    }
}