1use crate::native::NativeCommandRegistry;
27use crate::ui::messages::{MessageBuffer, MessageLevel, adjust_verbosity};
28use std::ffi::OsString;
29
30pub(crate) mod assembly;
31pub(crate) mod bootstrap;
32pub(crate) mod command_output;
33pub(crate) mod config_explain;
34pub(crate) mod dispatch;
35pub(crate) mod external;
36pub(crate) mod help;
37pub(crate) mod host;
38pub(crate) mod logging;
39pub(crate) mod rebuild;
40pub(crate) mod repl_lifecycle;
41pub(crate) mod runtime;
42pub(crate) mod session;
43pub(crate) mod sink;
45#[cfg(test)]
46mod tests;
47pub(crate) mod timing;
48
49pub(crate) use bootstrap::*;
50pub(crate) use command_output::*;
51pub use host::run_from;
52pub(crate) use host::*;
53pub(crate) use repl_lifecycle::rebuild_repl_in_place;
54pub use runtime::{
55 AppClients, AppClientsBuilder, AppRuntime, AuthState, ConfigState, LaunchContext,
56 LaunchContextBuilder, RuntimeContext, TerminalKind, UiState, UiStateBuilder,
57};
58#[cfg(test)]
59pub(crate) use session::AppStateInit;
60pub use session::{
61 AppSession, AppSessionBuilder, AppState, AppStateBuilder, DebugTimingBadge, DebugTimingState,
62 LastFailure, ReplScopeFrame, ReplScopeStack,
63};
64pub use sink::{BufferedUiSink, StdIoUiSink, UiSink};
65
66#[derive(Clone, Default)]
67pub struct App {
72 native_commands: NativeCommandRegistry,
73}
74
75impl App {
76 pub fn new() -> Self {
78 Self {
79 native_commands: NativeCommandRegistry::default(),
80 }
81 }
82
83 pub fn with_native_commands(mut self, native_commands: NativeCommandRegistry) -> Self {
85 self.native_commands = native_commands;
86 self
87 }
88
89 pub fn run_from<I, T>(&self, args: I) -> miette::Result<i32>
91 where
92 I: IntoIterator<Item = T>,
93 T: Into<OsString> + Clone,
94 {
95 host::run_from_with_sink_and_native(args, &mut StdIoUiSink, &self.native_commands)
96 }
97
98 pub fn with_sink<'a>(self, sink: &'a mut dyn UiSink) -> AppRunner<'a> {
100 AppRunner { app: self, sink }
101 }
102
103 pub fn run_with_sink<I, T>(&self, args: I, sink: &mut dyn UiSink) -> miette::Result<i32>
105 where
106 I: IntoIterator<Item = T>,
107 T: Into<OsString> + Clone,
108 {
109 host::run_from_with_sink_and_native(args, sink, &self.native_commands)
110 }
111
112 pub fn run_process<I, T>(&self, args: I) -> i32
114 where
115 I: IntoIterator<Item = T>,
116 T: Into<OsString> + Clone,
117 {
118 let mut sink = StdIoUiSink;
119 self.run_process_with_sink(args, &mut sink)
120 }
121
122 pub fn run_process_with_sink<I, T>(&self, args: I, sink: &mut dyn UiSink) -> i32
124 where
125 I: IntoIterator<Item = T>,
126 T: Into<OsString> + Clone,
127 {
128 let args = args.into_iter().map(Into::into).collect::<Vec<OsString>>();
129 let message_verbosity = bootstrap_message_verbosity(&args);
130
131 match host::run_from_with_sink_and_native(args, sink, &self.native_commands) {
132 Ok(code) => code,
133 Err(err) => {
134 let mut messages = MessageBuffer::default();
135 messages.error(render_report_message(&err, message_verbosity));
136 sink.write_stderr(&messages.render_grouped(message_verbosity));
137 classify_exit_code(&err)
138 }
139 }
140 }
141}
142
143pub struct AppRunner<'a> {
145 app: App,
146 sink: &'a mut dyn UiSink,
147}
148
149impl<'a> AppRunner<'a> {
150 pub fn run_from<I, T>(&mut self, args: I) -> miette::Result<i32>
152 where
153 I: IntoIterator<Item = T>,
154 T: Into<OsString> + Clone,
155 {
156 self.app.run_with_sink(args, self.sink)
157 }
158
159 pub fn run_process<I, T>(&mut self, args: I) -> i32
161 where
162 I: IntoIterator<Item = T>,
163 T: Into<OsString> + Clone,
164 {
165 self.app.run_process_with_sink(args, self.sink)
166 }
167}
168
169#[derive(Clone, Default)]
170pub struct AppBuilder {
174 native_commands: NativeCommandRegistry,
175}
176
177impl AppBuilder {
178 pub fn new() -> Self {
180 Self {
181 native_commands: NativeCommandRegistry::default(),
182 }
183 }
184
185 pub fn with_native_commands(mut self, native_commands: NativeCommandRegistry) -> Self {
187 self.native_commands = native_commands;
188 self
189 }
190
191 pub fn build(self) -> App {
193 App::new().with_native_commands(self.native_commands)
194 }
195
196 pub fn build_with_sink<'a>(self, sink: &'a mut dyn UiSink) -> AppRunner<'a> {
198 self.build().with_sink(sink)
199 }
200}
201
202pub fn run_process<I, T>(args: I) -> i32
204where
205 I: IntoIterator<Item = T>,
206 T: Into<OsString> + Clone,
207{
208 let mut sink = StdIoUiSink;
209 run_process_with_sink(args, &mut sink)
210}
211
212pub fn run_process_with_sink<I, T>(args: I, sink: &mut dyn UiSink) -> i32
214where
215 I: IntoIterator<Item = T>,
216 T: Into<OsString> + Clone,
217{
218 let args = args.into_iter().map(Into::into).collect::<Vec<OsString>>();
219 let message_verbosity = bootstrap_message_verbosity(&args);
220
221 match host::run_from_with_sink(args, sink) {
222 Ok(code) => code,
223 Err(err) => {
224 let mut messages = MessageBuffer::default();
225 messages.error(render_report_message(&err, message_verbosity));
226 sink.write_stderr(&messages.render_grouped(message_verbosity));
227 classify_exit_code(&err)
228 }
229 }
230}
231
232fn bootstrap_message_verbosity(args: &[OsString]) -> MessageLevel {
233 let mut verbose = 0u8;
234 let mut quiet = 0u8;
235
236 for token in args.iter().skip(1) {
237 let Some(value) = token.to_str() else {
238 continue;
239 };
240
241 if value == "--" {
242 break;
243 }
244
245 match value {
246 "--verbose" => {
247 verbose = verbose.saturating_add(1);
248 continue;
249 }
250 "--quiet" => {
251 quiet = quiet.saturating_add(1);
252 continue;
253 }
254 _ => {}
255 }
256
257 if value.starts_with('-') && !value.starts_with("--") {
258 for ch in value.chars().skip(1) {
259 match ch {
260 'v' => verbose = verbose.saturating_add(1),
261 'q' => quiet = quiet.saturating_add(1),
262 _ => {}
263 }
264 }
265 }
266 }
267
268 adjust_verbosity(MessageLevel::Success, verbose, quiet)
269}