diff --git a/src/css.rs b/src/css.rs index abb4e6c..6b7f11d 100644 --- a/src/css.rs +++ b/src/css.rs @@ -1,9 +1,9 @@ use std; -struct SimpleSelector { - tag_name: Option, - id: Option, - class: Vec, +pub struct SimpleSelector { + pub tag_name: Option, + pub id: Option, + pub class: Vec, } struct ChainSelector { @@ -12,7 +12,7 @@ struct ChainSelector { class: Vec>, } -enum Selector { +pub enum Selector { Simple(SimpleSelector), //Chain(ChainSelector), } @@ -43,7 +43,8 @@ impl std::fmt::Display for Selector { } } -enum Unit { +#[derive(Clone)] +pub enum Unit { Px, Em, Rm, @@ -65,14 +66,16 @@ impl std::fmt::Display for Unit { } } -struct Color { +#[derive(Clone)] +pub struct Color { r: u8, g: u8, b: u8, a: u8, } -enum Value { +#[derive(Clone)] +pub enum Value { Keyword(String), Length(f32, Unit), ColorValue(Color), @@ -88,9 +91,9 @@ impl std::fmt::Display for Value { } } -struct Declaration { - name: String, - value: Value, +pub struct Declaration { + pub name: String, + pub value: Value, } impl std::fmt::Display for Declaration { @@ -99,9 +102,9 @@ impl std::fmt::Display for Declaration { } } -struct Rule { - selectors: Vec, - declarations: Vec, +pub struct Rule { + pub selectors: Vec, + pub declarations: Vec, } impl std::fmt::Display for Rule { @@ -124,7 +127,7 @@ impl std::fmt::Display for Rule { } pub struct Stylesheet { - rules: Vec, + pub rules: Vec, } impl std::fmt::Display for Stylesheet { diff --git a/src/dom.rs b/src/dom.rs index d63b07c..24c8e5b 100644 --- a/src/dom.rs +++ b/src/dom.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::{HashMap,HashSet}; use std; use css; @@ -7,17 +7,30 @@ pub struct Attr { attrs: HashMap, } -struct EData { - name: String, - attr: Attr, +pub struct EData { + pub name: String, + pub attr: Attr, } -struct SData { +impl EData { + pub fn id(&self) -> Option<&String> { + self.attr.attrs.get("id") + } + + pub fn classes(&self) -> HashSet<&str> { + match self.attr.attrs.get("class") { + Some(classlist) => classlist.split(' ').collect(), + None => HashSet::new() + } + } +} + +pub struct SData { attr: Attr, content: css::Stylesheet, } -enum NType { +pub enum NType { Text(String), Comment(String), Element(EData), @@ -25,8 +38,8 @@ enum NType { } pub struct Node { - children: Vec, - ntype: NType, + pub children: Vec, + pub ntype: NType, } pub fn text(d: String) -> Node { diff --git a/src/main.rs b/src/main.rs index c439912..b9a0ad5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ use std::fs::File; pub mod css; pub mod dom; pub mod html; +pub mod styling; fn read_source(filename: String) -> String { let mut str = String::new(); diff --git a/src/styling.rs b/src/styling.rs new file mode 100644 index 0000000..7241677 --- /dev/null +++ b/src/styling.rs @@ -0,0 +1,73 @@ +use std::collections::HashMap; + +use css; +use dom; + +type Properties = HashMap; + +pub struct Node<'a> { + node: & 'a dom::Node, + values: Properties, + children: Vec>, +} + +fn matches(elem: &dom::EData, selector: &css::Selector) -> bool { + match *selector { + css::Selector::Simple(ref s) => matches_simple(elem, s) + } +} + +fn matches_simple(elem: &dom::EData, selector: &css::SimpleSelector) -> bool { + if selector.tag_name.iter().any(|name| elem.name != *name) { + return false; + } + + if selector.id.iter().any(|id| elem.id() != Some(id)) { + return false; + } + + let elem_classes = elem.classes(); + if selector.class.iter().any(|class| !elem_classes.contains(&**class)) { + return false; + } + + return true; +} + +type Rule<'a> = (css::Specificity, &'a css::Rule); + +fn match_rule<'a>(elem: &dom::EData, rule: &'a css::Rule) -> Option> { + rule.selectors.iter() + .find(|selector| matches(elem, *selector)) + .map(|selector| (selector.specificity(), rule)) +} + +fn matching_rules<'a>(elem: &dom::EData, stylesheet: &'a css::Stylesheet) -> Vec> { + stylesheet.rules.iter().filter_map(|rule| match_rule(elem, rule)).collect() +} + +fn values(elem: &dom::EData, stylesheet: &css::Stylesheet) -> Properties { + let mut values = HashMap::new(); + let mut rules = matching_rules(elem, stylesheet); + + rules.sort_by(|&(a, _), &(b, _)| a.cmp(&b)); + for (_, rule) in rules { + for decl in &rule.declarations { + values.insert(decl.name.clone(), decl.value.clone()); + } + } + return values; +} + +pub fn style_tree<'a>(root: &'a dom::Node, stylesheet: &'a css::Stylesheet) -> Node<'a> { + Node { + node: root, + values: match root.ntype { + dom::NType::Element(ref elem) => values(elem, stylesheet), + dom::NType::Text(_) => HashMap::new(), + dom::NType::Comment(_) => HashMap::new(), + dom::NType::Stylesheet(_) => HashMap::new(), + }, + children: root.children.iter().map(|child| style_tree(child, stylesheet)).collect(), + } +}