1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#[cfg(debug_assertions)]
mod playground;
mod rcon;
#[cfg(feature = "repl")]
mod repl;
#[cfg(feature = "lua")]
mod roll_seed;
mod start;

use crate::context::Context;
use crate::{APP_ABOUT, APP_AUTHOR, APP_NAME};
use clap::{value_parser, Arg, ArgMatches, Command};
use clap_complete::{generate, Generator, Shell};
use factorio_bot_core::miette::Result;
use std::future::Future;
use std::io;
use std::pin::Pin;

pub fn subcommands() -> Vec<Box<dyn Subcommand>> {
  vec![
    rcon::build(),
    #[cfg(feature = "lua")]
    roll_seed::build(),
    #[cfg(debug_assertions)]
    playground::build(),
    #[cfg(feature = "repl")]
    repl::build(),
    start::build(),
  ]
}

pub async fn start(mut context: Context) -> Result<Option<Command<'static>>> {
  let mut app = Command::new(APP_NAME)
    .version(env!("CARGO_PKG_VERSION"))
    .author(APP_AUTHOR)
    .about(APP_ABOUT)
    .arg(
      Arg::new("shell")
        .help("generate shell-completion script")
        .long("generate")
        .value_parser(value_parser!(Shell)),
    );
  let subcommands = subcommands();
  for subcommand in &subcommands {
    app = app.subcommand(subcommand.build_command());
  }
  let matches = app.clone().get_matches();
  if let Ok(shell) = matches.value_of_t::<Shell>("shell") {
    eprintln!("Generating completion file for {}...", shell);
    print_completions(shell, &mut app);
    return Ok(None);
  }
  for subcommand in &subcommands {
    if let Some(matches) = matches.subcommand_matches(&subcommand.name()) {
      let callback = subcommand.build_callback();
      callback(matches.clone(), &mut context).await?;
      return Ok(None);
    }
  }
  Ok(Some(app))
}

fn print_completions<G: Generator>(gen: G, cmd: &mut Command) {
  generate(gen, cmd, cmd.get_name().to_owned(), &mut io::stdout());
}

pub trait Subcommand {
  fn name(&self) -> &str;
  fn build_command(&self) -> Command<'static>;
  fn build_callback(&self) -> SubcommandCallback;
}

pub type SubcommandCallback =
  fn(ArgMatches, &'_ mut Context) -> Pin<Box<dyn Future<Output = Result<()>> + '_>>;