glass_easel_stylesheet_compiler/
output.rs

1use std::fmt::Write;
2
3use cssparser::{ToCss, Token, TokenSerializationType};
4use sourcemap::{SourceMap, SourceMapBuilder};
5
6use crate::step::StepToken;
7
8pub struct StyleSheetOutput {
9    s: String,
10    prev_ser_type: TokenSerializationType,
11    source_map: SourceMapBuilder,
12    source_id: u32,
13    utf16_len: u32,
14}
15
16impl StyleSheetOutput {
17    pub(crate) fn new(path: &str, source_css: &str) -> Self {
18        let mut source_map = SourceMapBuilder::new(None);
19        let source_id = source_map.add_source(path);
20        source_map.set_source_contents(source_id, Some(source_css));
21        Self {
22            s: String::new(),
23            prev_ser_type: TokenSerializationType::Nothing,
24            source_id,
25            source_map,
26            utf16_len: 0,
27        }
28    }
29
30    pub fn write(&self, mut w: impl std::io::Write) -> std::io::Result<()> {
31        w.write_all(self.s.as_bytes())
32    }
33
34    pub fn write_str(&self, mut w: impl std::fmt::Write) -> std::fmt::Result {
35        write!(w, "{}", self.s)
36    }
37
38    pub fn write_source_map(self, w: impl std::io::Write) -> Result<(), sourcemap::Error> {
39        self.source_map.into_sourcemap().to_writer(w)
40    }
41
42    pub fn extract_source_map(self) -> SourceMap {
43        self.source_map.into_sourcemap()
44    }
45
46    pub(crate) fn cur_utf8_len(&self) -> usize {
47        self.s.len()
48    }
49
50    pub(crate) fn get_output_segment(&self, range: std::ops::Range<usize>) -> &str {
51        &self.s[range]
52    }
53
54    pub(crate) fn append_raw(&mut self, s: &str) {
55        self.prev_ser_type = TokenSerializationType::Nothing;
56        let output_start_pos = self.s.len();
57        self.s += s;
58        self.utf16_len += str::encode_utf16(&self.s[output_start_pos..]).count() as u32;
59    }
60
61    pub(crate) fn append_token(&mut self, token: StepToken, src: Option<Token>) {
62        let next_ser_type = token.serialization_type();
63        if self
64            .prev_ser_type
65            .needs_separator_when_before(next_ser_type)
66        {
67            write!(&mut self.s, " ").unwrap();
68            self.utf16_len += 1;
69        }
70        self.prev_ser_type = next_ser_type;
71        let output_start_pos = self.s.len();
72        token.to_css(&mut self.s).unwrap();
73        let name = src.map(|x| {
74            let s = x.to_css_string();
75            self.source_map.add_name(&s)
76        });
77        self.source_map.add_raw(
78            0,
79            self.utf16_len,
80            token.position.line,
81            token.position.utf16_col,
82            Some(self.source_id),
83            name,
84        );
85        self.utf16_len += str::encode_utf16(&self.s[output_start_pos..]).count() as u32;
86    }
87
88    pub(crate) fn append_token_space_preserved(&mut self, token: StepToken, src: Option<Token>) {
89        if let Token::WhiteSpace(_) = &*token {
90            self.prev_ser_type = token.serialization_type();
91            self.s.push(' ');
92            self.utf16_len += 1;
93        } else {
94            self.append_token(token, src);
95        }
96    }
97}