1use serde::Serialize;
9
10use crate::config::Config;
11use crate::ctx::Ctx;
12use crate::errors;
13use crate::registry::Registry;
14use crate::spec::command::Command;
15use crate::spec::{XyzArgs, XyzSchema};
16use crate::{dispatch, spec};
17
18pub trait Definable {
21 fn register(&self, reg: &Registry) -> errors::Result<()>;
22}
23
24impl<T, R, F, E> Definable for Command<T, R, F, E>
25where
26 T: XyzArgs,
27 R: XyzSchema + Serialize,
28 F: Fn(&Ctx, &T) -> std::result::Result<R, E> + Send + Sync + 'static,
29 E: std::error::Error + Send + Sync + 'static,
30{
31 fn register(&self, reg: &Registry) -> errors::Result<()> {
32 self.entry().map(|e| {
33 let _ = reg.add(std::sync::Arc::new(e));
34 })
35 }
36}
37
38pub struct Builder<T, R, F, E>
41where
42 T: XyzArgs,
43 R: XyzSchema + Serialize,
44 F: Fn(&Ctx, &T) -> std::result::Result<R, E> + Send + Sync + 'static,
45 E: std::error::Error + Send + Sync + 'static,
46{
47 cmd: Command<T, R, F, E>,
48 reg: &'static Registry,
49 committed: bool,
50 config: Config,
51 err: Option<errors::Error>,
52}
53
54pub fn define<T, R, F, E>(name: &str, h: F) -> Builder<T, R, F, E>
56where
57 T: XyzArgs,
58 R: XyzSchema + Serialize,
59 F: Fn(&Ctx, &T) -> std::result::Result<R, E> + Send + Sync + 'static,
60 E: std::error::Error + Send + Sync + 'static,
61{
62 Builder {
63 cmd: Command::new(name, h),
64 reg: Registry::default(),
65 committed: false,
66 config: Config::default(),
67 err: None,
68 }
69}
70
71impl<T, R, F, E> Builder<T, R, F, E>
72where
73 T: XyzArgs,
74 R: XyzSchema + Serialize,
75 F: Fn(&Ctx, &T) -> std::result::Result<R, E> + Send + Sync + 'static,
76 E: std::error::Error + Send + Sync + 'static,
77{
78 pub fn summary(mut self, s: impl Into<String>) -> Self {
80 self.cmd = self.cmd.summary(s);
81 self
82 }
83
84 pub fn description(mut self, s: impl Into<String>) -> Self {
86 self.cmd = self.cmd.description(s);
87 self
88 }
89
90 pub fn cli(mut self, h: spec::CliHints) -> Self {
92 self.cmd = self.cmd.cli(h);
93 self
94 }
95
96 pub fn http(mut self, h: spec::HTTPHints) -> Self {
98 self.cmd = self.cmd.http(h);
99 self
100 }
101
102 pub fn mcp(mut self, h: spec::MCPHints) -> Self {
104 self.cmd = self.cmd.mcp(h);
105 self
106 }
107
108 pub fn configure(mut self, cfg: Config) -> Self {
111 self.config = cfg;
112 self
113 }
114
115 pub fn also(mut self, cmds: &[&dyn Definable]) -> Self {
118 if self.err.is_none() && !self.committed {
119 match self.commit_current() {
120 Ok(()) => self.committed = true,
121 Err(e) => self.err = Some(e),
122 }
123 }
124 if self.err.is_none() {
125 for c in cmds {
126 if let Err(e) = c.register(self.reg) {
127 self.err = Some(e);
128 break;
129 }
130 }
131 }
132 self
133 }
134
135 fn commit_current(&self) -> errors::Result<()> {
136 self.cmd.entry().map(|entry| {
137 let _ = self.reg.add(std::sync::Arc::new(entry));
138 })
139 }
140
141 pub fn run(self) -> ! {
144 let args: Vec<String> = std::env::args().skip(1).collect();
145 std::process::exit(self.run_args(args));
146 }
147
148 pub fn run_config(self, cfg: Config) -> ! {
150 let args: Vec<String> = std::env::args().skip(1).collect();
151 std::process::exit(self.run_args_config(args, cfg));
152 }
153
154 pub fn run_args(mut self, args: Vec<String>) -> i32 {
157 let cfg = std::mem::take(&mut self.config);
158 self.run_args_config(args, cfg)
159 }
160
161 pub fn run_args_config(mut self, args: Vec<String>, cfg: Config) -> i32 {
163 if self.err.is_none() && !self.committed {
164 match self.commit_current() {
165 Ok(()) => self.committed = true,
166 Err(e) => self.err = Some(e),
167 }
168 }
169 if let Some(e) = self.err {
170 eprintln!("{e}");
171 return 2;
172 }
173 dispatch::run_config(self.reg, args, cfg)
174 }
175}
176
177impl<T, R, F, E> Definable for Builder<T, R, F, E>
178where
179 T: XyzArgs,
180 R: XyzSchema + Serialize,
181 F: Fn(&Ctx, &T) -> std::result::Result<R, E> + Send + Sync + 'static,
182 E: std::error::Error + Send + Sync + 'static,
183{
184 fn register(&self, reg: &Registry) -> errors::Result<()> {
185 self.cmd.entry().map(|entry| {
186 let _ = reg.add(std::sync::Arc::new(entry));
187 })
188 }
189}