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