pub mod commands;
pub mod config;
pub mod error;
pub mod output;
pub mod templates;
pub mod utils;
use clap::{Parser, Subcommand, ValueEnum};
#[derive(Debug, Clone, Copy, ValueEnum, Default)]
pub enum LogLevel {
Error,
#[default]
Warn,
Info,
Debug,
Trace,
}
impl From<LogLevel> for tracing::Level {
fn from(level: LogLevel) -> Self {
match level {
LogLevel::Error => tracing::Level::ERROR,
LogLevel::Warn => tracing::Level::WARN,
LogLevel::Info => tracing::Level::INFO,
LogLevel::Debug => tracing::Level::DEBUG,
LogLevel::Trace => tracing::Level::TRACE,
}
}
}
#[derive(Debug, Parser)]
#[command(name = "theater")]
#[command(author, version, about, long_about = None)]
pub struct Cli {
#[arg(short, long, global = true, value_enum, default_value = "warn")]
pub log_level: LogLevel,
#[arg(long, global = true)]
pub json: bool,
#[command(subcommand)]
pub command: Commands,
}
#[derive(Debug, Subcommand)]
pub enum Commands {
#[command(name = "create")]
Create(commands::create::CreateArgs),
#[command(name = "build")]
Build(commands::build::BuildArgs),
#[command(name = "start")]
Start(commands::start::StartArgs),
#[command(name = "chains")]
Chains(commands::chains::ChainsArgs),
#[command(name = "completion")]
Completion(commands::completion::CompletionArgs),
#[command(name = "dynamic-completion", hide = true)]
DynamicCompletion(commands::dynamic_completion::DynamicCompletionArgs),
}
pub async fn run(
cli: Cli,
config: config::Config,
shutdown_token: tokio_util::sync::CancellationToken,
) -> anyhow::Result<()> {
let output = output::OutputManager::new(config.output.clone());
let ctx = CommandContext {
config,
output,
log_level: cli.log_level,
json: cli.json,
shutdown_token: shutdown_token.clone(),
};
let command_future = async {
match &cli.command {
Commands::Create(args) => commands::create::execute_async(args, &ctx)
.await
.map_err(anyhow::Error::from),
Commands::Build(args) => commands::build::execute_async(args, &ctx)
.await
.map_err(anyhow::Error::from),
Commands::Start(args) => commands::start::execute_async(args, &ctx)
.await
.map_err(anyhow::Error::from),
Commands::Chains(args) => commands::chains::execute_async(args, &ctx)
.await
.map_err(anyhow::Error::from),
Commands::Completion(args) => commands::completion::execute_async(args, &ctx)
.await
.map_err(anyhow::Error::from),
Commands::DynamicCompletion(args) => {
commands::dynamic_completion::execute_async(args, &ctx)
.await
.map_err(anyhow::Error::from)
}
}
};
let result = tokio::select! {
result = command_future => result,
_ = shutdown_token.cancelled() => {
return Ok(());
}
};
match result {
Ok(()) => Ok(()),
Err(e) => {
if let Some(cli_error) = e.downcast_ref::<error::CliError>() {
ctx.output.error(&cli_error.user_message())?;
if ctx.is_verbose() {
eprintln!("\nDebug info: {:?}", cli_error);
}
} else {
ctx.output.error(&format!("Error: {}", e))?;
if ctx.is_verbose() {
eprintln!("\nDebug info: {:?}", e);
}
}
std::process::exit(1);
}
}
}
pub struct CommandContext {
pub config: config::Config,
pub output: output::OutputManager,
pub log_level: LogLevel,
pub json: bool,
pub shutdown_token: tokio_util::sync::CancellationToken,
}
impl CommandContext {
pub fn is_verbose(&self) -> bool {
matches!(self.log_level, LogLevel::Debug | LogLevel::Trace)
}
}