Add DocumentNode and support for <style> tags
This commit is contained in:
		
							
								
								
									
										13
									
								
								fixtures/complex.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								fixtures/complex.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					<html>
 | 
				
			||||||
 | 
					  <head>
 | 
				
			||||||
 | 
					    <title>Document</title>
 | 
				
			||||||
 | 
					    <style>
 | 
				
			||||||
 | 
					      h1 {
 | 
				
			||||||
 | 
					        font-size: 14px;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    </style>
 | 
				
			||||||
 | 
					  </head>
 | 
				
			||||||
 | 
					  <body>
 | 
				
			||||||
 | 
					    <h1>Hello</h1>
 | 
				
			||||||
 | 
					  </body>
 | 
				
			||||||
 | 
					</html>
 | 
				
			||||||
							
								
								
									
										322
									
								
								src/css.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										322
									
								
								src/css.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,322 @@
 | 
				
			|||||||
 | 
					use core::fmt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct Parser {
 | 
				
			||||||
 | 
					    pos: usize,
 | 
				
			||||||
 | 
					    input: String,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Parser {
 | 
				
			||||||
 | 
					    fn eof(&self) -> bool {
 | 
				
			||||||
 | 
					        self.pos >= self.input.len()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn consume_while<F>(&mut self, test: F) -> String
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        F: Fn(char) -> bool,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        let mut result = String::new();
 | 
				
			||||||
 | 
					        while !self.eof() && test(self.next_char()) {
 | 
				
			||||||
 | 
					            result.push(self.consume_char());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn next_char(&self) -> char {
 | 
				
			||||||
 | 
					        self.input[self.pos..].chars().next().unwrap()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn consume_char(&mut self) -> char {
 | 
				
			||||||
 | 
					        let mut iter = self.input[self.pos..].char_indices();
 | 
				
			||||||
 | 
					        let (_, cur_char) = iter.next().unwrap();
 | 
				
			||||||
 | 
					        let (next_pos, _) = iter.next().unwrap_or((1, ' '));
 | 
				
			||||||
 | 
					        self.pos += next_pos;
 | 
				
			||||||
 | 
					        return cur_char;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn consume_whitespace(&mut self) {
 | 
				
			||||||
 | 
					        self.consume_while(|c| c.is_whitespace());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn parse_single_selector(&mut self) -> SingleSelector {
 | 
				
			||||||
 | 
					        let mut selector = SingleSelector::default();
 | 
				
			||||||
 | 
					        while !self.eof() {
 | 
				
			||||||
 | 
					            self.consume_whitespace();
 | 
				
			||||||
 | 
					            match self.next_char() {
 | 
				
			||||||
 | 
					                '#' => {
 | 
				
			||||||
 | 
					                    self.consume_char();
 | 
				
			||||||
 | 
					                    assert!(self.next_char().is_ascii_alphanumeric());
 | 
				
			||||||
 | 
					                    selector.id = Some(self.parse_identifier());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                '.' => {
 | 
				
			||||||
 | 
					                    self.consume_char();
 | 
				
			||||||
 | 
					                    assert!(self.next_char().is_ascii_alphanumeric());
 | 
				
			||||||
 | 
					                    selector.classes.push(self.parse_identifier());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                '*' => {
 | 
				
			||||||
 | 
					                    self.consume_char();
 | 
				
			||||||
 | 
					                    assert!(self.next_char().is_ascii_whitespace());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                '{' => {
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                c => {
 | 
				
			||||||
 | 
					                    if c.is_ascii_alphanumeric() == false {
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    selector.tag_name = Some(self.parse_identifier());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return selector;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn parse_identifier(&mut self) -> String {
 | 
				
			||||||
 | 
					        self.consume_while(|c| (c.is_ascii_alphanumeric() || c == '-'))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn parse_rule(&mut self) -> Rule {
 | 
				
			||||||
 | 
					        Rule {
 | 
				
			||||||
 | 
					            selectors: self.parse_selectors(),
 | 
				
			||||||
 | 
					            declarations: self.parse_declarations(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn parse_selectors(&mut self) -> Vec<Selector> {
 | 
				
			||||||
 | 
					        let mut selectors = Vec::new();
 | 
				
			||||||
 | 
					        loop {
 | 
				
			||||||
 | 
					            selectors.push(Selector::Single(self.parse_single_selector()));
 | 
				
			||||||
 | 
					            self.consume_whitespace();
 | 
				
			||||||
 | 
					            match self.next_char() {
 | 
				
			||||||
 | 
					                ',' => {
 | 
				
			||||||
 | 
					                    self.consume_char();
 | 
				
			||||||
 | 
					                    self.consume_whitespace();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                '{' => break,
 | 
				
			||||||
 | 
					                c => panic!("Unexpected character {} in selector list", c),
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return selectors;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn parse_declarations(&mut self) -> Vec<Declaration> {
 | 
				
			||||||
 | 
					        assert!(self.consume_char() == '{');
 | 
				
			||||||
 | 
					        let mut result = Vec::new();
 | 
				
			||||||
 | 
					        while !self.eof() {
 | 
				
			||||||
 | 
					            self.consume_whitespace();
 | 
				
			||||||
 | 
					            if self.next_char() == '}' {
 | 
				
			||||||
 | 
					                self.consume_char();
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            let identifier = self.parse_identifier();
 | 
				
			||||||
 | 
					            self.consume_whitespace();
 | 
				
			||||||
 | 
					            assert!(self.consume_char() == ':');
 | 
				
			||||||
 | 
					            self.consume_whitespace();
 | 
				
			||||||
 | 
					            let value = self.parse_declaration_value();
 | 
				
			||||||
 | 
					            result.push(Declaration {
 | 
				
			||||||
 | 
					                name: identifier,
 | 
				
			||||||
 | 
					                value: value,
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            self.consume_whitespace();
 | 
				
			||||||
 | 
					            assert!(self.consume_char() == ';');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn parse_declaration_value(&mut self) -> Value {
 | 
				
			||||||
 | 
					        match self.next_char() {
 | 
				
			||||||
 | 
					            '0'..='9' => self.parse_length(),
 | 
				
			||||||
 | 
					            '#' => self.parse_color(),
 | 
				
			||||||
 | 
					            _ => Value::Keyword(self.parse_identifier()),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn parse_hex_pair(&mut self) -> u8 {
 | 
				
			||||||
 | 
					        let s = &self.input[self.pos..self.pos + 2];
 | 
				
			||||||
 | 
					        self.pos += 2;
 | 
				
			||||||
 | 
					        u8::from_str_radix(s, 16).unwrap()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn parse_color(&mut self) -> Value {
 | 
				
			||||||
 | 
					        assert_eq!(self.consume_char(), '#');
 | 
				
			||||||
 | 
					        Value::Color(ColorValue::RGBA(
 | 
				
			||||||
 | 
					            self.parse_hex_pair(),
 | 
				
			||||||
 | 
					            self.parse_hex_pair(),
 | 
				
			||||||
 | 
					            self.parse_hex_pair(),
 | 
				
			||||||
 | 
					            255,
 | 
				
			||||||
 | 
					        ))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn parse_length(&mut self) -> Value {
 | 
				
			||||||
 | 
					        Value::Length(self.parse_float(), self.parse_unit())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn parse_float(&mut self) -> f32 {
 | 
				
			||||||
 | 
					        let s = self.consume_while(|c| match c {
 | 
				
			||||||
 | 
					            '0'..='9' | '.' => true,
 | 
				
			||||||
 | 
					            _ => false,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        s.parse().unwrap()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn parse_unit(&mut self) -> Unit {
 | 
				
			||||||
 | 
					        match &*self.parse_identifier().to_ascii_lowercase() {
 | 
				
			||||||
 | 
					            "px" => Unit::Px,
 | 
				
			||||||
 | 
					            _ => panic!("unrecognized unit"),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn parse_rules(&mut self) -> Vec<Rule> {
 | 
				
			||||||
 | 
					        let mut rules = Vec::new();
 | 
				
			||||||
 | 
					        while !self.eof() {
 | 
				
			||||||
 | 
					            rules.push(self.parse_rule());
 | 
				
			||||||
 | 
					            self.consume_whitespace();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        rules
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn parse(input: String) -> StyleSheet {
 | 
				
			||||||
 | 
					    let mut parser = Parser {
 | 
				
			||||||
 | 
					        pos: 0,
 | 
				
			||||||
 | 
					        input: input,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    StyleSheet {
 | 
				
			||||||
 | 
					        rules: parser.parse_rules(),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Default, Debug, Clone)]
 | 
				
			||||||
 | 
					struct SingleSelector {
 | 
				
			||||||
 | 
					    tag_name: Option<String>,
 | 
				
			||||||
 | 
					    id: Option<String>,
 | 
				
			||||||
 | 
					    classes: Vec<String>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone)]
 | 
				
			||||||
 | 
					enum Selector {
 | 
				
			||||||
 | 
					    Single(SingleSelector),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl fmt::Display for Selector {
 | 
				
			||||||
 | 
					    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
				
			||||||
 | 
					        match &self {
 | 
				
			||||||
 | 
					            Self::Single(selector) => {
 | 
				
			||||||
 | 
					                if let Some(tag_name) = &selector.tag_name {
 | 
				
			||||||
 | 
					                    write!(f, "{}", tag_name).unwrap();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if selector.classes.len() > 0 {
 | 
				
			||||||
 | 
					                    write!(f, ".{}", selector.classes.join(".")).unwrap();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if let Some(id) = &selector.id {
 | 
				
			||||||
 | 
					                    write!(f, "#{}", id).unwrap();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                Ok(())
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone)]
 | 
				
			||||||
 | 
					enum Value {
 | 
				
			||||||
 | 
					    Keyword(String),
 | 
				
			||||||
 | 
					    Length(f32, Unit),
 | 
				
			||||||
 | 
					    Color(ColorValue),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl fmt::Display for Value {
 | 
				
			||||||
 | 
					    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
				
			||||||
 | 
					        match &self {
 | 
				
			||||||
 | 
					            Self::Keyword(keyword) => {
 | 
				
			||||||
 | 
					                write!(f, "{}", keyword)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Self::Color(color) => {
 | 
				
			||||||
 | 
					                write!(f, "{}", color)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Self::Length(amount, unit) => {
 | 
				
			||||||
 | 
					                write!(f, "{}{}", amount, unit)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone)]
 | 
				
			||||||
 | 
					enum ColorValue {
 | 
				
			||||||
 | 
					    RGBA(u8, u8, u8, u8),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl fmt::Display for ColorValue {
 | 
				
			||||||
 | 
					    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
				
			||||||
 | 
					        match &self {
 | 
				
			||||||
 | 
					            Self::RGBA(r, g, b, a) => write!(f, "rgba({}, {}, {}, {})", r, g, b, a),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone)]
 | 
				
			||||||
 | 
					enum Unit {
 | 
				
			||||||
 | 
					    Px,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl fmt::Display for Unit {
 | 
				
			||||||
 | 
					    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
				
			||||||
 | 
					        match &self {
 | 
				
			||||||
 | 
					            Self::Px => write!(f, "px"),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone)]
 | 
				
			||||||
 | 
					struct Declaration {
 | 
				
			||||||
 | 
					    name: String,
 | 
				
			||||||
 | 
					    value: Value,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl fmt::Display for Declaration {
 | 
				
			||||||
 | 
					    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
				
			||||||
 | 
					        write!(f, "{}: {};", self.name, self.value)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone)]
 | 
				
			||||||
 | 
					struct Rule {
 | 
				
			||||||
 | 
					    selectors: Vec<Selector>,
 | 
				
			||||||
 | 
					    declarations: Vec<Declaration>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl fmt::Display for Rule {
 | 
				
			||||||
 | 
					    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
				
			||||||
 | 
					        let prepadding = "  ";
 | 
				
			||||||
 | 
					        let selectors = self
 | 
				
			||||||
 | 
					            .selectors
 | 
				
			||||||
 | 
					            .iter()
 | 
				
			||||||
 | 
					            .map(|d| format!("{d}"))
 | 
				
			||||||
 | 
					            .collect::<Vec<String>>();
 | 
				
			||||||
 | 
					        writeln!(f, "{} {{", selectors.join(", ")).unwrap();
 | 
				
			||||||
 | 
					        let declarations = self
 | 
				
			||||||
 | 
					            .declarations
 | 
				
			||||||
 | 
					            .iter()
 | 
				
			||||||
 | 
					            .map(|d| format!("{d}"))
 | 
				
			||||||
 | 
					            .collect::<Vec<String>>();
 | 
				
			||||||
 | 
					        writeln!(f, "{}{}", prepadding, declarations.join("")).unwrap();
 | 
				
			||||||
 | 
					        writeln!(f, "}}")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone)]
 | 
				
			||||||
 | 
					pub struct StyleSheet {
 | 
				
			||||||
 | 
					    rules: Vec<Rule>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl fmt::Display for StyleSheet {
 | 
				
			||||||
 | 
					    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
				
			||||||
 | 
					        if self.rules.len() > 0 {
 | 
				
			||||||
 | 
					            let rules = self
 | 
				
			||||||
 | 
					                .rules
 | 
				
			||||||
 | 
					                .iter()
 | 
				
			||||||
 | 
					                .map(|r| format!("{r}"))
 | 
				
			||||||
 | 
					                .collect::<Vec<String>>();
 | 
				
			||||||
 | 
					            writeln!(f, "{}", rules.join("\n")).unwrap()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										51
									
								
								src/dom.rs
									
									
									
									
									
								
							
							
						
						
									
										51
									
								
								src/dom.rs
									
									
									
									
									
								
							@@ -1,13 +1,18 @@
 | 
				
			|||||||
use core::fmt;
 | 
					use core::fmt;
 | 
				
			||||||
use std::collections::HashMap;
 | 
					use std::collections::HashMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug)]
 | 
					use crate::{
 | 
				
			||||||
 | 
					    css::{self, StyleSheet},
 | 
				
			||||||
 | 
					    html,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone)]
 | 
				
			||||||
pub enum AttrValue {
 | 
					pub enum AttrValue {
 | 
				
			||||||
    Text(String),
 | 
					    Text(String),
 | 
				
			||||||
    Implicit,
 | 
					    Implicit,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Default)]
 | 
					#[derive(Debug, Default, Clone)]
 | 
				
			||||||
pub struct AttrMap(pub HashMap<String, AttrValue>);
 | 
					pub struct AttrMap(pub HashMap<String, AttrValue>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl fmt::Display for AttrMap {
 | 
					impl fmt::Display for AttrMap {
 | 
				
			||||||
@@ -24,23 +29,24 @@ impl fmt::Display for AttrMap {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug)]
 | 
					#[derive(Debug, Clone)]
 | 
				
			||||||
pub struct ElementData {
 | 
					pub struct ElementData {
 | 
				
			||||||
    tag_name: String,
 | 
					    tag_name: String,
 | 
				
			||||||
    attributes: AttrMap,
 | 
					    attributes: AttrMap,
 | 
				
			||||||
    child_nodes: Vec<Node>,
 | 
					    child_nodes: Vec<Node>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug)]
 | 
					#[derive(Debug, Clone)]
 | 
				
			||||||
pub enum NodeType {
 | 
					pub enum NodeType {
 | 
				
			||||||
    ElementNode(ElementData),
 | 
					    ElementNode(ElementData),
 | 
				
			||||||
    TextNode(String),
 | 
					    TextNode(String),
 | 
				
			||||||
    CommentNode(String),
 | 
					    CommentNode(String),
 | 
				
			||||||
 | 
					    DocumentNode(DocumentData),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug)]
 | 
					#[derive(Debug, Clone)]
 | 
				
			||||||
pub struct Node {
 | 
					pub struct Node {
 | 
				
			||||||
    node_type: NodeType,
 | 
					    pub node_type: NodeType,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Node {
 | 
					impl Node {
 | 
				
			||||||
@@ -72,6 +78,7 @@ impl Node {
 | 
				
			|||||||
                writeln!(f, "{}{}", prepadding, text).unwrap();
 | 
					                writeln!(f, "{}{}", prepadding, text).unwrap();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            NodeType::CommentNode(text) => writeln!(f, "{}<!-- {} -->", prepadding, text).unwrap(),
 | 
					            NodeType::CommentNode(text) => writeln!(f, "{}<!-- {} -->", prepadding, text).unwrap(),
 | 
				
			||||||
 | 
					            NodeType::DocumentNode(_) => {}
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -83,6 +90,30 @@ impl fmt::Display for Node {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone)]
 | 
				
			||||||
 | 
					pub struct DocumentData {
 | 
				
			||||||
 | 
					    pub root: Box<Option<Node>>,
 | 
				
			||||||
 | 
					    pub stylesheets: Vec<StyleSheet>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl DocumentData {
 | 
				
			||||||
 | 
					    pub fn load_css(&mut self, styling: String) {
 | 
				
			||||||
 | 
					        self.stylesheets.push(css::parse(styling));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn load_document(&mut self, document: String) {
 | 
				
			||||||
 | 
					        let node = html::parse(document, self);
 | 
				
			||||||
 | 
					        _ = self.root.insert(node);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn new() -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            root: Box::new(None),
 | 
				
			||||||
 | 
					            stylesheets: vec![],
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn text(data: String) -> Node {
 | 
					pub fn text(data: String) -> Node {
 | 
				
			||||||
    Node {
 | 
					    Node {
 | 
				
			||||||
        node_type: NodeType::TextNode(data),
 | 
					        node_type: NodeType::TextNode(data),
 | 
				
			||||||
@@ -104,3 +135,11 @@ pub fn comment(text: String) -> Node {
 | 
				
			|||||||
        node_type: NodeType::CommentNode(text),
 | 
					        node_type: NodeType::CommentNode(text),
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn parse(document: String) -> Node {
 | 
				
			||||||
 | 
					    let mut context = DocumentData::new();
 | 
				
			||||||
 | 
					    context.load_document(document);
 | 
				
			||||||
 | 
					    Node {
 | 
				
			||||||
 | 
					        node_type: NodeType::DocumentNode(context),
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										17
									
								
								src/html.rs
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								src/html.rs
									
									
									
									
									
								
							@@ -1,14 +1,15 @@
 | 
				
			|||||||
#![allow(dead_code)]
 | 
					#![allow(dead_code)]
 | 
				
			||||||
use std::collections::HashMap;
 | 
					use std::collections::HashMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::dom::{comment, element, text, AttrMap, AttrValue, Node};
 | 
					use crate::dom::{comment, element, text, AttrMap, AttrValue, DocumentData, Node, NodeType};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct Parser {
 | 
					struct Parser<'a> {
 | 
				
			||||||
    pos: usize,
 | 
					    pos: usize,
 | 
				
			||||||
    input: String,
 | 
					    input: String,
 | 
				
			||||||
 | 
					    context: &'a mut DocumentData,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Parser {
 | 
					impl Parser<'_> {
 | 
				
			||||||
    fn next_char(&self) -> char {
 | 
					    fn next_char(&self) -> char {
 | 
				
			||||||
        self.input[self.pos..].chars().next().unwrap()
 | 
					        self.input[self.pos..].chars().next().unwrap()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -80,6 +81,13 @@ impl Parser {
 | 
				
			|||||||
        // Contents.
 | 
					        // Contents.
 | 
				
			||||||
        let children = self.parse_nodes();
 | 
					        let children = self.parse_nodes();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if tag_name == "style" {
 | 
				
			||||||
 | 
					            let inner_node = children.first().unwrap();
 | 
				
			||||||
 | 
					            if let NodeType::TextNode(styling) = &inner_node.node_type {
 | 
				
			||||||
 | 
					                self.context.load_css(styling.clone());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Closing tag.
 | 
					        // Closing tag.
 | 
				
			||||||
        assert!(self.consume_char() == '<');
 | 
					        assert!(self.consume_char() == '<');
 | 
				
			||||||
        assert!(self.consume_char() == '/');
 | 
					        assert!(self.consume_char() == '/');
 | 
				
			||||||
@@ -159,10 +167,11 @@ impl Parser {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn parse(source: String) -> Node {
 | 
					pub fn parse(source: String, context: &mut DocumentData) -> Node {
 | 
				
			||||||
    let mut parser = Parser {
 | 
					    let mut parser = Parser {
 | 
				
			||||||
        pos: 0,
 | 
					        pos: 0,
 | 
				
			||||||
        input: source,
 | 
					        input: source,
 | 
				
			||||||
 | 
					        context: context,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    let mut nodes = parser.parse_nodes();
 | 
					    let mut nodes = parser.parse_nodes();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										334
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										334
									
								
								src/main.rs
									
									
									
									
									
								
							@@ -1,335 +1,19 @@
 | 
				
			|||||||
use core::fmt;
 | 
					 | 
				
			||||||
use std::io;
 | 
					use std::io;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use dom::NodeType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mod css;
 | 
				
			||||||
mod dom;
 | 
					mod dom;
 | 
				
			||||||
mod html;
 | 
					mod html;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct Parser {
 | 
					 | 
				
			||||||
    pos: usize,
 | 
					 | 
				
			||||||
    input: String,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl Parser {
 | 
					 | 
				
			||||||
    fn eof(&self) -> bool {
 | 
					 | 
				
			||||||
        self.pos >= self.input.len()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn consume_while<F>(&mut self, test: F) -> String
 | 
					 | 
				
			||||||
    where
 | 
					 | 
				
			||||||
        F: Fn(char) -> bool,
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        let mut result = String::new();
 | 
					 | 
				
			||||||
        while !self.eof() && test(self.next_char()) {
 | 
					 | 
				
			||||||
            result.push(self.consume_char());
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return result;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn next_char(&self) -> char {
 | 
					 | 
				
			||||||
        self.input[self.pos..].chars().next().unwrap()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn consume_char(&mut self) -> char {
 | 
					 | 
				
			||||||
        let mut iter = self.input[self.pos..].char_indices();
 | 
					 | 
				
			||||||
        let (_, cur_char) = iter.next().unwrap();
 | 
					 | 
				
			||||||
        let (next_pos, _) = iter.next().unwrap_or((1, ' '));
 | 
					 | 
				
			||||||
        self.pos += next_pos;
 | 
					 | 
				
			||||||
        return cur_char;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn consume_whitespace(&mut self) {
 | 
					 | 
				
			||||||
        self.consume_while(|c| c.is_whitespace());
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn parse_single_selector(&mut self) -> SingleSelector {
 | 
					 | 
				
			||||||
        let mut selector = SingleSelector::default();
 | 
					 | 
				
			||||||
        while !self.eof() {
 | 
					 | 
				
			||||||
            self.consume_whitespace();
 | 
					 | 
				
			||||||
            match self.next_char() {
 | 
					 | 
				
			||||||
                '#' => {
 | 
					 | 
				
			||||||
                    self.consume_char();
 | 
					 | 
				
			||||||
                    assert!(self.next_char().is_ascii_alphanumeric());
 | 
					 | 
				
			||||||
                    selector.id = Some(self.parse_identifier());
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                '.' => {
 | 
					 | 
				
			||||||
                    self.consume_char();
 | 
					 | 
				
			||||||
                    assert!(self.next_char().is_ascii_alphanumeric());
 | 
					 | 
				
			||||||
                    selector.classes.push(self.parse_identifier());
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                '*' => {
 | 
					 | 
				
			||||||
                    self.consume_char();
 | 
					 | 
				
			||||||
                    assert!(self.next_char().is_ascii_whitespace());
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                '{' => {
 | 
					 | 
				
			||||||
                    break;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                c => {
 | 
					 | 
				
			||||||
                    if c.is_ascii_alphanumeric() == false {
 | 
					 | 
				
			||||||
                        break;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    selector.tag_name = Some(self.parse_identifier());
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return selector;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn parse_identifier(&mut self) -> String {
 | 
					 | 
				
			||||||
        self.consume_while(|c| (c.is_ascii_alphanumeric() || c == '-'))
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn parse_rule(&mut self) -> Rule {
 | 
					 | 
				
			||||||
        Rule {
 | 
					 | 
				
			||||||
            selectors: self.parse_selectors(),
 | 
					 | 
				
			||||||
            declarations: self.parse_declarations(),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn parse_selectors(&mut self) -> Vec<Selector> {
 | 
					 | 
				
			||||||
        let mut selectors = Vec::new();
 | 
					 | 
				
			||||||
        loop {
 | 
					 | 
				
			||||||
            selectors.push(Selector::Single(self.parse_single_selector()));
 | 
					 | 
				
			||||||
            self.consume_whitespace();
 | 
					 | 
				
			||||||
            match self.next_char() {
 | 
					 | 
				
			||||||
                ',' => {
 | 
					 | 
				
			||||||
                    self.consume_char();
 | 
					 | 
				
			||||||
                    self.consume_whitespace();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                '{' => break,
 | 
					 | 
				
			||||||
                c => panic!("Unexpected character {} in selector list", c),
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return selectors;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn parse_declarations(&mut self) -> Vec<Declaration> {
 | 
					 | 
				
			||||||
        assert!(self.consume_char() == '{');
 | 
					 | 
				
			||||||
        let mut result = Vec::new();
 | 
					 | 
				
			||||||
        while !self.eof() {
 | 
					 | 
				
			||||||
            self.consume_whitespace();
 | 
					 | 
				
			||||||
            if self.next_char() == '}' {
 | 
					 | 
				
			||||||
                self.consume_char();
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            let identifier = self.parse_identifier();
 | 
					 | 
				
			||||||
            self.consume_whitespace();
 | 
					 | 
				
			||||||
            assert!(self.consume_char() == ':');
 | 
					 | 
				
			||||||
            self.consume_whitespace();
 | 
					 | 
				
			||||||
            let value = self.parse_declaration_value();
 | 
					 | 
				
			||||||
            result.push(Declaration {
 | 
					 | 
				
			||||||
                name: identifier,
 | 
					 | 
				
			||||||
                value: value,
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
            self.consume_whitespace();
 | 
					 | 
				
			||||||
            assert!(self.consume_char() == ';');
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return result;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn parse_declaration_value(&mut self) -> Value {
 | 
					 | 
				
			||||||
        match self.next_char() {
 | 
					 | 
				
			||||||
            '0'..='9' => self.parse_length(),
 | 
					 | 
				
			||||||
            '#' => self.parse_color(),
 | 
					 | 
				
			||||||
            _ => Value::Keyword(self.parse_identifier()),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn parse_hex_pair(&mut self) -> u8 {
 | 
					 | 
				
			||||||
        let s = &self.input[self.pos..self.pos + 2];
 | 
					 | 
				
			||||||
        self.pos += 2;
 | 
					 | 
				
			||||||
        u8::from_str_radix(s, 16).unwrap()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn parse_color(&mut self) -> Value {
 | 
					 | 
				
			||||||
        assert_eq!(self.consume_char(), '#');
 | 
					 | 
				
			||||||
        Value::Color(ColorValue::RGBA(
 | 
					 | 
				
			||||||
            self.parse_hex_pair(),
 | 
					 | 
				
			||||||
            self.parse_hex_pair(),
 | 
					 | 
				
			||||||
            self.parse_hex_pair(),
 | 
					 | 
				
			||||||
            255,
 | 
					 | 
				
			||||||
        ))
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn parse_length(&mut self) -> Value {
 | 
					 | 
				
			||||||
        Value::Length(self.parse_float(), self.parse_unit())
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn parse_float(&mut self) -> f32 {
 | 
					 | 
				
			||||||
        let s = self.consume_while(|c| match c {
 | 
					 | 
				
			||||||
            '0'..='9' | '.' => true,
 | 
					 | 
				
			||||||
            _ => false,
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
        s.parse().unwrap()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn parse_unit(&mut self) -> Unit {
 | 
					 | 
				
			||||||
        match &*self.parse_identifier().to_ascii_lowercase() {
 | 
					 | 
				
			||||||
            "px" => Unit::Px,
 | 
					 | 
				
			||||||
            _ => panic!("unrecognized unit"),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn parse_rules(&mut self) -> Vec<Rule> {
 | 
					 | 
				
			||||||
        let mut rules = Vec::new();
 | 
					 | 
				
			||||||
        while !self.eof() {
 | 
					 | 
				
			||||||
            rules.push(self.parse_rule());
 | 
					 | 
				
			||||||
            self.consume_whitespace();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        rules
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
fn parse(input: String) -> StyleSheet {
 | 
					 | 
				
			||||||
    let mut parser = Parser {
 | 
					 | 
				
			||||||
        pos: 0,
 | 
					 | 
				
			||||||
        input: input,
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    StyleSheet {
 | 
					 | 
				
			||||||
        rules: parser.parse_rules(),
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[derive(Default, Debug)]
 | 
					 | 
				
			||||||
struct SingleSelector {
 | 
					 | 
				
			||||||
    tag_name: Option<String>,
 | 
					 | 
				
			||||||
    id: Option<String>,
 | 
					 | 
				
			||||||
    classes: Vec<String>,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[derive(Debug)]
 | 
					 | 
				
			||||||
enum Selector {
 | 
					 | 
				
			||||||
    Single(SingleSelector),
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl fmt::Display for Selector {
 | 
					 | 
				
			||||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
					 | 
				
			||||||
        match &self {
 | 
					 | 
				
			||||||
            Self::Single(selector) => {
 | 
					 | 
				
			||||||
                if let Some(tag_name) = &selector.tag_name {
 | 
					 | 
				
			||||||
                    write!(f, "{}", tag_name).unwrap();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                if selector.classes.len() > 0 {
 | 
					 | 
				
			||||||
                    write!(f, ".{}", selector.classes.join(".")).unwrap();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                if let Some(id) = &selector.id {
 | 
					 | 
				
			||||||
                    write!(f, "#{}", id).unwrap();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                Ok(())
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[derive(Debug)]
 | 
					 | 
				
			||||||
enum Value {
 | 
					 | 
				
			||||||
    Keyword(String),
 | 
					 | 
				
			||||||
    Length(f32, Unit),
 | 
					 | 
				
			||||||
    Color(ColorValue),
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl fmt::Display for Value {
 | 
					 | 
				
			||||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
					 | 
				
			||||||
        match &self {
 | 
					 | 
				
			||||||
            Self::Keyword(keyword) => {
 | 
					 | 
				
			||||||
                write!(f, "{}", keyword)
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            Self::Color(color) => {
 | 
					 | 
				
			||||||
                write!(f, "{}", color)
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            Self::Length(amount, unit) => {
 | 
					 | 
				
			||||||
                write!(f, "{}{}", amount, unit)
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[derive(Debug)]
 | 
					 | 
				
			||||||
enum ColorValue {
 | 
					 | 
				
			||||||
    RGBA(u8, u8, u8, u8),
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl fmt::Display for ColorValue {
 | 
					 | 
				
			||||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
					 | 
				
			||||||
        match &self {
 | 
					 | 
				
			||||||
            Self::RGBA(r, g, b, a) => write!(f, "rgba({}, {}, {}, {})", r, g, b, a),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[derive(Debug)]
 | 
					 | 
				
			||||||
enum Unit {
 | 
					 | 
				
			||||||
    Px,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl fmt::Display for Unit {
 | 
					 | 
				
			||||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
					 | 
				
			||||||
        match &self {
 | 
					 | 
				
			||||||
            Self::Px => write!(f, "px"),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[derive(Debug)]
 | 
					 | 
				
			||||||
struct Declaration {
 | 
					 | 
				
			||||||
    name: String,
 | 
					 | 
				
			||||||
    value: Value,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl fmt::Display for Declaration {
 | 
					 | 
				
			||||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
					 | 
				
			||||||
        write!(f, "{}: {};", self.name, self.value)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[derive(Debug)]
 | 
					 | 
				
			||||||
struct Rule {
 | 
					 | 
				
			||||||
    selectors: Vec<Selector>,
 | 
					 | 
				
			||||||
    declarations: Vec<Declaration>,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl fmt::Display for Rule {
 | 
					 | 
				
			||||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
					 | 
				
			||||||
        let prepadding = "  ";
 | 
					 | 
				
			||||||
        let selectors = self
 | 
					 | 
				
			||||||
            .selectors
 | 
					 | 
				
			||||||
            .iter()
 | 
					 | 
				
			||||||
            .map(|d| format!("{d}"))
 | 
					 | 
				
			||||||
            .collect::<Vec<String>>();
 | 
					 | 
				
			||||||
        writeln!(f, "{} {{", selectors.join(", ")).unwrap();
 | 
					 | 
				
			||||||
        let declarations = self
 | 
					 | 
				
			||||||
            .declarations
 | 
					 | 
				
			||||||
            .iter()
 | 
					 | 
				
			||||||
            .map(|d| format!("{d}"))
 | 
					 | 
				
			||||||
            .collect::<Vec<String>>();
 | 
					 | 
				
			||||||
        writeln!(f, "{}{}", prepadding, declarations.join("")).unwrap();
 | 
					 | 
				
			||||||
        writeln!(f, "}}")
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[derive(Debug)]
 | 
					 | 
				
			||||||
struct StyleSheet {
 | 
					 | 
				
			||||||
    rules: Vec<Rule>,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl fmt::Display for StyleSheet {
 | 
					 | 
				
			||||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
					 | 
				
			||||||
        if self.rules.len() > 0 {
 | 
					 | 
				
			||||||
            let rules = self
 | 
					 | 
				
			||||||
                .rules
 | 
					 | 
				
			||||||
                .iter()
 | 
					 | 
				
			||||||
                .map(|r| format!("{r}"))
 | 
					 | 
				
			||||||
                .collect::<Vec<String>>();
 | 
					 | 
				
			||||||
            writeln!(f, "{}", rules.join("\n")).unwrap()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        Ok(())
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
fn main() {
 | 
					fn main() {
 | 
				
			||||||
    let mut input = String::new();
 | 
					    let mut input = String::new();
 | 
				
			||||||
    let stdin = io::stdin();
 | 
					    let stdin = io::stdin();
 | 
				
			||||||
    stdin.read_line(&mut input).unwrap();
 | 
					    stdin.read_line(&mut input).unwrap();
 | 
				
			||||||
    // let nodes = html::parse(input);
 | 
					    // let input = "<html><head><title>Document</title><style>h1{font-size: 14px;}</style></head><body><h1>Hello</h1></body></html>".into();
 | 
				
			||||||
    let nodes = parse(input);
 | 
					
 | 
				
			||||||
    println!("{nodes}");
 | 
					    let node = dom::parse(input);
 | 
				
			||||||
 | 
					    if let NodeType::DocumentNode(data) = node.node_type {
 | 
				
			||||||
 | 
					        println!("{}", data.stylesheets[0]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user