Skip to main content

osp_cli/core/
output.rs

1//! Output-mode enums shared across config parsing, CLI flags, and the UI.
2//!
3//! These types exist so the rest of the crate can talk about rendering intent
4//! without passing around raw strings. They define the stable vocabulary for
5//! format, richness, color, and unicode behavior.
6//!
7//! Contract:
8//!
9//! - these enums should stay small and broadly reusable
10//! - parsing here should accept the user-facing spellings supported by config
11//!   and CLI flags
12//! - higher-level modules may interpret `Auto`, but the canonical labels live
13//!   here
14
15/// Supported output formats for rendered command results.
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum OutputFormat {
18    /// Chooses a format based on the command result and terminal context.
19    Auto,
20    /// Renders guidance-oriented documents.
21    Guide,
22    /// Emits JSON output.
23    Json,
24    /// Renders tabular output.
25    Table,
26    /// Renders Markdown tables or documents.
27    Markdown,
28    /// Renders the legacy mreg text format.
29    Mreg,
30    /// Emits a scalar or compact value view.
31    Value,
32}
33
34impl OutputFormat {
35    /// Returns the canonical config and CLI spelling for this format.
36    ///
37    /// # Examples
38    ///
39    /// ```
40    /// use osp_cli::core::output::OutputFormat;
41    ///
42    /// assert_eq!(OutputFormat::Markdown.as_str(), "md");
43    /// assert_eq!(OutputFormat::Json.as_str(), "json");
44    /// ```
45    pub fn as_str(self) -> &'static str {
46        match self {
47            OutputFormat::Auto => "auto",
48            OutputFormat::Guide => "guide",
49            OutputFormat::Json => "json",
50            OutputFormat::Table => "table",
51            OutputFormat::Markdown => "md",
52            OutputFormat::Mreg => "mreg",
53            OutputFormat::Value => "value",
54        }
55    }
56
57    /// Parses the user-facing output format spellings accepted by config and
58    /// CLI flags.
59    ///
60    /// # Examples
61    ///
62    /// ```
63    /// use osp_cli::core::output::OutputFormat;
64    ///
65    /// assert_eq!(OutputFormat::parse("guide"), Some(OutputFormat::Guide));
66    /// assert_eq!(OutputFormat::parse(" markdown "), Some(OutputFormat::Markdown));
67    /// assert_eq!(OutputFormat::parse("MD"), Some(OutputFormat::Markdown));
68    /// assert_eq!(OutputFormat::parse("wat"), None);
69    /// ```
70    pub fn parse(value: &str) -> Option<Self> {
71        match value.trim().to_ascii_lowercase().as_str() {
72            "auto" => Some(OutputFormat::Auto),
73            "guide" => Some(OutputFormat::Guide),
74            "json" => Some(OutputFormat::Json),
75            "table" => Some(OutputFormat::Table),
76            "md" | "markdown" => Some(OutputFormat::Markdown),
77            "mreg" => Some(OutputFormat::Mreg),
78            "value" => Some(OutputFormat::Value),
79            _ => None,
80        }
81    }
82}
83
84/// Rendering mode preference for terminal output.
85#[derive(Debug, Clone, Copy, PartialEq, Eq)]
86pub enum RenderMode {
87    /// Chooses plain or rich rendering automatically.
88    Auto,
89    /// Forces plain rendering.
90    Plain,
91    /// Forces rich rendering.
92    Rich,
93}
94
95impl RenderMode {
96    /// Returns the canonical config and CLI spelling for this render mode.
97    ///
98    /// # Examples
99    ///
100    /// ```
101    /// use osp_cli::core::output::RenderMode;
102    ///
103    /// assert_eq!(RenderMode::Rich.as_str(), "rich");
104    /// ```
105    pub fn as_str(self) -> &'static str {
106        match self {
107            RenderMode::Auto => "auto",
108            RenderMode::Plain => "plain",
109            RenderMode::Rich => "rich",
110        }
111    }
112
113    /// Parses the render-mode spellings accepted by config and CLI flags.
114    ///
115    /// # Examples
116    ///
117    /// ```
118    /// use osp_cli::core::output::RenderMode;
119    ///
120    /// assert_eq!(RenderMode::parse("RICH"), Some(RenderMode::Rich));
121    /// assert_eq!(RenderMode::parse(" plain "), Some(RenderMode::Plain));
122    /// assert_eq!(RenderMode::parse("wat"), None);
123    /// ```
124    pub fn parse(value: &str) -> Option<Self> {
125        match value.trim().to_ascii_lowercase().as_str() {
126            "auto" => Some(RenderMode::Auto),
127            "plain" => Some(RenderMode::Plain),
128            "rich" => Some(RenderMode::Rich),
129            _ => None,
130        }
131    }
132}
133
134/// Color handling policy for rendered output.
135#[derive(Debug, Clone, Copy, PartialEq, Eq)]
136pub enum ColorMode {
137    /// Chooses color usage from terminal capabilities.
138    Auto,
139    /// Always emits color sequences.
140    Always,
141    /// Never emits color sequences.
142    Never,
143}
144
145impl ColorMode {
146    /// Returns the canonical config and CLI spelling for this color mode.
147    ///
148    /// # Examples
149    ///
150    /// ```
151    /// use osp_cli::core::output::ColorMode;
152    ///
153    /// assert_eq!(ColorMode::Always.as_str(), "always");
154    /// ```
155    pub fn as_str(self) -> &'static str {
156        match self {
157            ColorMode::Auto => "auto",
158            ColorMode::Always => "always",
159            ColorMode::Never => "never",
160        }
161    }
162
163    /// Parses the color-mode spellings accepted by config and CLI flags.
164    ///
165    /// # Examples
166    ///
167    /// ```
168    /// use osp_cli::core::output::ColorMode;
169    ///
170    /// assert_eq!(ColorMode::parse("never"), Some(ColorMode::Never));
171    /// assert_eq!(ColorMode::parse(" AUTO "), Some(ColorMode::Auto));
172    /// assert_eq!(ColorMode::parse("wat"), None);
173    /// ```
174    pub fn parse(value: &str) -> Option<Self> {
175        match value.trim().to_ascii_lowercase().as_str() {
176            "auto" => Some(ColorMode::Auto),
177            "always" => Some(ColorMode::Always),
178            "never" => Some(ColorMode::Never),
179            _ => None,
180        }
181    }
182}
183
184/// Unicode handling policy for rendered output.
185#[derive(Debug, Clone, Copy, PartialEq, Eq)]
186pub enum UnicodeMode {
187    /// Chooses Unicode usage from terminal capabilities.
188    Auto,
189    /// Always emits Unicode output.
190    Always,
191    /// Never emits Unicode output.
192    Never,
193}
194
195impl UnicodeMode {
196    /// Returns the canonical config and CLI spelling for this unicode mode.
197    ///
198    /// # Examples
199    ///
200    /// ```
201    /// use osp_cli::core::output::UnicodeMode;
202    ///
203    /// assert_eq!(UnicodeMode::Never.as_str(), "never");
204    /// ```
205    pub fn as_str(self) -> &'static str {
206        match self {
207            UnicodeMode::Auto => "auto",
208            UnicodeMode::Always => "always",
209            UnicodeMode::Never => "never",
210        }
211    }
212
213    /// Parses the unicode-mode spellings accepted by config and CLI flags.
214    ///
215    /// # Examples
216    ///
217    /// ```
218    /// use osp_cli::core::output::UnicodeMode;
219    ///
220    /// assert_eq!(UnicodeMode::parse("always"), Some(UnicodeMode::Always));
221    /// assert_eq!(UnicodeMode::parse(" Auto "), Some(UnicodeMode::Auto));
222    /// assert_eq!(UnicodeMode::parse("wat"), None);
223    /// ```
224    pub fn parse(value: &str) -> Option<Self> {
225        match value.trim().to_ascii_lowercase().as_str() {
226            "auto" => Some(UnicodeMode::Auto),
227            "always" => Some(UnicodeMode::Always),
228            "never" => Some(UnicodeMode::Never),
229            _ => None,
230        }
231    }
232}
233
234#[cfg(test)]
235mod tests {
236    use super::{ColorMode, OutputFormat, RenderMode, UnicodeMode};
237
238    #[test]
239    fn output_modes_round_trip_known_values_aliases_and_variants_unit() {
240        assert_eq!(OutputFormat::Auto.as_str(), "auto");
241        assert_eq!(OutputFormat::Guide.as_str(), "guide");
242        assert_eq!(OutputFormat::Json.as_str(), "json");
243        assert_eq!(OutputFormat::Table.as_str(), "table");
244        assert_eq!(OutputFormat::Markdown.as_str(), "md");
245        assert_eq!(OutputFormat::Mreg.as_str(), "mreg");
246        assert_eq!(OutputFormat::Value.as_str(), "value");
247        assert_eq!(OutputFormat::parse("guide"), Some(OutputFormat::Guide));
248        assert_eq!(OutputFormat::parse(" json "), Some(OutputFormat::Json));
249        assert_eq!(OutputFormat::parse("auto"), Some(OutputFormat::Auto));
250        assert_eq!(
251            OutputFormat::parse("markdown"),
252            Some(OutputFormat::Markdown)
253        );
254        assert_eq!(OutputFormat::parse("md"), Some(OutputFormat::Markdown));
255        assert_eq!(OutputFormat::parse("mreg"), Some(OutputFormat::Mreg));
256        assert_eq!(OutputFormat::parse(" value "), Some(OutputFormat::Value));
257        assert_eq!(OutputFormat::parse("wat"), None);
258
259        assert_eq!(RenderMode::Auto.as_str(), "auto");
260        assert_eq!(RenderMode::Plain.as_str(), "plain");
261        assert_eq!(RenderMode::Rich.as_str(), "rich");
262        assert_eq!(RenderMode::parse("plain"), Some(RenderMode::Plain));
263        assert_eq!(RenderMode::parse("RICH"), Some(RenderMode::Rich));
264        assert_eq!(RenderMode::parse("wat"), None);
265
266        assert_eq!(ColorMode::Auto.as_str(), "auto");
267        assert_eq!(ColorMode::Always.as_str(), "always");
268        assert_eq!(ColorMode::parse("always"), Some(ColorMode::Always));
269        assert_eq!(ColorMode::parse(" never "), Some(ColorMode::Never));
270        assert_eq!(ColorMode::parse("wat"), None);
271
272        assert_eq!(UnicodeMode::Auto.as_str(), "auto");
273        assert_eq!(UnicodeMode::Always.as_str(), "always");
274        assert_eq!(UnicodeMode::Never.as_str(), "never");
275        assert_eq!(UnicodeMode::parse("AUTO"), Some(UnicodeMode::Auto));
276        assert_eq!(UnicodeMode::parse("never"), Some(UnicodeMode::Never));
277        assert_eq!(UnicodeMode::parse("AUTO"), Some(UnicodeMode::Auto));
278        assert_eq!(UnicodeMode::parse("wat"), None);
279    }
280}