1use crate::core::output::{ColorMode, OutputFormat, RenderMode, UnicodeMode};
12use crate::core::output_model::OutputResult;
13use crate::ui::chrome::{RuledSectionPolicy, SectionFrameStyle};
14use crate::ui::theme;
15use crate::ui::{
16 HelpChromeSettings, RenderBackend, RenderSettings, StyleOverrides, TableBorderStyle,
17 TableOverflow, ThemeDefinition,
18};
19
20#[derive(Debug, Clone, PartialEq, Eq)]
22pub struct ResolvedRenderSettings {
23 pub backend: RenderBackend,
25 pub color: bool,
27 pub unicode: bool,
29 pub width: Option<usize>,
31 pub margin: usize,
33 pub indent_size: usize,
35 pub short_list_max: usize,
37 pub medium_list_max: usize,
39 pub grid_padding: usize,
41 pub grid_columns: Option<usize>,
43 pub column_weight: usize,
45 pub table_overflow: TableOverflow,
47 pub table_border: TableBorderStyle,
49 pub help_table_border: TableBorderStyle,
51 pub theme_name: String,
53 pub theme: ThemeDefinition,
55 pub style_overrides: StyleOverrides,
57 pub chrome_frame: SectionFrameStyle,
59}
60
61#[derive(Debug, Clone, PartialEq, Eq)]
67pub(crate) struct ResolvedRenderPlan {
68 pub(crate) format: OutputFormat,
70 pub(crate) render: ResolvedRenderSettings,
72 pub(crate) guide: ResolvedGuideRenderSettings,
74 pub(crate) mreg: ResolvedMregBuildSettings,
76}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq)]
79pub(crate) struct ResolvedHelpChromeSettings {
80 pub(crate) table_border: TableBorderStyle,
81 pub(crate) entry_indent: Option<usize>,
82 pub(crate) entry_gap: Option<usize>,
83 pub(crate) section_spacing: Option<usize>,
84}
85
86#[derive(Debug, Clone, Copy, PartialEq, Eq)]
87pub(crate) struct ResolvedGuideRenderSettings {
88 pub(crate) frame_style: SectionFrameStyle,
89 pub(crate) ruled_section_policy: RuledSectionPolicy,
90 pub(crate) help_chrome: ResolvedHelpChromeSettings,
91}
92
93#[derive(Debug, Clone, Copy, PartialEq, Eq)]
94pub(crate) struct ResolvedMregBuildSettings {
95 pub(crate) short_list_max: usize,
96 pub(crate) medium_list_max: usize,
97 pub(crate) indent_size: usize,
98 pub(crate) stack_min_col_width: usize,
99 pub(crate) stack_overflow_ratio: usize,
100}
101
102impl RenderSettings {
103 pub(crate) fn resolve_guide_render_settings(&self) -> ResolvedGuideRenderSettings {
104 ResolvedGuideRenderSettings {
105 frame_style: self.chrome_frame,
106 ruled_section_policy: self.ruled_section_policy,
107 help_chrome: self.help_chrome.resolve(self.table_border),
108 }
109 }
110
111 pub(crate) fn resolve_mreg_build_settings(&self) -> ResolvedMregBuildSettings {
112 ResolvedMregBuildSettings {
113 short_list_max: self.short_list_max.max(1),
114 medium_list_max: self.medium_list_max.max(self.short_list_max.max(1) + 1),
115 indent_size: self.indent_size.max(1),
116 stack_min_col_width: self.mreg_stack_min_col_width.max(1),
117 stack_overflow_ratio: self.mreg_stack_overflow_ratio.max(100),
118 }
119 }
120
121 fn resolve_color_mode(&self) -> bool {
122 match self.color {
123 ColorMode::Always => true,
124 ColorMode::Never => false,
125 ColorMode::Auto => !self.runtime.no_color && self.runtime.stdout_is_tty,
126 }
127 }
128
129 fn resolve_unicode_mode(&self) -> bool {
130 match self.unicode {
131 UnicodeMode::Always => true,
132 UnicodeMode::Never => false,
133 UnicodeMode::Auto => {
134 if !self.runtime.stdout_is_tty {
135 return false;
136 }
137 if matches!(self.runtime.terminal.as_deref(), Some("dumb")) {
138 return false;
139 }
140 match self.runtime.locale_utf8 {
141 Some(true) => true,
142 Some(false) => false,
143 None => true,
144 }
145 }
146 }
147 }
148
149 pub fn resolve_render_settings(&self) -> ResolvedRenderSettings {
173 let backend = match self.mode {
174 RenderMode::Plain => RenderBackend::Plain,
175 RenderMode::Rich => RenderBackend::Rich,
176 RenderMode::Auto => {
177 if matches!(self.color, ColorMode::Always)
178 || matches!(self.unicode, UnicodeMode::Always)
179 {
180 RenderBackend::Rich
181 } else if !self.runtime.stdout_is_tty
182 || matches!(self.runtime.terminal.as_deref(), Some("dumb"))
183 {
184 RenderBackend::Plain
185 } else {
186 RenderBackend::Rich
187 }
188 }
189 };
190
191 let theme = self
192 .theme
193 .clone()
194 .unwrap_or_else(|| theme::resolve_theme(&self.theme_name));
195 let theme_name = theme::normalize_theme_name(&theme.id);
196
197 match backend {
198 RenderBackend::Plain => ResolvedRenderSettings {
199 backend,
200 color: false,
201 unicode: false,
202 width: self.resolve_width(),
203 margin: self.margin,
204 indent_size: self.indent_size.max(1),
205 short_list_max: self.short_list_max.max(1),
206 medium_list_max: self.medium_list_max.max(self.short_list_max.max(1) + 1),
207 grid_padding: self.grid_padding.max(1),
208 grid_columns: self.grid_columns.filter(|value| *value > 0),
209 column_weight: self.column_weight.max(1),
210 table_overflow: self.table_overflow,
211 table_border: self.table_border,
212 help_table_border: self.help_chrome.table_chrome.resolve(self.table_border),
213 theme_name,
214 theme: theme.clone(),
215 style_overrides: self.style_overrides.clone(),
216 chrome_frame: self.chrome_frame,
217 },
218 RenderBackend::Rich => ResolvedRenderSettings {
219 backend,
220 color: self.resolve_color_mode(),
221 unicode: self.resolve_unicode_mode(),
222 width: self.resolve_width(),
223 margin: self.margin,
224 indent_size: self.indent_size.max(1),
225 short_list_max: self.short_list_max.max(1),
226 medium_list_max: self.medium_list_max.max(self.short_list_max.max(1) + 1),
227 grid_padding: self.grid_padding.max(1),
228 grid_columns: self.grid_columns.filter(|value| *value > 0),
229 column_weight: self.column_weight.max(1),
230 table_overflow: self.table_overflow,
231 table_border: self.table_border,
232 help_table_border: self.help_chrome.table_chrome.resolve(self.table_border),
233 theme_name,
234 theme,
235 style_overrides: self.style_overrides.clone(),
236 chrome_frame: self.chrome_frame,
237 },
238 }
239 }
240
241 pub(crate) fn resolve_render_plan(&self, output: &OutputResult) -> ResolvedRenderPlan {
243 let format = crate::ui::format::resolve_output_format(output, self);
244 let render = if matches!(format, OutputFormat::Json) {
247 self.plain_copy_settings().resolve_render_settings()
248 } else {
249 self.resolve_render_settings()
250 };
251
252 ResolvedRenderPlan {
253 format,
254 render,
255 guide: self.resolve_guide_render_settings(),
256 mreg: self.resolve_mreg_build_settings(),
257 }
258 }
259
260 fn resolve_width(&self) -> Option<usize> {
261 if let Some(width) = self.width {
262 return (width > 0).then_some(width);
263 }
264 self.runtime.width.filter(|width| *width > 0)
265 }
266
267 pub(crate) fn plain_copy_settings(&self) -> Self {
268 Self {
269 format: self.format,
270 format_explicit: self.format_explicit,
271 mode: RenderMode::Plain,
272 color: ColorMode::Never,
273 unicode: UnicodeMode::Never,
274 width: self.width,
275 margin: self.margin,
276 indent_size: self.indent_size,
277 short_list_max: self.short_list_max,
278 medium_list_max: self.medium_list_max,
279 grid_padding: self.grid_padding,
280 grid_columns: self.grid_columns,
281 column_weight: self.column_weight,
282 table_overflow: self.table_overflow,
283 table_border: self.table_border,
284 help_chrome: self.help_chrome,
285 mreg_stack_min_col_width: self.mreg_stack_min_col_width,
286 mreg_stack_overflow_ratio: self.mreg_stack_overflow_ratio,
287 theme_name: self.theme_name.clone(),
288 theme: self.theme.clone(),
289 style_overrides: self.style_overrides.clone(),
290 chrome_frame: self.chrome_frame,
291 ruled_section_policy: self.ruled_section_policy,
292 guide_default_format: self.guide_default_format,
293 runtime: self.runtime.clone(),
294 }
295 }
296}
297
298impl HelpChromeSettings {
299 pub(crate) fn resolve(self, table_border: TableBorderStyle) -> ResolvedHelpChromeSettings {
300 ResolvedHelpChromeSettings {
301 table_border: self.table_chrome.resolve(table_border),
302 entry_indent: self.entry_indent,
303 entry_gap: self.entry_gap,
304 section_spacing: self.section_spacing,
305 }
306 }
307}
308
309impl ResolvedGuideRenderSettings {
310 #[cfg(test)]
311 pub(crate) fn plain_help(
312 frame_style: SectionFrameStyle,
313 table_border: TableBorderStyle,
314 ) -> Self {
315 Self {
316 frame_style,
317 ruled_section_policy: RuledSectionPolicy::PerSection,
318 help_chrome: HelpChromeSettings {
319 table_chrome: match table_border {
320 TableBorderStyle::None => crate::ui::HelpTableChrome::None,
321 TableBorderStyle::Square => crate::ui::HelpTableChrome::Square,
322 TableBorderStyle::Round => crate::ui::HelpTableChrome::Round,
323 },
324 ..HelpChromeSettings::default()
325 }
326 .resolve(table_border),
327 }
328 }
329}