Skip to content

Commit abdd703

Browse files
committed
refactor(anstyle-svg): Use a concrete retrurn type from adapter
1 parent 8d50530 commit abdd703

File tree

2 files changed

+84
-40
lines changed

2 files changed

+84
-40
lines changed

crates/anstyle-svg/src/adapter.rs

Lines changed: 58 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ pub(crate) struct AnsiBytesIter<'s> {
3232
}
3333

3434
impl Iterator for AnsiBytesIter<'_> {
35-
type Item = (anstyle::Style, String);
35+
type Item = Element;
3636

3737
#[inline]
3838
fn next(&mut self) -> Option<Self::Item> {
@@ -45,7 +45,7 @@ fn next_bytes(
4545
bytes: &mut &[u8],
4646
parser: &mut anstyle_parse::Parser,
4747
capture: &mut AnsiCapture,
48-
) -> Option<(anstyle::Style, String)> {
48+
) -> Option<Element> {
4949
capture.reset();
5050
while capture.ready.is_none() {
5151
let byte = if let Some((byte, remainder)) = (*bytes).split_first() {
@@ -61,7 +61,10 @@ fn next_bytes(
6161
}
6262

6363
let style = capture.ready.unwrap_or(capture.style);
64-
Some((style, std::mem::take(&mut capture.printable)))
64+
Some(Element {
65+
text: std::mem::take(&mut capture.printable),
66+
style,
67+
})
6568
}
6669

6770
#[derive(Default, Clone, Debug, PartialEq, Eq)]
@@ -268,6 +271,12 @@ impl anstyle_parse::Perform for AnsiCapture {
268271
}
269272
}
270273

274+
#[derive(Clone, Debug, PartialEq, Eq)]
275+
pub(crate) struct Element {
276+
pub(crate) text: String,
277+
pub(crate) style: anstyle::Style,
278+
}
279+
271280
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
272281
enum CsiState {
273282
Normal,
@@ -304,11 +313,8 @@ mod test {
304313
use proptest::prelude::*;
305314

306315
#[track_caller]
307-
fn verify(input: &str, expected: Vec<(anstyle::Style, &str)>) {
308-
let expected = expected
309-
.into_iter()
310-
.map(|(style, value)| (style, value.to_owned()))
311-
.collect::<Vec<_>>();
316+
fn verify(input: &str, expected: Vec<Element>) {
317+
let expected = expected.into_iter().collect::<Vec<_>>();
312318
let mut state = AnsiBytes::new();
313319
let actual = state.extract_next(input.as_bytes()).collect::<Vec<_>>();
314320
assert_eq!(expected, actual, "{input:?}");
@@ -319,8 +325,14 @@ mod test {
319325
let green_on_red = anstyle::AnsiColor::Green.on(anstyle::AnsiColor::Red);
320326
let input = format!("{green_on_red}Hello{green_on_red:#} world!");
321327
let expected = vec![
322-
(green_on_red, "Hello"),
323-
(anstyle::Style::default(), " world!"),
328+
Element {
329+
text: "Hello".to_owned(),
330+
style: green_on_red,
331+
},
332+
Element {
333+
text: " world!".to_owned(),
334+
style: anstyle::Style::default(),
335+
},
324336
];
325337
verify(&input, expected);
326338
}
@@ -330,9 +342,18 @@ mod test {
330342
let green_on_red = anstyle::AnsiColor::Green.on(anstyle::AnsiColor::Red);
331343
let input = format!("Hello {green_on_red}world{green_on_red:#}!");
332344
let expected = vec![
333-
(anstyle::Style::default(), "Hello "),
334-
(green_on_red, "world"),
335-
(anstyle::Style::default(), "!"),
345+
Element {
346+
text: "Hello ".to_owned(),
347+
style: anstyle::Style::default(),
348+
},
349+
Element {
350+
text: "world".to_owned(),
351+
style: green_on_red,
352+
},
353+
Element {
354+
text: "!".to_owned(),
355+
style: anstyle::Style::default(),
356+
},
336357
];
337358
verify(&input, expected);
338359
}
@@ -342,8 +363,14 @@ mod test {
342363
let green_on_red = anstyle::AnsiColor::Green.on(anstyle::AnsiColor::Red);
343364
let input = format!("Hello {green_on_red}world!{green_on_red:#}");
344365
let expected = vec![
345-
(anstyle::Style::default(), "Hello "),
346-
(green_on_red, "world!"),
366+
Element {
367+
text: "Hello ".to_owned(),
368+
style: anstyle::Style::default(),
369+
},
370+
Element {
371+
text: "world!".to_owned(),
372+
style: green_on_red,
373+
},
347374
];
348375
verify(&input, expected);
349376
}
@@ -354,9 +381,18 @@ mod test {
354381
// termcolor only supports "brights" via these
355382
let input = format!("Hello {ansi_11}world{ansi_11:#}!");
356383
let expected = vec![
357-
(anstyle::Style::default(), "Hello "),
358-
(ansi_11, "world"),
359-
(anstyle::Style::default(), "!"),
384+
Element {
385+
text: "Hello ".to_owned(),
386+
style: anstyle::Style::default(),
387+
},
388+
Element {
389+
text: "world".to_owned(),
390+
style: ansi_11,
391+
},
392+
Element {
393+
text: "!".to_owned(),
394+
style: anstyle::Style::default(),
395+
},
360396
];
361397
verify(&input, expected);
362398
}
@@ -368,7 +404,10 @@ mod test {
368404
let expected = if s.is_empty() {
369405
vec![]
370406
} else {
371-
vec![(anstyle::Style::default(), s.clone())]
407+
vec![Element {
408+
text: s.clone(),
409+
style: anstyle::Style::default(),
410+
}]
372411
};
373412
let mut state = AnsiBytes::new();
374413
let actual = state.extract_next(s.as_bytes()).collect::<Vec<_>>();

crates/anstyle-svg/src/lib.rs

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,10 @@ impl Term {
9191
const BG: &str = "bg";
9292

9393
let mut styled = adapter::AnsiBytes::new();
94-
let mut styled = styled.extract_next(ansi.as_bytes()).collect::<Vec<_>>();
94+
let mut elements = styled.extract_next(ansi.as_bytes()).collect::<Vec<_>>();
9595
let mut effects_in_use = anstyle::Effects::new();
96-
for (style, _) in &mut styled {
96+
for element in &mut elements {
97+
let style = &mut element.style;
9798
// Pre-process INVERT to make fg/bg calculations easier
9899
if style.get_effects().contains(anstyle::Effects::INVERT) {
99100
*style = style
@@ -103,7 +104,7 @@ impl Term {
103104
}
104105
effects_in_use |= style.get_effects();
105106
}
106-
let styled_lines = split_lines(&styled);
107+
let styled_lines = split_lines(&elements);
107108

108109
let fg_color = rgb_value(self.fg_color, self.palette);
109110
let bg_color = rgb_value(self.bg_color, self.palette);
@@ -113,7 +114,7 @@ impl Term {
113114
let height = styled_lines.len() * line_height + self.padding_px * 2;
114115
let max_width = styled_lines
115116
.iter()
116-
.map(|l| l.iter().map(|(_, t)| t.width()).sum())
117+
.map(|l| l.iter().map(|e| e.text.width()).sum())
117118
.max()
118119
.unwrap_or(0);
119120
let width_px = (max_width as f64 * 8.4).ceil() as usize;
@@ -128,7 +129,7 @@ impl Term {
128129
writeln!(&mut buffer, r#" <style>"#).unwrap();
129130
writeln!(&mut buffer, r#" .{FG} {{ fill: {fg_color} }}"#).unwrap();
130131
writeln!(&mut buffer, r#" .{BG} {{ background: {bg_color} }}"#).unwrap();
131-
for (name, rgb) in color_styles(&styled, self.palette) {
132+
for (name, rgb) in color_styles(&elements, self.palette) {
132133
if name.starts_with(FG_PREFIX) {
133134
writeln!(&mut buffer, r#" .{name} {{ fill: {rgb} }}"#).unwrap();
134135
}
@@ -230,25 +231,25 @@ impl Term {
230231
)
231232
.unwrap();
232233
for line in &styled_lines {
233-
if line.iter().any(|(s, _)| s.get_bg_color().is_some()) {
234+
if line.iter().any(|e| e.style.get_bg_color().is_some()) {
234235
write!(&mut buffer, r#" <tspan x="{text_x}px" y="{text_y}px">"#).unwrap();
235-
for (style, fragment) in line {
236-
if fragment.is_empty() {
236+
for element in line {
237+
if element.text.is_empty() {
237238
continue;
238239
}
239-
write_bg_span(&mut buffer, style, fragment);
240+
write_bg_span(&mut buffer, &element.style, &element.text);
240241
}
241242
// HACK: must close tspan on newline to include them in copy/paste
242243
writeln!(&mut buffer).unwrap();
243244
writeln!(&mut buffer, r#"</tspan>"#).unwrap();
244245
}
245246

246247
write!(&mut buffer, r#" <tspan x="{text_x}px" y="{text_y}px">"#).unwrap();
247-
for (style, fragment) in line {
248-
if fragment.is_empty() {
248+
for element in line {
249+
if element.text.is_empty() {
249250
continue;
250251
}
251-
write_fg_span(&mut buffer, style, fragment);
252+
write_fg_span(&mut buffer, element, &element.text);
252253
}
253254
// HACK: must close tspan on newline to include them in copy/paste
254255
writeln!(&mut buffer).unwrap();
@@ -267,8 +268,9 @@ impl Term {
267268
const FG_COLOR: anstyle::Color = anstyle::Color::Ansi(anstyle::AnsiColor::White);
268269
const BG_COLOR: anstyle::Color = anstyle::Color::Ansi(anstyle::AnsiColor::Black);
269270

270-
fn write_fg_span(buffer: &mut String, style: &anstyle::Style, fragment: &str) {
271+
fn write_fg_span(buffer: &mut String, element: &adapter::Element, fragment: &str) {
271272
use std::fmt::Write as _;
273+
let style = element.style;
272274
let fg_color = style.get_fg_color().map(|c| color_name(FG_PREFIX, c));
273275
let underline_color = style
274276
.get_underline_color()
@@ -415,11 +417,12 @@ fn color_name(prefix: &str, color: anstyle::Color) -> String {
415417
}
416418

417419
fn color_styles(
418-
styled: &[(anstyle::Style, String)],
420+
styled: &[adapter::Element],
419421
palette: Palette,
420422
) -> impl Iterator<Item = (String, String)> {
421423
let mut colors = std::collections::BTreeMap::new();
422-
for (style, _) in styled {
424+
for element in styled {
425+
let style = element.style;
423426
if let Some(color) = style.get_fg_color() {
424427
colors.insert(color_name(FG_PREFIX, color), rgb_value(color, palette));
425428
}
@@ -437,18 +440,20 @@ fn color_styles(
437440
colors.into_iter()
438441
}
439442

440-
fn split_lines(styled: &[(anstyle::Style, String)]) -> Vec<Vec<(anstyle::Style, &str)>> {
443+
fn split_lines(styled: &[adapter::Element]) -> Vec<Vec<adapter::Element>> {
441444
let mut lines = Vec::new();
442445
let mut current_line = Vec::new();
443-
for (style, mut next) in styled.iter().map(|(s, t)| (*s, t.as_str())) {
444-
while let Some((current, remaining)) = next.split_once('\n') {
446+
for mut element in styled.iter().cloned() {
447+
while let Some((current, remaining)) = element.text.split_once('\n') {
445448
let current = current.strip_suffix('\r').unwrap_or(current);
446-
current_line.push((style, current));
449+
let mut new_element = element.clone();
450+
new_element.text = current.to_owned();
451+
current_line.push(new_element);
447452
lines.push(current_line);
448453
current_line = Vec::new();
449-
next = remaining;
454+
element.text = remaining.to_owned();
450455
}
451-
current_line.push((style, next));
456+
current_line.push(element);
452457
}
453458
if !current_line.is_empty() {
454459
lines.push(current_line);

0 commit comments

Comments
 (0)