相较javascript
,rust
有更加严谨的语法,编译期能发现bug的优点。既然javascript
能写html
,是不是用rust
写html
体验更好。基于此想法,我写了topus
。
我的想法是
struct DOM
,由上至下有enum Node
和enum Attribute
。Display trait
,通过to_string()
转换成字符串html
文件,写入字符串为了减少学习难度,所有字符串类型为String
,而不是带有生命周期的&str
。
enum Attribute
有两个attribute变种。
Boolean(String)
,代表布尔属性,如hidden。Normal { key: String, value: Sting }
,代表普通属性,如style="display: None"
。rust
let hidden = Attribute::Boolean("hidden".to_string());
let style = Attribute::Normal {
key: "style".to_string(),
value: "display: None".to_string()
};
let http_equiv = Attribute::Normal {
key: "http-equiv".to_string(),
value: "X-UA-Compatible".to_string()
};
rust
let macro_hidden = attribute!(hidden);
let macro_style = attribute!(style="display: None");
let macro_http_equiv = attribute!(http-equiv="X-UA-Compatible");
推荐使用宏创建Attribute
,方便快捷。
rust
assert_eq!(hidden, macro_hidden);
assert_eq!(style, macro_style);
assert_eq!(http_equiv, macro_http_equiv);
使用attributes宏可以很方便的创建Vec
rust
let attributes = attributes!(html style="display:None");
assert_eq!(
vec![
Attribute::Normal{
key: "style".to_string(),
value: "display:None".to_string()
},
Attribute::Boolean("html".to_string())],
attrs);
细心的应该发现问题了,html
和style="display:None"
属性是逆向加入Vec容器的。
enum Node
有三个变种。
Element { node_name: String, attributes: Vec<Attribute>, child_nodes: Vec<Node>}
,代表element node
。Text { node_value: String }
,代表text node
。Comment { node_value: String }
,代表comment node
。``` rust let text = Node::Text { nodevalue: "hello world".tostring() } let comment = Node::Comment { nodevalue: "comment".tostring()}
let doctype = Node::Element {
nodename: "!DOCTYPE".tostring(),
attributes: attributes!(html),
childnodes: Vec::
``` rust let macrotext = text!("hello world"); let macrocomment = comment!("comment");
let macrodoctype = element!(!DOCTYPE html); let macroa = element!(a hidden style="display:None"); ```
``` rust asserteq!(text, macrotext); asserteq!(comment, macrocomment); asserteq!(doctype, macrodoctype);
asserteq!(a, macroa); asserteq!("".tostring(), macroa.tostring()); ```
细心的又发现了,macro_a.to_string()
中的hidden
和style="display: None"
属性顺序是正向了,因为在实现Display trait
过程中,通过attributes.iter().rev()
逆转了attributes
的显示顺序。
使用elements宏可以很方便的创建Vec
rust
let nodes = nodes!(head body);
assert_eq!(
vec![
element!(body),
element!(head),],
attrs);
同样的,head
和body
节点是逆序的。
在element!
宏调用中我们也可以传入表达式参数。如
rust
let html = element!(html charset="UTF-8" =>
element!(head =>
element!(meta charset="UTF-8"),
element!(meta http-equiv="X-UA-Compatible" content="IE=edge"),
element!(meta name="viewport" content="width=device-width, initial-scale=1.0"),
element!(title => text!(title),),
),
element!(body => text!(""),),
);
Attribute
和Node
表达式后面要跟着,
,Vec<Attribute>
和Vec<Node>
表达式后面要跟着;
。
通过build!
宏,生成html文件。
rust
build!(html => "index.html");