1#![allow(non_camel_case_types)]
78#![allow(dead_code)]
79
80use std::io;
81pub use dot_structures::*;
82use crate::cmd::CommandArg;
83use crate::printer::{DotPrinter, PrinterContext};
84
85pub mod attributes;
86pub mod printer;
87pub mod cmd;
88mod parser;
89
90#[macro_use]
91extern crate pest_derive;
92extern crate pest;
93
94pub fn parse(dot: &str) -> Result<Graph, String> {
95 parser::parse(dot)
96}
97
98pub fn print(graph: Graph, ctx: &mut PrinterContext) -> String {
99 graph.print(ctx)
100}
101
102pub fn exec(graph: Graph, ctx:&mut PrinterContext, args: Vec<CommandArg>) -> io::Result<String> {
103 cmd::exec(print(graph, ctx), args)
104}
105
106#[cfg(test)]
107mod tests {
108 use std::fs;
109 use std::path::{Path, PathBuf};
110 use dot_structures::*;
111 use dot_generator::*;
112 use crate::attributes::*;
113 use crate::{exec, parse};
114 use crate::cmd::{CommandArg, Format};
115 use crate::printer::{DotPrinter, PrinterContext};
116
117 #[test]
118 fn parse_test() {
119 let g: Graph = parse(r#"
120 strict digraph t {
121 aa[color=green]
122 subgraph v {
123 aa[shape=square]
124 subgraph vv{a2 -> b2}
125 aaa[color=red]
126 aaa -> bbb
127 }
128 aa -> be -> subgraph v { d -> aaa}
129 aa -> aaa -> v
130 }
131 "#).unwrap();
132
133 assert_eq!(
134 g,
135 graph!(strict di id!("t");
136 node!("aa";attr!("color","green")),
137 subgraph!("v";
138 node!("aa"; attr!("shape","square")),
139 subgraph!("vv"; edge!(node_id!("a2") => node_id!("b2"))),
140 node!("aaa";attr!("color","red")),
141 edge!(node_id!("aaa") => node_id!("bbb"))
142 ),
143 edge!(node_id!("aa") => node_id!("be") => subgraph!("v"; edge!(node_id!("d") => node_id!("aaa")))),
144 edge!(node_id!("aa") => node_id!("aaa") => node_id!("v"))
145 )
146 )
147 }
148
149 #[test]
150 fn print_test() {
151 let mut g = graph!(id!("id"));
152
153 for el in (1..10000).into_iter() {
154 if el % 2 == 0 {
155 g.add_stmt(stmt!(node!(el)))
156 } else {
157 g.add_stmt(stmt!(subgraph!(el)))
158 }
159 }
160
161 assert_eq!(178896, g.print(&mut PrinterContext::default()).len())
162 }
163
164 #[cfg(windows)]
165 const LS: &'static str = "\r\n";
166 #[cfg(not(windows))]
167 const LS: &'static str = "\n";
168
169 #[test]
170 fn exec_test() {
171 let mut g = graph!(id!("id");
172 node!("nod"),
173 subgraph!("sb";
174 edge!(node_id!("a") => subgraph!(;
175 node!("n";
176 NodeAttributes::color(color_name::black), NodeAttributes::shape(shape::egg))
177 ))
178 ),
179 edge!(node_id!("a1") => node_id!(esc "a2"))
180 );
181 let graph_str = "graph id {\n nod\n subgraph sb {\n a -- subgraph {n[color=black,shape=egg]} \n }\n a1 -- \"a2\" \n}";
182
183 let mut ctx = PrinterContext::default();
184 assert_eq!(graph_str, g.print(&mut ctx));
185
186
187 let graph_str_svg = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n<!-- Generated by graphviz version 2.49.3 (20211023.0002)\n -->\n<!-- Title: id Pages: 1 -->\n<svg width=\"208pt\" height=\"116pt\"\n viewBox=\"0.00 0.00 208.00 116.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 112)\">\n<title>id</title>\n<polygon fill=\"white\" stroke=\"transparent\" points=\"-4,4 -4,-112 204,-112 204,4 -4,4\"/>\n<!-- nod -->\n<g id=\"node1\" class=\"node\">\n<title>nod</title>\n<ellipse fill=\"none\" stroke=\"black\" cx=\"27\" cy=\"-90\" rx=\"27\" ry=\"18\"/>\n<text text-anchor=\"middle\" x=\"27\" y=\"-86.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">nod</text>\n</g>\n<!-- a -->\n<g id=\"node2\" class=\"node\">\n<title>a</title>\n<ellipse fill=\"none\" stroke=\"black\" cx=\"99\" cy=\"-90\" rx=\"27\" ry=\"18\"/>\n<text text-anchor=\"middle\" x=\"99\" y=\"-86.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">a</text>\n</g>\n<!-- n -->\n<g id=\"node3\" class=\"node\">\n<title>n</title>\n<polygon fill=\"none\" stroke=\"black\" points=\"101.68,-0.05 103.46,-0.15 105.22,-0.3 106.95,-0.49 108.65,-0.74 110.31,-1.03 111.92,-1.36 113.48,-1.75 114.99,-2.18 116.43,-2.65 117.8,-3.16 119.1,-3.71 120.32,-4.31 121.45,-4.94 122.51,-5.61 123.47,-6.31 124.35,-7.04 125.13,-7.8 125.81,-8.59 126.41,-9.41 126.91,-10.25 127.31,-11.11 127.62,-11.99 127.83,-12.89 127.96,-13.8 127.99,-14.72 127.93,-15.65 127.79,-16.59 127.57,-17.53 127.27,-18.47 126.89,-19.41 126.44,-20.35 125.92,-21.28 125.33,-22.2 124.69,-23.11 123.98,-24.01 123.22,-24.89 122.41,-25.75 121.56,-26.59 120.67,-27.41 119.73,-28.2 118.76,-28.96 117.76,-29.69 116.74,-30.39 115.68,-31.06 114.61,-31.69 113.52,-32.29 112.41,-32.84 111.28,-33.35 110.14,-33.82 108.99,-34.25 107.84,-34.64 106.67,-34.97 105.5,-35.26 104.33,-35.51 103.15,-35.7 101.96,-35.85 100.78,-35.95 99.59,-36 98.41,-36 97.22,-35.95 96.04,-35.85 94.85,-35.7 93.67,-35.51 92.5,-35.26 91.33,-34.97 90.16,-34.64 89.01,-34.25 87.86,-33.82 86.72,-33.35 85.59,-32.84 84.48,-32.29 83.39,-31.69 82.32,-31.06 81.26,-30.39 80.24,-29.69 79.24,-28.96 78.27,-28.2 77.33,-27.41 76.44,-26.59 75.59,-25.75 74.78,-24.89 74.02,-24.01 73.31,-23.11 72.67,-22.2 72.08,-21.28 71.56,-20.35 71.11,-19.41 70.73,-18.47 70.43,-17.53 70.21,-16.59 70.07,-15.65 70.01,-14.72 70.04,-13.8 70.17,-12.89 70.38,-11.99 70.69,-11.11 71.09,-10.25 71.59,-9.41 72.19,-8.59 72.87,-7.8 73.65,-7.04 74.53,-6.31 75.49,-5.61 76.55,-4.94 77.68,-4.31 78.9,-3.71 80.2,-3.16 81.57,-2.65 83.01,-2.18 84.52,-1.75 86.08,-1.36 87.69,-1.03 89.35,-0.74 91.05,-0.49 92.78,-0.3 94.54,-0.15 96.32,-0.05 98.1,0 99.9,0 101.68,-0.05\"/>\n<text text-anchor=\"middle\" x=\"99\" y=\"-14.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">n</text>\n</g>\n<!-- a--n -->\n<g id=\"edge1\" class=\"edge\">\n<title>a--n</title>\n<path fill=\"none\" stroke=\"black\" d=\"M99,-71.7C99,-60.85 99,-46.92 99,-36.1\"/>\n</g>\n<!-- a1 -->\n<g id=\"node4\" class=\"node\">\n<title>a1</title>\n<ellipse fill=\"none\" stroke=\"black\" cx=\"173\" cy=\"-90\" rx=\"27\" ry=\"18\"/>\n<text text-anchor=\"middle\" x=\"173\" y=\"-86.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">a1</text>\n</g>\n<!-- a2 -->\n<g id=\"node5\" class=\"node\">\n<title>a2</title>\n<ellipse fill=\"none\" stroke=\"black\" cx=\"173\" cy=\"-18\" rx=\"27\" ry=\"18\"/>\n<text text-anchor=\"middle\" x=\"173\" y=\"-14.3\" font-family=\"Times New Roman,serif\" font-size=\"14.00\">a2</text>\n</g>\n<!-- a1--a2 -->\n<g id=\"edge2\" class=\"edge\">\n<title>a1--a2</title>\n<path fill=\"none\" stroke=\"black\" d=\"M173,-71.7C173,-60.85 173,-46.92 173,-36.1\"/>\n</g>\n</g>\n</svg>\n".replace("\n",LS);
188 let out = exec(g.clone(), &mut ctx, vec![
189 CommandArg::Format(Format::Svg),
190 ]).unwrap();
191
192 assert_eq!(graph_str_svg, out);
193
194 let p = "1.svg";
195 let out = exec(g.clone(), &mut PrinterContext::default(), vec![
196 CommandArg::Format(Format::Svg),
197 CommandArg::Output(p.to_string()),
198 ]).unwrap();
199
200 assert_eq!("", out);
201
202 let file = fs::read_to_string(p).unwrap();
203 assert_eq!(graph_str_svg, file);
204
205 std::fs::remove_file(p);
206 }
207}