spectrum/emit/
fragment.rs1use std::{fmt, fmt::Formatter, io::Write};
2
3use super::{
4 buf::Buf,
5 error::{EmitError, EmitResult},
6 style::Style,
7};
8
9pub trait StyledFragmentTrait {
18 fn clone_frag(&self) -> StyledFragment;
19
20 fn emit_into_formatter(&self, f: &mut Formatter<'_>, backend: &EmitBackend<'_>) -> EmitResult;
21}
22
23impl<T> From<T> for StyledFragment
24where
25 T: StyledFragmentTrait + 'static,
26{
27 fn from(value: T) -> StyledFragment {
28 StyledFragment {
29 fragment: Box::new(value),
30 }
31 }
32}
33
34pub struct StyledFragment {
37 fragment: Box<dyn StyledFragmentTrait + 'static>,
38}
39
40impl Clone for StyledFragment {
41 fn clone(&self) -> Self {
42 self.clone_frag()
43 }
44}
45
46impl StyledFragment {
47 pub fn new(frag: impl StyledFragmentTrait + 'static) -> StyledFragment {
48 StyledFragment {
49 fragment: Box::new(frag),
50 }
51 }
52
53 pub fn clone_frag(&self) -> StyledFragment {
54 self.fragment.clone_frag()
55 }
56
57 pub fn plain(&self) -> String {
58 self.emit_into_string(EmitPlain).unwrap()
59 }
60
61 pub fn emit_into_formatter(
62 &self,
63 f: &mut Formatter<'_>,
64 backend: &EmitBackend<'_>,
65 ) -> EmitResult {
66 self.fragment.emit_into_formatter(f, backend)
67 }
68
69 pub fn emit_into(
70 &self,
71 write: &mut dyn std::io::Write,
72 backend: &EmitBackend<'_>,
73 ) -> EmitResult {
74 let formatted = format::Display(move |f| Ok(self.emit_into_formatter(f, backend)?));
75 Ok(write!(write, "{}", formatted)?)
76 }
77
78 pub fn emit_into_string(&self, backend: impl EmitBackendTrait) -> EmitResult<String> {
79 Ok(Buf::collect_string(|write| {
80 Ok(self.emit_into(write, &backend.emitter())?)
81 })?)
82 }
83}
84
85pub struct StyledNewline;
86
87impl StyledFragmentTrait for StyledNewline {
88 fn emit_into_formatter(&self, f: &mut Formatter<'_>, backend: &EmitBackend<'_>) -> EmitResult {
89 backend.emit(f, "\n", &Style::default())
90 }
91
92 fn clone_frag(&self) -> StyledFragment {
93 StyledFragment::new(StyledNewline)
94 }
95}
96
97#[derive(Debug, Clone)]
100pub struct StyledString {
101 string: String,
102 style: Style,
103}
104
105impl StyledString {
106 pub fn new(string: impl Into<String>, style: impl Into<Style>) -> StyledString {
107 StyledString {
108 string: string.into(),
109 style: style.into(),
110 }
111 }
112}
113
114impl StyledFragmentTrait for StyledString {
115 fn emit_into_formatter(&self, f: &mut Formatter<'_>, backend: &EmitBackend<'_>) -> EmitResult {
116 backend.emit(f, &self.string[..], &self.style)
117 }
118
119 fn clone_frag(&self) -> StyledFragment {
120 StyledFragment::new(self.clone())
121 }
122}
123
124pub struct StyledLine {
126 line: Vec<StyledFragment>,
127}
128
129impl StyledLine {
130 pub fn new(fragments: Vec<StyledFragment>) -> StyledLine {
131 StyledLine { line: fragments }
132 }
133}
134
135impl StyledFragmentTrait for StyledLine {
136 fn emit_into_formatter(&self, f: &mut Formatter<'_>, backend: &EmitBackend<'_>) -> EmitResult {
137 for fragment in &self.line {
138 fragment.emit_into_formatter(f, backend)?
139 }
140
141 Ok(())
142 }
143
144 fn clone_frag(&self) -> StyledFragment {
145 StyledFragment::new(StyledLine {
146 line: self.line.to_vec(),
147 })
148 }
149}
150
151pub trait EmitBackendTrait {
154 fn emit(&self, f: &mut Formatter<'_>, fragment: &str, style: &Style) -> EmitResult;
155
156 fn emitter(&self) -> EmitBackend<'_>
157 where
158 Self: Sized,
159 {
160 EmitBackend { backend: self }
161 }
162}
163
164impl<'a, T> From<&'a T> for EmitBackend<'a>
165where
166 T: EmitBackendTrait + 'a,
167{
168 fn from(backend: &'a T) -> Self {
169 backend.emitter()
170 }
171}
172
173pub struct EmitBackend<'a> {
174 backend: &'a dyn EmitBackendTrait,
175}
176
177impl<'a> EmitBackend<'a> {
178 fn emit(&self, f: &mut Formatter<'_>, fragment: &str, style: &Style) -> EmitResult {
179 self.backend.emit(f, fragment, style)
180 }
181}
182
183pub struct EmitColored;
184
185impl EmitBackendTrait for EmitColored {
186 fn emit(&self, f: &mut Formatter<'_>, fragment: &str, style: &Style) -> EmitResult {
187 write!(f, "{}", style.apply_to(fragment)).map_err(EmitError::new)
188 }
189}
190
191pub struct EmitPlain;
192
193impl EmitBackendTrait for EmitPlain {
194 fn emit(&self, f: &mut Formatter<'_>, fragment: &str, _style: &Style) -> EmitResult {
195 write!(f, "{}", fragment).map_err(EmitError::new)
196 }
197}
198
199pub fn write_into(
200 write: &mut impl Write,
201 callback: impl Fn(&mut Formatter<'_>) -> fmt::Result,
202) -> EmitResult {
203 let formatted = format::Display(move |f| callback(f));
204 Ok(write!(write, "{}", formatted)?)
205}
206
207#[cfg(test)]
208mod tests {
209 use crate::EmitForTest;
210
211 use super::*;
212 use console::Color;
213
214 #[test]
215 fn emit_test() -> EmitResult {
216 let styled: StyledFragment =
217 StyledString::new("hello emitter world", Style::new().fg(Color::Red)).into();
218 let string = styled.emit_into_string(EmitForTest)?;
219
220 assert_eq!(&string, "[Red:hello emitter world]");
221
222 Ok(())
223 }
224
225 #[test]
226 fn emit_plain() -> EmitResult {
227 let styled: StyledFragment =
228 StyledString::new("hello emitter world", Style::new().fg(Color::Red)).into();
229 let string = styled.emit_into_string(EmitPlain)?;
230
231 assert_eq!(&string, "hello emitter world");
232
233 Ok(())
234 }
235
236 #[test]
237 fn emit_colored() -> EmitResult {
238 let styled: StyledFragment =
239 StyledString::new("hello emitter world", Style::new().fg(Color::Red)).into();
240 let string = styled.emit_into_string(EmitColored)?;
241
242 assert_eq!(&string, "\u{1b}[31mhello emitter world\u{1b}[0m");
243
244 Ok(())
245 }
246}