1use crate::ui::messages::{MessageBuffer, MessageLevel, adjust_verbosity};
4use std::ffi::OsString;
5
6pub(crate) mod bootstrap;
7pub(crate) mod command_output;
8pub(crate) mod config_explain;
9pub(crate) mod dispatch;
10pub(crate) mod external;
11pub(crate) mod help;
12pub(crate) mod host;
13pub(crate) mod logging;
14pub(crate) mod repl_lifecycle;
15pub mod runtime;
16pub mod session;
17pub mod sink;
18#[cfg(test)]
19mod tests;
20pub(crate) mod timing;
21
22pub(crate) use bootstrap::*;
23pub(crate) use command_output::*;
24pub use host::run_from;
25pub(crate) use host::*;
26pub use runtime::{
27 AppClients, AppRuntime, AuthState, ConfigState, LaunchContext, RuntimeContext, TerminalKind,
28 UiState,
29};
30pub(crate) use session::AppStateInit;
31pub use session::{
32 AppSession, AppState, DebugTimingBadge, DebugTimingState, LastFailure, ReplScopeFrame,
33 ReplScopeStack,
34};
35pub use sink::{BufferedUiSink, StdIoUiSink, UiSink};
36
37#[derive(Debug, Default, Clone, Copy)]
38pub struct App;
39
40impl App {
41 pub const fn new() -> Self {
42 Self
43 }
44
45 pub fn run_from<I, T>(&self, args: I) -> miette::Result<i32>
46 where
47 I: IntoIterator<Item = T>,
48 T: Into<OsString> + Clone,
49 {
50 run_from(args)
51 }
52
53 pub fn with_sink<'a>(self, sink: &'a mut dyn UiSink) -> AppRunner<'a> {
54 AppRunner { app: self, sink }
55 }
56
57 pub fn run_with_sink<I, T>(&self, args: I, sink: &mut dyn UiSink) -> miette::Result<i32>
58 where
59 I: IntoIterator<Item = T>,
60 T: Into<OsString> + Clone,
61 {
62 host::run_from_with_sink(args, sink)
63 }
64
65 pub fn run_process<I, T>(&self, args: I) -> i32
66 where
67 I: IntoIterator<Item = T>,
68 T: Into<OsString> + Clone,
69 {
70 run_process(args)
71 }
72
73 pub fn run_process_with_sink<I, T>(&self, args: I, sink: &mut dyn UiSink) -> i32
74 where
75 I: IntoIterator<Item = T>,
76 T: Into<OsString> + Clone,
77 {
78 run_process_with_sink(args, sink)
79 }
80}
81
82pub struct AppRunner<'a> {
83 app: App,
84 sink: &'a mut dyn UiSink,
85}
86
87impl<'a> AppRunner<'a> {
88 pub fn run_from<I, T>(&mut self, args: I) -> miette::Result<i32>
89 where
90 I: IntoIterator<Item = T>,
91 T: Into<OsString> + Clone,
92 {
93 self.app.run_with_sink(args, self.sink)
94 }
95
96 pub fn run_process<I, T>(&mut self, args: I) -> i32
97 where
98 I: IntoIterator<Item = T>,
99 T: Into<OsString> + Clone,
100 {
101 self.app.run_process_with_sink(args, self.sink)
102 }
103}
104
105#[derive(Debug, Default, Clone, Copy)]
106pub struct AppBuilder;
107
108impl AppBuilder {
109 pub const fn new() -> Self {
110 Self
111 }
112
113 pub fn build(self) -> App {
114 App::new()
115 }
116
117 pub fn build_with_sink<'a>(self, sink: &'a mut dyn UiSink) -> AppRunner<'a> {
118 self.build().with_sink(sink)
119 }
120}
121
122pub fn run_process<I, T>(args: I) -> i32
123where
124 I: IntoIterator<Item = T>,
125 T: Into<OsString> + Clone,
126{
127 let mut sink = StdIoUiSink;
128 run_process_with_sink(args, &mut sink)
129}
130
131pub fn run_process_with_sink<I, T>(args: I, sink: &mut dyn UiSink) -> i32
132where
133 I: IntoIterator<Item = T>,
134 T: Into<OsString> + Clone,
135{
136 let args = args.into_iter().map(Into::into).collect::<Vec<OsString>>();
137 let message_verbosity = bootstrap_message_verbosity(&args);
138
139 match host::run_from_with_sink(args, sink) {
140 Ok(code) => code,
141 Err(err) => {
142 let mut messages = MessageBuffer::default();
143 messages.error(render_report_message(&err, message_verbosity));
144 sink.write_stderr(&messages.render_grouped(message_verbosity));
145 classify_exit_code(&err)
146 }
147 }
148}
149
150fn bootstrap_message_verbosity(args: &[OsString]) -> MessageLevel {
151 let mut verbose = 0u8;
152 let mut quiet = 0u8;
153
154 for token in args.iter().skip(1) {
155 let Some(value) = token.to_str() else {
156 continue;
157 };
158
159 if value == "--" {
160 break;
161 }
162
163 match value {
164 "--verbose" => {
165 verbose = verbose.saturating_add(1);
166 continue;
167 }
168 "--quiet" => {
169 quiet = quiet.saturating_add(1);
170 continue;
171 }
172 _ => {}
173 }
174
175 if value.starts_with('-') && !value.starts_with("--") {
176 for ch in value.chars().skip(1) {
177 match ch {
178 'v' => verbose = verbose.saturating_add(1),
179 'q' => quiet = quiet.saturating_add(1),
180 _ => {}
181 }
182 }
183 }
184 }
185
186 adjust_verbosity(MessageLevel::Success, verbose, quiet)
187}