Skip to main content

cli_engine/output/
renderer.rs

1use std::{io::Write, str::FromStr};
2
3use crate::{CliCoreError, DetailedError, Result};
4
5use super::{
6    Envelope, build_detailed_error_envelope, build_error_envelope, render_human, render_json,
7    render_toon,
8};
9
10/// Supported output formats.
11#[derive(Clone, Copy, Debug, Eq, PartialEq)]
12pub enum OutputFormat {
13    /// TOON renderer.
14    Toon,
15    /// Pretty JSON renderer.
16    Json,
17    /// Human-readable terminal renderer.
18    Human,
19}
20
21impl FromStr for OutputFormat {
22    type Err = CliCoreError;
23
24    fn from_str(value: &str) -> std::result::Result<Self, Self::Err> {
25        if value == "human" {
26            Ok(Self::Human)
27        } else if value == "toon" {
28            Ok(Self::Toon)
29        } else {
30            Ok(Self::Json)
31        }
32    }
33}
34
35/// Returns true when `format` is supported by the framework.
36#[must_use]
37pub fn is_valid_output_format(format: &str) -> bool {
38    matches!(format, "toon" | "json" | "human")
39}
40
41/// Small rendering facade for callers that prefer an object-style renderer.
42#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
43pub struct RendererFactory;
44
45impl RendererFactory {
46    /// Creates a renderer factory.
47    #[must_use]
48    pub fn new() -> Self {
49        Self
50    }
51
52    /// Renders an envelope in the requested format.
53    pub fn render(&self, format: &str, envelope: &Envelope) -> Result<String> {
54        render_format(format, envelope)
55    }
56
57    /// Writes rendered envelope output to a writer.
58    pub fn write(&self, mut writer: impl Write, format: &str, envelope: &Envelope) -> Result<()> {
59        writer
60            .write_all(self.render(format, envelope)?.as_bytes())
61            .map_err(CliCoreError::from)
62    }
63}
64
65/// Renders an envelope in the requested format.
66pub fn render(format: OutputFormat, envelope: &Envelope) -> Result<String> {
67    match format {
68        OutputFormat::Human => {
69            envelope.serialization_result()?;
70            Ok(render_human(envelope))
71        }
72        OutputFormat::Json => render_json(envelope),
73        OutputFormat::Toon => render_toon(envelope),
74    }
75}
76
77/// Parses an output format string and renders an envelope.
78pub fn render_format(format: &str, envelope: &Envelope) -> Result<String> {
79    render(format.parse()?, envelope)
80}
81
82/// Writes rendered envelope output to a writer.
83pub fn write_render(mut writer: impl Write, format: &str, envelope: &Envelope) -> Result<()> {
84    writer
85        .write_all(render_format(format, envelope)?.as_bytes())
86        .map_err(CliCoreError::from)
87}
88
89/// Wraps data in a success envelope and renders it.
90pub fn render_data(
91    format: OutputFormat,
92    data: impl serde::Serialize,
93    system: impl Into<String>,
94) -> Result<String> {
95    let data = serde_json::to_value(data)?;
96    render(format, &Envelope::success(data, system))
97}
98
99/// Parses an output format string, wraps data in a success envelope, and renders it.
100pub fn render_data_format(
101    format: &str,
102    data: impl serde::Serialize,
103    system: impl Into<String>,
104) -> Result<String> {
105    let data = serde_json::to_value(data)?;
106    render_format(format, &Envelope::success(data, system))
107}
108
109/// Wraps an error in an error envelope and renders it.
110pub fn render_error(
111    format: OutputFormat,
112    err: &(dyn std::error::Error + 'static),
113    system: &str,
114) -> Result<String> {
115    render(format, &build_error_envelope(err, system))
116}
117
118/// Parses an output format string, wraps an error, and renders it.
119pub fn render_error_format(
120    format: &str,
121    err: &(dyn std::error::Error + 'static),
122    system: &str,
123) -> Result<String> {
124    render_format(format, &build_error_envelope(err, system))
125}
126
127/// Wraps a detailed error in an error envelope and renders it.
128pub fn render_detailed_error(
129    format: OutputFormat,
130    err: &dyn DetailedError,
131    system: &str,
132) -> Result<String> {
133    render(format, &build_detailed_error_envelope(err, system))
134}
135
136/// Parses an output format string, wraps a detailed error, and renders it.
137pub fn render_detailed_error_format(
138    format: &str,
139    err: &dyn DetailedError,
140    system: &str,
141) -> Result<String> {
142    render_format(format, &build_detailed_error_envelope(err, system))
143}