7 个版本
| 0.1.6 | 2022年3月26日 |
|---|---|
| 0.1.5 | 2022年3月26日 |
#79 in #dom
21KB
454 行
Topus
与 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"。
创建方式
- 直接创建
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()
};
- 宏创建
let macro_hidden = attribute!(hidden);
let macro_style = attribute!(style="display: None");
let macro_http_equiv = attribute!(http-equiv="X-UA-Compatible");
推荐使用宏创建 Attribute,方便快捷。
断言
assert_eq!(hidden, macro_hidden);
assert_eq!(style, macro_style);
assert_eq!(http_equiv, macro_http_equiv);
创建 Vec<Attribute>
使用 attributes 宏可以很方便地创建 Vec
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())],
attributes);
细心的应该发现问题了,html 和 style="display:None" 属性是逆向加入 Vec 容器的。
节点
enum Node 有三个变体。
元素 { node_name: String, attributes: Vec<Attribute>, child_nodes: Vec<Node>},代表element node。Text { node_value: String },代表text node。Comment { node_value: String },代表comment node。
创建方式
- 直接创建
let text = Node::Text { node_value: "hello world".to_string() };
let comment = Node::Comment { node_value: "comment".to_string()};
let doctype = Node::Element {
node_name: "!DOCTYPE".to_string(),
attributes: attributes!(html),
child_nodes: Vec::<Node>::with_capacity(0)
};
let a = Node::Element {
node_name: "a".to_string(),
attributes: attributes!(hidden style="display:None"),
child_nodes: Vec::<Node>::with_capacity(0)
};
- 宏创建
let macro_text = text!("hello world");
let macro_comment = comment!("comment");
let macro_doctype = element!(!DOCTYPE html);
let macro_a = element!(a hidden style="display:None");
断言
assert_eq!(text, macro_text);
assert_eq!(comment, macro_comment);
assert_eq!(doctype, macro_doctype);
assert_eq!(a, macro_a);
assert_eq!("<a hidden style=\"display:None\">".to_string(), macro_a.to_string());
细心地又发现了,macro_a.to_string() 中的 hidden 和 style="display: None" 属性顺序是正向的,因为在实现 Display trait 过程中,通过 attributes.iter().rev() 逆转了 attributes 的显示顺序。
创建 Vec<Node>
使用 elements 宏可以很方便地创建 Vec<Node>
let nodes = nodes!(head body);
assert_eq!(
vec![
element!(body),
element!(head),],
attrs);
同样的,head 和 body 节点是逆序的。
使用 expression
在 element! 宏调用中我们也可以传入表达式参数。如
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> 表达式后面要跟着 ;。
生成 html 文件
通过 build! 宏,生成 html 文件。
build!(html => "index.html");