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