Skip to main content

arc_lang/
fmt.rs

1/// Arc formatter — pretty-prints .arc files with consistent style.
2
3use crate::ast::*;
4
5pub fn format_document(doc: &Document) -> String {
6    let mut out = String::new();
7
8    // Directives first
9    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    // Includes
36    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    // Nodes
44    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    // Connections
52    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    // Groups
60    for group in &doc.groups {
61        format_group(group, &mut out, 0);
62        out.push('\n');
63    }
64
65    // Trim trailing whitespace
66    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}