1pub mod client;
2pub mod commands;
3pub mod config;
4pub mod error;
5pub mod output;
6pub mod templates;
7pub mod tui;
8pub mod utils;
9
10use clap::{Parser, Subcommand};
11
12#[derive(Debug, Parser)]
15#[command(name = "theater")]
16#[command(author, version, about, long_about = None)]
17pub struct Cli {
18 #[arg(short, long, global = true)]
20 pub verbose: bool,
21
22 #[arg(long, global = true)]
24 pub json: bool,
25
26 #[command(subcommand)]
27 pub command: Commands,
28}
29
30#[derive(Debug, Subcommand)]
31pub enum Commands {
32 #[command(name = "create")]
34 Create(commands::create::CreateArgs),
35
36 #[command(name = "build")]
38 Build(commands::build::BuildArgs),
39
40 #[command(name = "start")]
42 Start(commands::start::StartArgs),
43
44 #[command(name = "subscribe")]
46 Subscribe(commands::subscribe::SubscribeArgs),
47
48 #[command(name = "list")]
50 List(commands::list::ListArgs),
51
52 #[command(name = "state")]
54 State(commands::state::StateArgs),
55
56 #[command(name = "events")]
58 Events(commands::events::EventsArgs),
59
60 #[command(name = "events-explore")]
62 EventsExplore(commands::events_explore::ExploreArgs),
63
64 #[command(name = "inspect")]
66 Inspect(commands::inspect::InspectArgs),
67
68 #[command(name = "stop")]
70 Stop(commands::stop::StopArgs),
71
72 #[command(name = "message")]
74 Message(commands::message::MessageArgs),
75
76 #[command(name = "list-stored")]
78 ListStored(commands::list_stored::ListStoredArgs),
79
80 #[command(name = "channel")]
82 Channel(commands::channel::ChannelArgs),
83
84 #[command(name = "completion")]
86 Completion(commands::completion::CompletionArgs),
87
88 #[command(name = "dynamic-completion", hide = true)]
90 DynamicCompletion(commands::dynamic_completion::DynamicCompletionArgs),
91}
92
93pub async fn run(
95 cli: Cli,
96 config: config::Config,
97 _shutdown_rx: tokio::sync::oneshot::Receiver<()>,
98) -> anyhow::Result<()> {
99 let output = output::OutputManager::new(config.output.clone());
101
102 let ctx = CommandContext {
104 config,
105 output,
106 verbose: cli.verbose,
107 json: cli.json,
108 };
109
110 let result = match &cli.command {
112 Commands::Subscribe(args) => commands::subscribe::execute_async(args, &ctx)
113 .await
114 .map_err(|e| anyhow::Error::from(e)),
115 Commands::Create(args) => commands::create::execute_async(args, &ctx)
116 .await
117 .map_err(|e| anyhow::Error::from(e)),
118 Commands::Build(args) => commands::build::execute_async(args, &ctx)
119 .await
120 .map_err(|e| anyhow::Error::from(e)),
121 Commands::List(args) => commands::list::execute_async(args, &ctx)
122 .await
123 .map_err(|e| anyhow::Error::from(e)),
124 Commands::State(args) => commands::state::execute_async(args, &ctx)
125 .await
126 .map_err(|e| anyhow::Error::from(e)),
127 Commands::Events(args) => commands::events::execute_async(args, &ctx)
128 .await
129 .map_err(|e| anyhow::Error::from(e)),
130 Commands::EventsExplore(args) => commands::events_explore::execute_async(args, &ctx)
131 .await
132 .map_err(|e| anyhow::Error::from(e)),
133 Commands::Inspect(args) => commands::inspect::execute_async(args, &ctx)
134 .await
135 .map_err(|e| anyhow::Error::from(e)),
136 Commands::Start(args) => commands::start::execute_async(args, &ctx)
137 .await
138 .map_err(|e| anyhow::Error::from(e)),
139 Commands::Stop(args) => commands::stop::execute_async(args, &ctx)
140 .await
141 .map_err(|e| anyhow::Error::from(e)),
142 Commands::Message(args) => commands::message::execute_async(args, &ctx)
143 .await
144 .map_err(|e| anyhow::Error::from(e)),
145 Commands::Channel(args) => match &args.command {
146 commands::channel::ChannelCommands::Open(open_args) => {
147 commands::channel::open::execute_async(open_args, &ctx)
148 .await
149 .map_err(|e| anyhow::Error::from(e))
150 }
151 },
152 Commands::ListStored(args) => commands::list_stored::execute_async(args, &ctx)
153 .await
154 .map_err(|e| anyhow::Error::from(e)),
155 Commands::Completion(args) => commands::completion::execute_async(args, &ctx)
156 .await
157 .map_err(|e| anyhow::Error::from(e)),
158 Commands::DynamicCompletion(args) => {
159 commands::dynamic_completion::execute_async(args, &ctx)
160 .await
161 .map_err(|e| anyhow::Error::from(e))
162 }
163 };
164
165 match result {
167 Ok(()) => Ok(()),
168 Err(e) => {
169 if let Some(cli_error) = e.downcast_ref::<error::CliError>() {
171 ctx.output.error(&cli_error.user_message())?;
172 if ctx.verbose {
173 eprintln!("\nDebug info: {:?}", cli_error);
174 }
175 } else {
176 ctx.output.error(&format!("Error: {}", e))?;
177 if ctx.verbose {
178 eprintln!("\nDebug info: {:?}", e);
179 }
180 }
181 std::process::exit(1);
182 }
183 }
184}
185
186pub struct CommandContext {
188 pub config: config::Config,
189 pub output: output::OutputManager,
190 pub verbose: bool,
191 pub json: bool,
192}
193
194impl CommandContext {
195 pub fn create_client(&self) -> client::TheaterClient {
197 client::TheaterClient::new(self.config.server.default_address)
198 }
199
200 pub fn server_address(
202 &self,
203 override_addr: Option<std::net::SocketAddr>,
204 ) -> std::net::SocketAddr {
205 override_addr.unwrap_or(self.config.server.default_address)
206 }
207}