Skip to main content

sparrow/tui/
renderer.rs

1//! CLI Rich Rendering Engine for Sparrow.
2//!
3//! Provides terminal-native rendering of markdown, code with syntax highlighting,
4//! diffs, JSON, tables, and key-value pairs.
5
6pub struct RenderConfig {
7    pub width: u16,
8    pub colors_enabled: bool,
9    pub syntax_theme: String,
10    pub line_numbers: bool,
11}
12
13impl Default for RenderConfig {
14    fn default() -> Self {
15        Self {
16            width: console::Term::stdout().size().1,
17            colors_enabled: true,
18            syntax_theme: "base16-ocean.dark".into(),
19            line_numbers: true,
20        }
21    }
22}
23
24pub struct TermRenderer {
25    pub config: RenderConfig,
26}
27
28impl TermRenderer {
29    pub fn new(config: RenderConfig) -> Self {
30        Self { config }
31    }
32
33    pub fn render_markdown(&self, md: &str) -> String {
34        crate::tui::formatters::markdown::render_markdown_with_theme(md, &self.config.syntax_theme)
35    }
36
37    pub fn render_code(&self, code: &str, language: &str) -> String {
38        let lang = if language.is_empty() {
39            None
40        } else {
41            Some(language)
42        };
43        match lang {
44            Some(l) => crate::tui::formatters::code::highlight_with_line_numbers(
45                code,
46                l,
47                &self.config.syntax_theme,
48            )
49            .unwrap_or_else(|_| code.to_string()),
50            None => crate::tui::formatters::code::highlight(code, "", &self.config.syntax_theme)
51                .unwrap_or_else(|_| code.to_string()),
52        }
53    }
54
55    pub fn render_diff(&self, diff: &str) -> String {
56        crate::tui::formatters::diff::format_diff(diff)
57    }
58
59    pub fn render_json(&self, json: &str) -> String {
60        crate::tui::formatters::json::format_json(json)
61    }
62
63    pub fn render_table(&self, headers: &[&str], rows: &[Vec<String>]) -> String {
64        crate::tui::formatters::table::render_table(
65            headers,
66            rows,
67            Some(self.config.width as usize),
68            None,
69        )
70    }
71
72    pub fn render_key_value(&self, pairs: &[(&str, &str)]) -> String {
73        if pairs.is_empty() {
74            return String::new();
75        }
76        let max_key = pairs.iter().map(|(k, _)| k.len()).max().unwrap_or(0);
77        let mut out = String::new();
78        for (key, value) in pairs {
79            out.push_str(&format!("  {:>width$} : {}\n", key, value, width = max_key));
80        }
81        out
82    }
83}