css_parse/traits/
cursor_sink.rs

1use crate::{Cursor, SourceCursor, Token};
2use bumpalo::collections::Vec;
3
4/// This trait provides the generic `impl` that [ToCursors][crate::ToCursors] can use. This provides just enough API
5/// surface for nodes to put the cursors they represent into some buffer which can later be read, the details of which
6/// are elided.
7pub trait CursorSink {
8	fn append(&mut self, c: Cursor);
9}
10
11pub trait SourceCursorSink<'a> {
12	fn append(&mut self, c: SourceCursor<'a>);
13}
14
15const SEPARATOR: Cursor = Cursor::dummy(Token::SPACE);
16
17impl<'a> CursorSink for Vec<'a, Cursor> {
18	fn append(&mut self, c: Cursor) {
19		// If two adjacent cursors which could not be re-tokenized in the same way if they were written out adjacently occur
20		// then they should be separated by some token.
21		if let Some(last) = self.last() {
22			if last.token().needs_separator_for(c.into()) {
23				self.push(SEPARATOR);
24			}
25		}
26		self.push(c);
27	}
28}
29
30impl<'a> SourceCursorSink<'a> for &mut Vec<'a, SourceCursor<'a>> {
31	fn append(&mut self, c: SourceCursor<'a>) {
32		// If two adjacent cursors which could not be re-tokenized in the same way if they were written out adjacently occur
33		// then they should be separated by some token.
34		if let Some(last) = self.last() {
35			if last.token().needs_separator_for(c.token()) {
36				self.push(SourceCursor::from(SEPARATOR, " "));
37			}
38		}
39		self.push(c);
40	}
41}
42
43pub struct CursorToSourceCursorSink<'a, T: SourceCursorSink<'a>> {
44	source: &'a str,
45	sink: T,
46}
47
48impl<'a, T: SourceCursorSink<'a>> CursorToSourceCursorSink<'a, T> {
49	pub fn new(source: &'a str, sink: T) -> Self {
50		Self { source, sink }
51	}
52}
53
54impl<'a, T: SourceCursorSink<'a>> CursorSink for CursorToSourceCursorSink<'a, T> {
55	fn append(&mut self, c: Cursor) {
56		self.sink.append(SourceCursor::from(c, c.str_slice(self.source)))
57	}
58}
59
60impl<'a> SourceCursorSink<'a> for &mut String {
61	fn append(&mut self, c: SourceCursor<'a>) {
62		use std::fmt::Write;
63		let _ = write!(self, "{c}");
64	}
65}
66
67#[cfg(test)]
68mod test {
69	use super::*;
70	use crate::{ToCursors, parse};
71	use bumpalo::Bump;
72
73	#[test]
74	fn test_cursor_sink_for_vec() {
75		let source_text = "black white";
76		let bump = Bump::default();
77		let mut stream = Vec::new_in(&bump);
78		parse!(in bump &source_text).to_cursors(&mut stream);
79		let mut str = String::new();
80		for sc in stream {
81			sc.write_str(source_text, &mut str).unwrap();
82		}
83		assert_eq!(str, "black white");
84	}
85
86	#[test]
87	fn test_source_cursor_sink_for_string() {
88		let source_text = "black white";
89		let bump = Bump::default();
90		let mut str = String::new();
91		let mut transform = CursorToSourceCursorSink::new(source_text, &mut str);
92		parse!(in bump &source_text).to_cursors(&mut transform);
93		assert_eq!(str, "black white");
94	}
95}