DOT DSL
1. Readme
DOT DSL
编写类似于 Graphviz 点语言的特定领域语言.
一个Domain Specific Language (DSL)是针对特定域优化的小语言.
比如说DOT 语言允许您编写图形的文本描述,然后通过Graphviz中其中一个图形工具(如dot
)转换为图像,一个简单的图形如下所示:
graph {
graph [bgcolor="yellow"]
a [color="red"]
b [color="blue"]
a -- b [color="green"]
}
把它放在一个example.dot
文件中,并运行dot example.dot -T png -o example.png
,就会创建一个图像example.png
,其中黄色背景上的绿线连接的红色和蓝色圆圈。
创建类似于点语言的 DSL.
生成器模式(Builder pattern)
本练习希望您通过使用builder pattern
模式,构建多个结构。简而言之,此模式允许您将包含大量参数的结构的构造函数,拆分为多个单独的函数。这种方法为您提供了实现紧凑,但高度灵活的结构构造和配置的方法。你可以在该页面上面阅读更多相关信息。
2. 开始你的表演
pub mod graph { pub struct Graph; impl Graph { pub fn new() -> Self { unimplemented!("Construct a new Graph struct."); } } }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { #[macro_use] extern crate maplit; use graph::graph_items::edge::Edge; use graph::graph_items::node::Node; use graph::Graph; #[test] fn test_empty_graph() { let graph = Graph::new(); assert!(graph.nodes.is_empty()); assert!(graph.edges.is_empty()); assert!(graph.attrs.is_empty()); } #[test] //#[ignore] fn test_graph_with_one_node() { let nodes = vec![Node::new("a")]; let graph = Graph::new().with_nodes(&nodes); assert!(graph.edges.is_empty()); assert!(graph.attrs.is_empty()); assert_eq!(graph.nodes, vec![Node::new("a")]); } #[test] //#[ignore] fn test_graph_with_one_node_with_keywords() { let nodes = vec![Node::new("a").with_attrs(&[("color", "green")])]; let graph = Graph::new().with_nodes(&nodes); assert!(graph.edges.is_empty()); assert!(graph.attrs.is_empty()); assert_eq!( graph.nodes, vec![Node::new("a").with_attrs(&[("color", "green")])] ); } #[test] //#[ignore] fn test_graph_with_one_edge() { let edges = vec![Edge::new("a", "b")]; let graph = Graph::new().with_edges(&edges); assert!(graph.nodes.is_empty()); assert!(graph.attrs.is_empty()); assert_eq!(graph.edges, vec![Edge::new("a", "b")]); } #[test] //#[ignore] fn test_graph_with_one_attribute() { let graph = Graph::new().with_attrs(&[("foo", "1")]); let expected_attrs = hashmap!{ "foo".to_string() => "1".to_string(), }; assert!(graph.nodes.is_empty()); assert!(graph.edges.is_empty()); assert_eq!(graph.attrs, expected_attrs); } #[test] //#[ignore] fn test_graph_with_attributes() { let nodes = vec![ Node::new("a").with_attrs(&[("color", "green")]), Node::new("c"), Node::new("b").with_attrs(&[("label", "Beta!")]), ]; let edges = vec![ Edge::new("b", "c"), Edge::new("a", "b").with_attrs(&[("color", "blue")]), ]; let attrs = vec![("foo", "1"), ("title", "Testing Attrs"), ("bar", "true")]; let expected_attrs = hashmap! { "foo".to_string() => "1".to_string(), "title".to_string() => "Testing Attrs".to_string(), "bar".to_string() => "true".to_string(), }; let graph = Graph::new() .with_nodes(&nodes) .with_edges(&edges) .with_attrs(&attrs); assert_eq!( graph.nodes, vec![ Node::new("a").with_attrs(&[("color", "green")]), Node::new("c"), Node::new("b").with_attrs(&[("label", "Beta!")]), ] ); assert_eq!( graph.edges, vec![ Edge::new("b", "c"), Edge::new("a", "b").with_attrs(&[("color", "blue")]), ] ); assert_eq!(graph.attrs, expected_attrs); } #[test] //#[ignore] fn test_graph_stores_attributes() { let attributes = [("foo", "bar"), ("bat", "baz"), ("bim", "bef")]; let graph = Graph::new().with_nodes( &['a', 'b', 'c'] .iter() .enumerate() .map(|(i, n)| Node::new(&n.to_string()).with_attrs(&attributes[i..i + 1])) .collect::<Vec<_>>(), ); assert_eq!( graph .get_node("c") .expect("node must be stored") .get_attr("bim"), Some("bef") ); } #}
4. 答案
# #![allow(unused_variables)] #fn main() { pub mod graph { use std::collections::HashMap; use self::graph_items::edge::Edge; use self::graph_items::node::Node; pub struct Graph { pub nodes: Vec<Node>, pub edges: Vec<Edge>, pub attrs: HashMap<String, String>, } impl Graph { pub fn new() -> Self { Graph { nodes: vec![], edges: vec![], attrs: HashMap::new(), } } pub fn with_nodes(mut self, nodes: &[Node]) -> Self { self.nodes = nodes.clone().to_vec(); self } pub fn with_edges(mut self, edges: &[Edge]) -> Self { self.edges = edges.clone().to_vec(); self } pub fn with_attrs(mut self, attrs: &[(&'static str, &'static str)]) -> Self { attrs.iter().for_each(|&(name, value)| { self.attrs.insert(name.to_string(), value.to_string()); }); self } pub fn get_node(&self, name: &str) -> Option<&Node> { self.nodes.iter().filter(|n| n.name == name).nth(0) } } pub mod graph_items { pub mod edge { use std::collections::HashMap; #[derive(Clone, PartialEq, Debug)] pub struct Edge { src: String, dst: String, attrs: HashMap<String, String>, } impl Edge { pub fn new(src: &str, dst: &str) -> Self { Edge { src: src.to_string(), dst: dst.to_string(), attrs: HashMap::new(), } } pub fn with_attrs(mut self, attrs: &[(&'static str, &'static str)]) -> Self { attrs.iter().for_each(|&(name, value)| { self.attrs.insert(name.to_string(), value.to_string()); }); self } } } pub mod node { use std::collections::HashMap; #[derive(Clone, PartialEq, Debug)] pub struct Node { pub name: String, attrs: HashMap<String, String>, } impl Node { pub fn new(name: &str) -> Self { Node { name: name.to_string(), attrs: HashMap::new(), } } pub fn with_attrs(mut self, attrs: &[(&'static str, &'static str)]) -> Self { attrs.iter().for_each(|&(name, value)| { self.attrs.insert(name.to_string(), value.to_string()); }); self } pub fn get_attr(&self, name: &str) -> Option<&str> { self.attrs.get(name).map(|v| v.as_ref()) } } } } } #}