mod commands;
mod graph;
mod parser;
mod scanner;
mod tangle;
mod tui;
use anyhow::Result;
use clap::{ArgGroup, Parser, Subcommand};
#[derive(Parser)]
#[command(
name = "tngl",
about = "A human-authored, repo-native relational graph tool"
)]
struct Cli {
#[command(subcommand)]
command: Command,
}
#[derive(Subcommand)]
enum Command {
Init,
Update {
#[arg(long)]
silent: bool,
},
Status,
#[command(
group(
ArgGroup::new("inspect_query")
.args([
"orphans",
"dangling",
"edges",
"unreachable",
"comment_mismatches",
"reconcile_comments",
])
.multiple(false)
)
)]
Inspect {
#[arg(long)]
orphans: bool,
#[arg(long)]
dangling: bool,
#[arg(long)]
edges: Option<String>,
#[arg(long)]
unreachable: bool,
#[arg(long)]
comment_mismatches: bool,
#[arg(long)]
reconcile_comments: bool,
},
MarkOrphans,
List,
Edit,
View {
#[arg(long)]
demo: bool,
},
Open,
Setup,
}
fn main() -> Result<()> {
let cli = Cli::parse();
match cli.command {
Command::Init => commands::init::run(),
Command::Update { silent } => commands::update::run(silent),
Command::Status => commands::status::run(),
Command::Inspect {
orphans,
dangling,
edges,
unreachable,
comment_mismatches,
reconcile_comments,
} => {
if orphans {
commands::inspect::run_orphans()
} else if dangling {
commands::inspect::run_dangling()
} else if let Some(node) = edges {
commands::inspect::run_edges(&node)
} else if unreachable {
commands::inspect::run_unreachable()
} else if comment_mismatches {
commands::inspect::run_comment_mismatches()
} else if reconcile_comments {
commands::inspect::run_reconcile_comment_mismatches()
} else {
eprintln!(
"Specify one of: --orphans, --dangling, --edges <node>, --unreachable, --comment-mismatches, --reconcile-comments"
);
Ok(())
}
}
Command::MarkOrphans => commands::intentionalize_orphans::run(),
Command::List => commands::list::run(),
Command::Edit => commands::edit::run(),
Command::View { demo } => commands::view::run(demo),
Command::Open => commands::open::run(),
Command::Setup => commands::view::run_setup(),
}
}
#[cfg(test)]
mod tests {
use super::*;
use clap::error::ErrorKind;
#[test]
fn inspect_rejects_multiple_query_flags() {
let parsed = Cli::try_parse_from(["tngl", "inspect", "--orphans", "--dangling"]);
assert!(
parsed.is_err(),
"inspect flags should be mutually exclusive"
);
let err = parsed.err().expect("expected clap parse error");
assert_eq!(err.kind(), ErrorKind::ArgumentConflict);
}
#[test]
fn inspect_accepts_single_query_flag() {
let cli = Cli::try_parse_from(["tngl", "inspect", "--orphans"])
.expect("single inspect flag should parse");
match cli.command {
Command::Inspect { orphans, .. } => assert!(orphans),
_ => panic!("expected inspect command"),
}
}
}