1#![warn(unused_crate_dependencies)]
37#![warn(clippy::print_stdout, clippy::print_stderr)]
38#![cfg_attr(target_pointer_width = "64", warn(clippy::trivially_copy_pass_by_ref))]
40#![cfg_attr(docsrs, feature(doc_auto_cfg))]
42#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
43#![allow(
47 missing_debug_implementations,
48 unreachable_pub,
49 clippy::use_self,
50 clippy::missing_assert_message,
51 clippy::missing_panics_doc,
52 clippy::exhaustive_enums,
53 clippy::unseparated_literal_suffix
54)]
55#![cfg_attr(test, allow(unused_crate_dependencies))] extern crate alloc;
58
59use alloc::vec::Vec;
60use core::fmt;
61
62use log::warn;
63
64mod selector;
65mod stream;
66
67pub use selector::*;
68use stream::Stream;
69
70#[derive(Clone, Copy, PartialEq, Debug)]
72pub enum Error {
73 UnexpectedEndOfStream,
77
78 InvalidIdent(TextPos),
80
81 InvalidComment(TextPos),
83
84 InvalidValue(TextPos),
86
87 #[allow(missing_docs)]
89 InvalidByte {
90 expected: u8,
91 actual: u8,
92 pos: TextPos,
93 },
94
95 SelectorMissing,
97
98 UnexpectedSelector,
100
101 UnexpectedCombinator,
103
104 InvalidAttributeSelector,
106
107 InvalidLanguagePseudoClass,
109}
110
111impl fmt::Display for Error {
112 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113 match *self {
114 Error::UnexpectedEndOfStream => {
115 write!(f, "unexpected end of stream")
116 }
117 Error::InvalidIdent(pos) => {
118 write!(f, "invalid ident at {}", pos)
119 }
120 Error::InvalidComment(pos) => {
121 write!(f, "invalid comment at {}", pos)
122 }
123 Error::InvalidValue(pos) => {
124 write!(f, "invalid value at {}", pos)
125 }
126 Error::InvalidByte {
127 expected,
128 actual,
129 pos,
130 } => {
131 write!(
132 f,
133 "expected '{}' not '{}' at {}",
134 expected as char, actual as char, pos
135 )
136 }
137 Error::SelectorMissing => {
138 write!(f, "selector missing")
139 }
140 Error::UnexpectedSelector => {
141 write!(f, "unexpected selector")
142 }
143 Error::UnexpectedCombinator => {
144 write!(f, "unexpected combinator")
145 }
146 Error::InvalidAttributeSelector => {
147 write!(f, "invalid or unsupported attribute selector")
148 }
149 Error::InvalidLanguagePseudoClass => {
150 write!(f, "invalid language pseudo-class")
151 }
152 }
153 }
154}
155
156#[cfg(feature = "std")]
157impl std::error::Error for Error {}
158
159#[derive(Clone, Copy, PartialEq, Debug)]
163#[allow(missing_docs)]
164pub struct TextPos {
165 pub row: u32,
166 pub col: u32,
167}
168
169impl TextPos {
170 pub fn new(row: u32, col: u32) -> TextPos {
174 TextPos { row, col }
175 }
176}
177
178impl fmt::Display for TextPos {
179 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180 write!(f, "{}:{}", self.row, self.col)
181 }
182}
183
184#[derive(Clone, Copy, PartialEq, Debug)]
186#[allow(missing_docs)]
187pub struct Declaration<'a> {
188 pub name: &'a str,
189 pub value: &'a str,
190 pub important: bool,
191}
192
193#[derive(Clone, Debug)]
195pub struct Rule<'a> {
196 pub selector: Selector<'a>,
198 pub declarations: Vec<Declaration<'a>>,
200}
201
202#[derive(Clone, Debug)]
204pub struct StyleSheet<'a> {
205 pub rules: Vec<Rule<'a>>,
207}
208
209impl<'a> StyleSheet<'a> {
210 pub fn new() -> Self {
212 StyleSheet { rules: Vec::new() }
213 }
214
215 pub fn parse(text: &'a str) -> Self {
225 let mut sheet = StyleSheet::new();
226 sheet.parse_more(text);
227 sheet
228 }
229
230 pub fn parse_more(&mut self, text: &'a str) {
232 let mut s = Stream::from(text);
233
234 if s.skip_spaces_and_comments().is_err() {
235 return;
236 }
237
238 while !s.at_end() {
239 if s.skip_spaces_and_comments().is_err() {
240 break;
241 }
242
243 let _ = consume_statement(&mut s, &mut self.rules);
244 }
245
246 if !s.at_end() {
247 warn!("{} bytes were left.", s.slice_tail().len());
248 }
249
250 self.rules.retain(|rule| !rule.declarations.is_empty());
252
253 self.rules
255 .sort_by_cached_key(|rule| rule.selector.specificity());
256 }
257}
258
259impl fmt::Display for StyleSheet<'_> {
260 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
261 for (i, rule) in self.rules.iter().enumerate() {
262 write!(f, "{} {{ ", rule.selector)?;
263 for dec in &rule.declarations {
264 write!(f, "{}:{}", dec.name, dec.value)?;
265 if dec.important {
266 write!(f, " !important")?;
267 }
268 write!(f, ";")?;
269 }
270 write!(f, " }}")?;
271
272 if i != self.rules.len() - 1 {
273 writeln!(f)?;
274 }
275 }
276
277 Ok(())
278 }
279}
280
281impl Default for StyleSheet<'_> {
282 fn default() -> Self {
283 Self::new()
284 }
285}
286
287fn consume_statement<'a>(s: &mut Stream<'a>, rules: &mut Vec<Rule<'a>>) -> Result<(), Error> {
288 if s.curr_byte() == Ok(b'@') {
289 s.advance(1);
290 consume_at_rule(s)
291 } else {
292 consume_rule_set(s, rules)
293 }
294}
295
296fn consume_at_rule(s: &mut Stream<'_>) -> Result<(), Error> {
297 let ident = s.consume_ident()?;
298 warn!("The @{} rule is not supported. Skipped.", ident);
299
300 s.skip_bytes(|c| c != b';' && c != b'{');
301
302 match s.curr_byte()? {
303 b';' => s.advance(1),
304 b'{' => consume_block(s),
305 _ => {}
306 }
307
308 Ok(())
309}
310
311fn consume_rule_set<'a>(s: &mut Stream<'a>, rules: &mut Vec<Rule<'a>>) -> Result<(), Error> {
312 let start_rule_idx = rules.len();
313
314 while s.curr_byte()? == b',' || start_rule_idx == rules.len() {
315 if s.curr_byte()? == b',' {
316 s.advance(1);
317 }
318
319 let (selector, offset) = parse(s.slice_tail());
320 s.advance(offset);
321 s.skip_spaces();
322
323 if let Some(selector) = selector {
324 rules.push(Rule {
325 selector,
326 declarations: Vec::new(),
327 });
328 }
329
330 match s.curr_byte()? {
331 b'{' => break,
332 b',' => {}
333 _ => {
334 s.skip_bytes(|c| c != b'{');
335 break;
336 }
337 }
338 }
339
340 s.try_consume_byte(b'{');
341
342 let declarations = consume_declarations(s)?;
343 for rule in rules.iter_mut().skip(start_rule_idx) {
344 rule.declarations = declarations.clone();
345 }
346
347 s.try_consume_byte(b'}');
348
349 Ok(())
350}
351
352fn consume_block(s: &mut Stream<'_>) {
353 s.try_consume_byte(b'{');
354 consume_until_block_end(s);
355}
356
357fn consume_until_block_end(s: &mut Stream<'_>) {
358 let mut braces = 0;
363 while !s.at_end() {
364 match s.curr_byte_unchecked() {
365 b'{' => {
366 braces += 1;
367 }
368 b'}' => {
369 if braces == 0 {
370 break;
371 } else {
372 braces -= 1;
373 }
374 }
375 _ => {}
376 }
377
378 s.advance(1);
379 }
380
381 s.try_consume_byte(b'}');
382}
383
384fn consume_declarations<'a>(s: &mut Stream<'a>) -> Result<Vec<Declaration<'a>>, Error> {
385 let mut declarations = Vec::new();
386
387 while !s.at_end() && s.curr_byte() != Ok(b'}') {
388 match consume_declaration(s) {
389 Ok(declaration) => declarations.push(declaration),
390 Err(_) => {
391 consume_until_block_end(s);
392 break;
393 }
394 }
395 }
396
397 Ok(declarations)
398}
399
400pub struct DeclarationTokenizer<'a> {
414 stream: Stream<'a>,
415}
416
417impl<'a> From<&'a str> for DeclarationTokenizer<'a> {
418 fn from(text: &'a str) -> Self {
419 DeclarationTokenizer {
420 stream: Stream::from(text),
421 }
422 }
423}
424
425impl<'a> Iterator for DeclarationTokenizer<'a> {
426 type Item = Declaration<'a>;
427
428 fn next(&mut self) -> Option<Self::Item> {
429 let _ = self.stream.skip_spaces_and_comments();
430
431 if self.stream.at_end() {
432 return None;
433 }
434
435 match consume_declaration(&mut self.stream) {
436 Ok(v) => Some(v),
437 Err(_) => {
438 self.stream.jump_to_end();
439 None
440 }
441 }
442 }
443}
444
445fn consume_declaration<'a>(s: &mut Stream<'a>) -> Result<Declaration<'a>, Error> {
446 s.skip_spaces_and_comments()?;
447
448 if s.curr_byte() == Ok(b'*') {
452 s.advance(1);
453 }
454
455 let name = s.consume_ident()?;
456
457 s.skip_spaces_and_comments()?;
458 s.consume_byte(b':')?;
459 s.skip_spaces_and_comments()?;
460
461 let start = s.pos();
463 let mut end = s.pos();
464 while consume_term(s).is_ok() {
465 end = s.pos();
466 s.skip_spaces_and_comments()?;
467 }
468 let value = s.slice_range(start, end).trim();
469
470 s.skip_spaces_and_comments()?;
471
472 let mut important = false;
474 if s.curr_byte() == Ok(b'!') {
475 s.advance(1);
476 s.skip_spaces_and_comments()?;
477 if s.slice_tail().starts_with("important") {
478 s.advance(9);
479 important = true;
480 }
481 }
482
483 s.skip_spaces_and_comments()?;
484
485 while s.curr_byte() == Ok(b';') {
486 s.advance(1);
487 s.skip_spaces_and_comments()?;
488 }
489
490 s.skip_spaces_and_comments()?;
491
492 if value.is_empty() {
493 return Err(Error::InvalidValue(s.gen_text_pos_from(start)));
494 }
495
496 Ok(Declaration {
497 name,
498 value,
499 important,
500 })
501}
502
503fn consume_term(s: &mut Stream<'_>) -> Result<(), Error> {
504 fn consume_digits(s: &mut Stream<'_>) {
505 while let Ok(b'0'..=b'9') = s.curr_byte() {
506 s.advance(1);
507 }
508 }
509
510 match s.curr_byte()? {
511 b'#' => {
512 s.advance(1);
513 match s.consume_ident() {
514 Ok(_) => {}
515 Err(_) => {
516 while let Ok(c) = s.curr_byte() {
518 match c {
519 b'0'..=b'9' | b'a'..=b'f' | b'A'..=b'F' => s.advance(1),
520 _ => break,
521 }
522 }
523 }
524 }
525 }
526 b'+' | b'-' | b'0'..=b'9' | b'.' => {
527 s.advance(1);
530 consume_digits(s);
531 if s.curr_byte() == Ok(b'.') {
532 s.advance(1);
533 consume_digits(s);
534 }
535
536 if s.curr_byte() == Ok(b'%') {
537 s.advance(1);
538 } else {
539 let _ = s.consume_ident();
541 }
542 }
543 b'\'' | b'"' => {
544 s.consume_string()?;
545 }
546 b',' => {
547 s.advance(1);
548 }
549 _ => {
550 let _ = s.consume_ident()?;
551
552 if s.curr_byte() == Ok(b'(') {
554 s.skip_bytes(|c| c != b')');
555 s.consume_byte(b')')?;
556 }
557 }
558 }
559
560 Ok(())
561}