#![allow(clippy::multiple_crate_versions)]
use std::{
ffi::OsString,
io,
io::Write,
process::{ExitCode, Termination},
};
use clap::{ArgAction, Args, Parser, Subcommand};
use error_stack::Result;
use forkfs::SessionOperand;
#[derive(Parser, Debug)]
#[command(version, author = "Alex Saveau (@SUPERCILEX)")]
#[command(infer_subcommands = true, infer_long_args = true)]
#[command(next_display_order = None)]
#[command(max_term_width = 100)]
#[command(disable_help_flag = true)]
#[cfg_attr(test, command(help_expected = true))]
struct ForkFs {
#[command(subcommand)]
cmd: Cmd,
#[arg(short, long, short_alias = '?', global = true)]
#[arg(action = ArgAction::Help, help = "Print help information (use `--help` for more detail)")]
#[arg(long_help = "Print help information (use `-h` for a summary)")]
help: Option<bool>,
}
#[derive(Subcommand, Debug)]
enum Cmd {
#[command(alias = "execute")]
Run(Run),
#[command(subcommand)]
Sessions(Sessions),
}
#[derive(Args, Debug)]
#[command(arg_required_else_help = true)]
struct Run {
#[arg(short = 's', long = "session", short_alias = 'n', aliases = & ["name", "id"])]
#[arg(default_value = "default")]
session: String,
#[arg(required = true)]
command: Vec<OsString>,
}
#[derive(Subcommand, Debug)]
enum Sessions {
#[command(alias = "ls")]
List,
#[command(alias = "close")]
Stop(SessionCmd),
#[command(alias = "destroy")]
Delete(SessionCmd),
}
#[derive(Args, Debug)]
#[command(arg_required_else_help = true)]
struct SessionCmd {
#[arg(short = 'a', long = "all", group = "names")]
all: bool,
#[arg(required = true, group = "names")]
sessions: Vec<String>,
}
fn main() -> ExitCode {
let args = ForkFs::parse();
match forkfs(args) {
Ok(o) => o.report(),
Err(err) => {
drop(writeln!(io::stderr(), "Error: {err:?}"));
err.report()
}
}
}
fn forkfs(ForkFs { cmd, help: _ }: ForkFs) -> Result<(), forkfs::Error> {
match cmd {
Cmd::Run(r) => run(r),
Cmd::Sessions(s) => sessions(s),
}
}
fn run(Run { session, command }: Run) -> Result<(), forkfs::Error> {
forkfs::run(&session, command.as_slice())
}
fn sessions(sessions: Sessions) -> Result<(), forkfs::Error> {
match sessions {
Sessions::List => forkfs::list_sessions(),
Sessions::Stop(SessionCmd { all, sessions }) => forkfs::stop_sessions(if all {
SessionOperand::All
} else {
SessionOperand::List(sessions.as_slice())
}),
Sessions::Delete(SessionCmd { all, sessions }) => forkfs::delete_sessions(if all {
SessionOperand::All
} else {
SessionOperand::List(sessions.as_slice())
}),
}
}
#[cfg(test)]
mod cli_tests {
use std::fmt::Write;
use clap::{Command, CommandFactory};
use expect_test::expect_file;
use super::*;
#[test]
fn verify_app() {
ForkFs::command().debug_assert();
}
#[test]
#[cfg_attr(miri, ignore)] fn help_for_review() {
let mut command = ForkFs::command();
command.build();
let mut long = String::new();
let mut short = String::new();
write_help(&mut long, &mut command, LongOrShortHelp::Long);
write_help(&mut short, &mut command, LongOrShortHelp::Short);
expect_file!["../command-reference.golden"].assert_eq(&long);
expect_file!["../command-reference-short.golden"].assert_eq(&short);
}
#[derive(Copy, Clone)]
enum LongOrShortHelp {
Long,
Short,
}
fn write_help(buffer: &mut impl Write, cmd: &mut Command, long_or_short_help: LongOrShortHelp) {
write!(
buffer,
"{}",
match long_or_short_help {
LongOrShortHelp::Long => cmd.render_long_help(),
LongOrShortHelp::Short => cmd.render_help(),
}
)
.unwrap();
for sub in cmd.get_subcommands_mut() {
writeln!(buffer).unwrap();
writeln!(buffer, "---").unwrap();
writeln!(buffer).unwrap();
write_help(buffer, sub, long_or_short_help);
}
}
}