1pub mod commands;
2pub mod config;
3pub mod error;
4pub mod output;
5pub mod templates;
6pub mod utils;
7
8use clap::{Parser, Subcommand, ValueEnum};
9
10#[derive(Debug, Clone, Copy, ValueEnum, Default)]
12pub enum LogLevel {
13 Error,
15 #[default]
17 Warn,
18 Info,
20 Debug,
22 Trace,
24}
25
26impl From<LogLevel> for tracing::Level {
27 fn from(level: LogLevel) -> Self {
28 match level {
29 LogLevel::Error => tracing::Level::ERROR,
30 LogLevel::Warn => tracing::Level::WARN,
31 LogLevel::Info => tracing::Level::INFO,
32 LogLevel::Debug => tracing::Level::DEBUG,
33 LogLevel::Trace => tracing::Level::TRACE,
34 }
35 }
36}
37
38#[derive(Debug, Parser)]
41#[command(name = "theater")]
42#[command(author, version, about, long_about = None)]
43pub struct Cli {
44 #[arg(short, long, global = true, value_enum, default_value = "warn")]
46 pub log_level: LogLevel,
47
48 #[arg(long, global = true)]
50 pub json: bool,
51
52 #[command(subcommand)]
53 pub command: Commands,
54}
55
56#[derive(Debug, Subcommand)]
57pub enum Commands {
58 #[command(name = "create")]
60 Create(commands::create::CreateArgs),
61
62 #[command(name = "build")]
64 Build(commands::build::BuildArgs),
65
66 #[command(name = "start")]
68 Start(commands::start::StartArgs),
69
70 #[command(name = "chains")]
72 Chains(commands::chains::ChainsArgs),
73
74 #[command(name = "completion")]
76 Completion(commands::completion::CompletionArgs),
77
78 #[command(name = "dynamic-completion", hide = true)]
80 DynamicCompletion(commands::dynamic_completion::DynamicCompletionArgs),
81}
82
83pub async fn run(
85 cli: Cli,
86 config: config::Config,
87 shutdown_token: tokio_util::sync::CancellationToken,
88) -> anyhow::Result<()> {
89 let output = output::OutputManager::new(config.output.clone());
91
92 let ctx = CommandContext {
94 config,
95 output,
96 log_level: cli.log_level,
97 json: cli.json,
98 shutdown_token: shutdown_token.clone(),
99 };
100
101 let command_future = async {
103 match &cli.command {
104 Commands::Create(args) => commands::create::execute_async(args, &ctx)
105 .await
106 .map_err(anyhow::Error::from),
107 Commands::Build(args) => commands::build::execute_async(args, &ctx)
108 .await
109 .map_err(anyhow::Error::from),
110 Commands::Start(args) => commands::start::execute_async(args, &ctx)
111 .await
112 .map_err(anyhow::Error::from),
113 Commands::Chains(args) => commands::chains::execute_async(args, &ctx)
114 .await
115 .map_err(anyhow::Error::from),
116 Commands::Completion(args) => commands::completion::execute_async(args, &ctx)
117 .await
118 .map_err(anyhow::Error::from),
119 Commands::DynamicCompletion(args) => {
120 commands::dynamic_completion::execute_async(args, &ctx)
121 .await
122 .map_err(anyhow::Error::from)
123 }
124 }
125 };
126
127 let result = tokio::select! {
129 result = command_future => result,
130 _ = shutdown_token.cancelled() => {
131 return Ok(());
132 }
133 };
134
135 match result {
137 Ok(()) => Ok(()),
138 Err(e) => {
139 if let Some(cli_error) = e.downcast_ref::<error::CliError>() {
141 ctx.output.error(&cli_error.user_message())?;
142 if ctx.is_verbose() {
143 eprintln!("\nDebug info: {:?}", cli_error);
144 }
145 } else {
146 ctx.output.error(&format!("Error: {}", e))?;
147 if ctx.is_verbose() {
148 eprintln!("\nDebug info: {:?}", e);
149 }
150 }
151 std::process::exit(1);
152 }
153 }
154}
155
156pub struct CommandContext {
158 pub config: config::Config,
159 pub output: output::OutputManager,
160 pub log_level: LogLevel,
161 pub json: bool,
162 pub shutdown_token: tokio_util::sync::CancellationToken,
163}
164
165impl CommandContext {
166 pub fn is_verbose(&self) -> bool {
168 matches!(self.log_level, LogLevel::Debug | LogLevel::Trace)
169 }
170}