1use crate::console::{ConsoleOptions, DynRenderable, RenderResult, Renderable};
8use crate::style::Style;
9
10#[derive(Debug, Clone)]
16pub struct Styled {
17 renderable: DynRenderable,
19 style: Style,
21}
22
23impl Styled {
24 pub fn new(renderable: impl Renderable + Send + Sync + 'static, style: Style) -> Self {
28 Self {
29 renderable: DynRenderable::new(renderable),
30 style,
31 }
32 }
33
34 pub fn style(mut self, style: Style) -> Self {
36 self.style = style;
37 self
38 }
39}
40
41impl Renderable for Styled {
42 fn render(&self, options: &ConsoleOptions) -> RenderResult {
43 let mut result = self.renderable.render(options);
44 let default_style = Style::new();
45
46 for line in &mut result.lines {
48 for seg in line.iter_mut() {
49 let existing_style = seg.style.as_ref().unwrap_or(&default_style);
50 seg.style = Some(self.style.combine(existing_style));
51 }
52 }
53
54 for item in &mut result.items {
57 if let crate::console::RenderItem::Segment(ref mut seg) = item {
58 let existing_style = seg.style.as_ref().unwrap_or(&default_style);
59 seg.style = Some(self.style.combine(existing_style));
60 }
61 }
62
63 result
64 }
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70 use crate::color::Color;
71 use crate::console::ConsoleOptions;
72
73 #[test]
74 fn test_styled_default() {
75 let style = Style::new().bold(true);
76 let styled = Styled::new("Hello", style);
77 let opts = ConsoleOptions::default();
78 let result = styled.render(&opts);
79 let ansi = result.to_ansi();
80 assert!(ansi.contains("Hello"));
81 assert!(ansi.contains("\x1b[1m") || ansi.contains("[1m"));
83 }
84
85 #[test]
86 fn test_styled_with_color() {
87 let color = Color::parse("red").unwrap();
88 let style = Style::new().color(color);
89 let styled = Styled::new("Red Text", style);
90 let opts = ConsoleOptions::default();
91 let result = styled.render(&opts);
92 let ansi = result.to_ansi();
93 assert!(ansi.contains("Red Text"));
94 }
95
96 #[test]
97 fn test_styled_builder_replace_style() {
98 let s1 = Style::new().bold(true);
99 let s2 = Style::new().italic(true);
100 let styled = Styled::new("text", s1).style(s2);
101 let opts = ConsoleOptions::default();
102 let result = styled.render(&opts);
103 let ansi = result.to_ansi();
104 assert!(ansi.contains("text"));
105 }
106
107 #[test]
108 fn test_styled_style_override() {
109 let outer_style = Style::new().bold(true);
111 let styled = Styled::new("test", outer_style);
112 let opts = ConsoleOptions::default();
113 let result = styled.render(&opts);
114 let ansi = result.to_ansi();
115 assert!(ansi.contains("test"));
116 }
117
118 #[test]
119 fn test_styled_multiple_segments() {
120 let style = Style::new().color(Color::parse("green").unwrap());
121 let text = "Hello\nWorld";
122 let styled = Styled::new(text, style);
123 let opts = ConsoleOptions::default();
124 let result = styled.render(&opts);
125 let ansi = result.to_ansi();
126 assert!(ansi.contains("Hello"));
127 assert!(ansi.contains("World"));
128 }
129}