dot-viewer 0.1.1

A viewer/debugger for large DAGs in Vim-like TUI
use crate::viewer::utils::Trie;
use clap::builder::{Arg, Command as ClapCommand};

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) enum Command {
    Neighbors(Neighbors),
    Export(Export),
    Xdot(Xdot),
    Filter,
    Help,
    Subgraph,
    Quit,
    NoMatch,
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) struct Neighbors {
    pub(crate) depth: Option<usize>,
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) struct Export {
    pub(crate) filename: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) struct Xdot {
    pub(crate) filename: Option<String>,
}

pub(crate) struct CommandTrie {
    pub(crate) trie_cmd: Trie,
    pub(crate) _trie_arg: Trie,
}

fn subcommands() -> [ClapCommand; 7] {
    [
        ClapCommand::new("neighbors")
            .arg(Arg::new("depth").value_parser(clap::value_parser!(usize))),
        ClapCommand::new("export").arg(Arg::new("filename")),
        ClapCommand::new("xdot").arg(Arg::new("filename")),
        ClapCommand::new("filter"),
        ClapCommand::new("help"),
        ClapCommand::new("subgraph"),
        ClapCommand::new("q"),
    ]
}

fn commands() -> ClapCommand {
    ClapCommand::new("dot-viewer")
        .multicall(true)
        .disable_help_subcommand(true)
        .subcommand_required(true)
        .subcommands(subcommands())
}

impl Command {
    pub fn parse(input: &str) -> Self {
        let inputs: Vec<&str> = input.split_whitespace().collect();

        match commands().try_get_matches_from(inputs) {
            Ok(matches) => match matches.subcommand() {
                Some(("neighbors", matches)) => {
                    let depth = matches.get_one::<usize>("depth").copied();
                    let neigbors = Neighbors { depth };

                    Self::Neighbors(neigbors)
                }
                Some(("export", matches)) => {
                    let filename = matches.get_one::<String>("filename").cloned();
                    let export = Export { filename };

                    Self::Export(export)
                }
                Some(("xdot", matches)) => {
                    let filename = matches.get_one::<String>("filename").cloned();
                    let xdot = Xdot { filename };

                    Self::Xdot(xdot)
                }
                Some(("filter", _)) => Self::Filter,
                Some(("help", _)) => Self::Help,
                Some(("subgraph", _)) => Self::Subgraph,
                Some(("q", _)) => Self::Quit,
                _ => unreachable!(),
            },
            Err(_) => Self::NoMatch,
        }
    }
}

impl CommandTrie {
    pub fn new() -> CommandTrie {
        let trie_cmd = Trie::from_iter(subcommands().iter().map(|c| c.get_name().to_string()));

        let _trie_arg = Trie::from_iter([]);

        Self { trie_cmd, _trie_arg }
    }
}