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
use crate::command::{CmdArgs, Command, Handler};
use crate::error::Error;
use crate::help::help;
use crate::prelude::CResult;
use crate::{CliMeta, Clier, ExitCode};

use super::format::match_command;
use super::utils::{format_validate_reg_flags, global_flags};
use super::{AlreadyHasMeta, MissingMeta};

pub trait Runnable {
  fn add_command(self, cmd: Command) -> Self;
  fn root(self, description: &str, handler: Handler) -> Self;
  fn commands(self, cmd: Vec<Command>) -> Self;
  fn run(self) -> Result<ExitCode, Error>;
}

impl Clier<MissingMeta> {
  pub fn meta(self, meta: CliMeta) -> Clier<AlreadyHasMeta> {
    Clier {
      options: AlreadyHasMeta(meta),
      args: self.args,
      registered_commands: self.registered_commands,
    }
  }
}
impl Runnable for Clier<AlreadyHasMeta> {
  fn add_command(mut self, cmd: Command) -> Self {
    if cmd.name.contains('.') {
      panic!("{:?}", Error::InvalidFormat(String::from("'name' can't contain '.'",)));
    }
    self.registered_commands.push(cmd);
    self
  }

  fn root(self, description: &str, handler: Handler) -> Self {
    let options = self.clone().options.0;
    let mut root_command = Command::new("root", description, handler);

    if let Some(usage) = options.usage {
      root_command = root_command.clone().usage(usage.as_str());
    }
    self.add_command(root_command)
  }

  fn commands(mut self, cmd: Vec<Command>) -> Self {
    self.registered_commands = cmd;
    self
  }

  fn run(self) -> CResult<ExitCode> {
    if global_flags(&self.args, &self.registered_commands, self.clone().options.0) {
      return CResult::Ok(ExitCode(0));
    }

    let Some(command) = match_command(&self.registered_commands, &self.args.commands) else {
      return Err(Error::CommandNotFound(self.args.commands.join(" ")));
    };

    let registered_flags = match format_validate_reg_flags(&self.args.flags, &command) {
      CResult::Ok(value) => value,
      CResult::Err(value) => {
        help(&self.registered_commands, &self.args.commands, self.options.0);
        return CResult::Err(value);
      }
    };

    let args = CmdArgs { args: self.args, registered_flags };
    let exit_code = (command.handler)(args);

    Ok(ExitCode(exit_code))
  }
}