1mod builder;
25pub mod config;
26pub mod doc;
27pub mod printer;
28pub mod source;
29
30#[cfg(any(feature = "unformat", test))]
31pub mod unformat;
32
33pub use config::FormatConfig;
34pub use doc::Doc;
35pub use source::{build_source_doc, format_source_document};
36
37use eure_tree::Cst;
38
39use builder::FormatBuilder;
40use printer::Printer;
41
42#[derive(Debug, Clone)]
44pub enum FormatError {
45 ParseError(String),
47}
48
49impl std::fmt::Display for FormatError {
50 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51 match self {
52 FormatError::ParseError(msg) => write!(f, "Parse error: {}", msg),
53 }
54 }
55}
56
57impl std::error::Error for FormatError {}
58
59#[derive(Debug, Clone)]
61pub enum FormatCheckResult {
62 WellFormatted,
64 NeedsFormatting {
66 formatted: String,
68 },
69 ParseError(String),
71}
72
73impl FormatCheckResult {
74 pub fn is_well_formatted(&self) -> bool {
76 matches!(self, FormatCheckResult::WellFormatted)
77 }
78
79 pub fn needs_formatting(&self) -> bool {
81 matches!(self, FormatCheckResult::NeedsFormatting { .. })
82 }
83
84 pub fn is_parse_error(&self) -> bool {
86 matches!(self, FormatCheckResult::ParseError(_))
87 }
88}
89
90pub fn format_cst(input: &str, cst: &Cst, config: &FormatConfig) -> String {
94 let builder = FormatBuilder::new(input, cst, config);
95 let doc = builder.build(cst);
96 Printer::new(config.clone()).print(&doc)
97}
98
99pub fn build_doc(input: &str, cst: &Cst, config: &FormatConfig) -> Doc {
103 let builder = FormatBuilder::new(input, cst, config);
104 builder.build(cst)
105}
106
107#[derive(Debug, Clone, PartialEq, Eq)]
109pub struct TextEdit {
110 pub start: usize,
112 pub end: usize,
114 pub new_text: String,
116}
117
118pub fn compute_edits(input: &str, formatted: &str) -> Vec<TextEdit> {
123 if input == formatted {
126 Vec::new()
127 } else {
128 vec![TextEdit {
129 start: 0,
130 end: input.len(),
131 new_text: formatted.to_string(),
132 }]
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139
140 fn parse_and_format(input: &str) -> String {
141 let cst = eure_parol::parse(input).expect("parse failed");
142 format_cst(input, &cst, &FormatConfig::default())
143 }
144
145 #[test]
146 fn test_format_simple() {
147 let formatted = parse_and_format("a = 1");
148 assert_eq!(formatted, "a = 1\n");
149 }
150
151 #[test]
152 fn test_format_preserves_content() {
153 let input = "name = \"hello\"\nage = 42";
154 let formatted = parse_and_format(input);
155 assert!(formatted.contains("name"));
156 assert!(formatted.contains("\"hello\""));
157 assert!(formatted.contains("age"));
158 assert!(formatted.contains("42"));
159 }
160
161 #[test]
162 fn test_format_array() {
163 let formatted = parse_and_format("= [1, 2, 3]");
164 assert_eq!(formatted, "= [1, 2, 3]\n");
165 }
166
167 #[test]
168 fn test_format_empty_array() {
169 let formatted = parse_and_format("= []");
170 assert_eq!(formatted, "= []\n");
171 }
172
173 #[test]
174 fn test_format_empty_object() {
175 let formatted = parse_and_format("= {}");
176 assert_eq!(formatted, "= {}\n");
177 }
178
179 #[test]
180 fn test_compute_edits_no_change() {
181 let edits = compute_edits("a = 1\n", "a = 1\n");
182 assert!(edits.is_empty());
183 }
184
185 #[test]
186 fn test_compute_edits_with_change() {
187 let edits = compute_edits("a=1", "a = 1\n");
188 assert_eq!(edits.len(), 1);
189 assert_eq!(edits[0].start, 0);
190 assert_eq!(edits[0].end, 3);
191 assert_eq!(edits[0].new_text, "a = 1\n");
192 }
193}