1use crate::ast::*;
4
5pub fn format_document(doc: &Document) -> String {
6 let mut out = String::new();
7
8 for dir in &doc.directives {
10 match dir {
11 Directive::Direction(d) => {
12 let val = match d {
13 Direction::Down => "down",
14 Direction::Right => "right",
15 };
16 out.push_str(&format!("@direction {}\n", val));
17 }
18 Directive::Theme(t) => {
19 out.push_str(&format!("@theme {}\n", t));
20 }
21 Directive::Spacing(s) => {
22 let val = match s {
23 Spacing::Compact => "compact",
24 Spacing::Normal => "normal",
25 Spacing::Wide => "wide",
26 };
27 out.push_str(&format!("@spacing {}\n", val));
28 }
29 }
30 }
31 if !doc.directives.is_empty() {
32 out.push('\n');
33 }
34
35 for inc in &doc.includes {
37 out.push_str(&format!("include \"{}\"\n", inc.path));
38 }
39 if !doc.includes.is_empty() {
40 out.push('\n');
41 }
42
43 if !doc.nodes.is_empty() {
45 for node in &doc.nodes {
46 format_node(node, &mut out, 0);
47 }
48 out.push('\n');
49 }
50
51 if !doc.connections.is_empty() {
53 for conn in &doc.connections {
54 format_connection(conn, &mut out, 0);
55 }
56 out.push('\n');
57 }
58
59 for group in &doc.groups {
61 format_group(group, &mut out, 0);
62 out.push('\n');
63 }
64
65 while out.ends_with("\n\n") {
67 out.pop();
68 }
69 if !out.ends_with('\n') {
70 out.push('\n');
71 }
72
73 out
74}
75
76fn format_node(node: &Node, out: &mut String, indent: usize) {
77 let pad = " ".repeat(indent);
78 out.push_str(&pad);
79 out.push_str(node.node_type.as_str());
80 out.push(' ');
81 out.push_str(&node.id);
82
83 if let Some(ref label) = node.label {
84 out.push_str(&format!(" \"{}\"", label));
85 }
86
87 format_tags(&node.tags, out);
88 out.push('\n');
89}
90
91fn format_connection(conn: &Connection, out: &mut String, indent: usize) {
92 let pad = " ".repeat(indent);
93 let arrow = match conn.arrow {
94 ArrowKind::Solid => "->",
95 ArrowKind::Dashed => "-->",
96 ArrowKind::Bidirectional => "<->",
97 ArrowKind::Blocked => "-x",
98 };
99
100 out.push_str(&format!("{}{} {} {}", pad, conn.from, arrow, conn.to));
101
102 if let Some(ref label) = conn.label {
103 out.push_str(&format!(": \"{}\"", label));
104 }
105
106 format_tags(&conn.tags, out);
107 out.push('\n');
108}
109
110fn format_group(group: &Group, out: &mut String, indent: usize) {
111 let pad = " ".repeat(indent);
112 out.push_str(&format!("{}group \"{}\"", pad, group.label));
113 format_tags(&group.tags, out);
114 out.push_str(" {\n");
115
116 for member in &group.members {
117 match member {
118 GroupMember::NodeRef(id) => {
119 out.push_str(&format!("{} {}\n", pad, id));
120 }
121 GroupMember::NodeRefList(ids) => {
122 out.push_str(&format!("{} {}\n", pad, ids.join(", ")));
123 }
124 GroupMember::Node(node) => {
125 format_node(node, out, indent + 1);
126 }
127 GroupMember::Connection(conn) => {
128 format_connection(conn, out, indent + 1);
129 }
130 GroupMember::Group(sub) => {
131 format_group(sub, out, indent + 1);
132 }
133 }
134 }
135
136 out.push_str(&format!("{}}}\n", pad));
137}
138
139fn format_tags(tags: &[String], out: &mut String) {
140 if !tags.is_empty() {
141 out.push_str(&format!(" [{}]", tags.join(", ")));
142 }
143}