Skip to main content

rusty_rich/
repr.rs

1//! Rich repr protocol — equivalent to Python rich's `repr.py`.
2//! Provides `__rich_repr__`-like functionality for Rust types.
3
4#[cfg(feature = "syntax-highlighting")]
5use crate::highlighter::ReprHighlighter;
6use crate::style::Style;
7use crate::text::Text;
8
9/// Error type for repr operations.
10#[derive(Debug, Clone)]
11pub struct ReprError {
12    pub message: String,
13    pub source: Option<String>,
14}
15
16impl std::fmt::Display for ReprError {
17    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18        write!(f, "ReprError: {}", self.message)
19    }
20}
21
22impl std::error::Error for ReprError {}
23
24/// Trait for types that provide a rich representation.
25pub trait RichRepr {
26    /// Generate a rich representation of self.
27    fn rich_repr(&self) -> Text;
28}
29
30/// Auto-implement RichRepr for Debug types using the ReprHighlighter.
31pub fn auto<T: std::fmt::Debug>(value: &T) -> Text {
32    let debug_str = format!("{:#?}", value);
33    #[cfg(feature = "syntax-highlighting")]
34    {
35        ReprHighlighter::new().highlight_str(&debug_str)
36    }
37    #[cfg(not(feature = "syntax-highlighting"))]
38    {
39        let mut t = Text::new("");
40        t.plain = debug_str;
41        t
42    }
43}
44
45/// Create a rich representation with custom formatting.
46pub fn rich_repr(
47    type_name: &str,
48    fields: &[(&str, &dyn std::fmt::Display)],
49    _options: Option<&ReprOptions>,
50) -> Text {
51    let mut text = Text::new("");
52
53    // Type name in bold
54    text.append_styled(
55        type_name,
56        Style::new()
57            .bold(true)
58            .color(crate::color::Color::parse("cyan").unwrap()),
59    );
60
61    text.plain.push('(');
62
63    for (i, (name, value)) in fields.iter().enumerate() {
64        if i > 0 {
65            text.plain.push_str(", ");
66        }
67        text.append_styled(
68            format!("{}=", name),
69            Style::new().color(crate::color::Color::parse("yellow").unwrap()),
70        );
71        text.append_styled(
72            value.to_string(),
73            Style::new().color(crate::color::Color::parse("green").unwrap()),
74        );
75    }
76
77    text.plain.push(')');
78    text
79}
80
81/// Options for repr formatting.
82#[derive(Debug, Clone)]
83pub struct ReprOptions {
84    pub max_string: Option<usize>,
85    pub max_depth: Option<usize>,
86    pub max_length: Option<usize>,
87    pub indent_guides: bool,
88    pub expand_all: bool,
89}
90
91impl Default for ReprOptions {
92    fn default() -> Self {
93        Self {
94            max_string: Some(100),
95            max_depth: Some(4),
96            max_length: Some(100),
97            indent_guides: true,
98            expand_all: false,
99        }
100    }
101}
102
103impl ReprOptions {
104    pub fn new() -> Self {
105        Self::default()
106    }
107    pub fn max_string(mut self, max: usize) -> Self {
108        self.max_string = Some(max);
109        self
110    }
111    pub fn max_depth(mut self, depth: usize) -> Self {
112        self.max_depth = Some(depth);
113        self
114    }
115    pub fn max_length(mut self, max: usize) -> Self {
116        self.max_length = Some(max);
117        self
118    }
119    pub fn indent_guides(mut self, value: bool) -> Self {
120        self.indent_guides = value;
121        self
122    }
123    pub fn expand_all(mut self) -> Self {
124        self.expand_all = true;
125        self
126    }
127}
128
129#[cfg(test)]
130mod tests {
131    use super::*;
132
133    #[test]
134    fn test_auto() {
135        let value = vec![1, 2, 3];
136        let text = auto(&value);
137        assert!(!text.plain.is_empty());
138    }
139
140    #[test]
141    fn test_rich_repr() {
142        let text = rich_repr("Point", &[("x", &1), ("y", &2)], None);
143        assert!(text.plain.contains("Point"));
144        assert!(text.plain.contains("x=1"));
145        assert!(text.plain.contains("y=2"));
146    }
147}