mod builder;
pub mod config;
pub mod doc;
pub mod printer;
pub mod source;
#[cfg(any(feature = "unformat", test))]
pub mod unformat;
pub use config::FormatConfig;
pub use doc::Doc;
pub use source::{build_source_doc, format_source_document};
use eure_tree::Cst;
use builder::FormatBuilder;
use printer::Printer;
#[derive(Debug, Clone)]
pub enum FormatError {
ParseError(String),
}
impl std::fmt::Display for FormatError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FormatError::ParseError(msg) => write!(f, "Parse error: {}", msg),
}
}
}
impl std::error::Error for FormatError {}
#[derive(Debug, Clone)]
pub enum FormatCheckResult {
WellFormatted,
NeedsFormatting {
formatted: String,
},
ParseError(String),
}
impl FormatCheckResult {
pub fn is_well_formatted(&self) -> bool {
matches!(self, FormatCheckResult::WellFormatted)
}
pub fn needs_formatting(&self) -> bool {
matches!(self, FormatCheckResult::NeedsFormatting { .. })
}
pub fn is_parse_error(&self) -> bool {
matches!(self, FormatCheckResult::ParseError(_))
}
}
pub fn format_cst(input: &str, cst: &Cst, config: &FormatConfig) -> String {
let builder = FormatBuilder::new(input, cst, config);
let doc = builder.build(cst);
Printer::new(config.clone()).print(&doc)
}
pub fn build_doc(input: &str, cst: &Cst, config: &FormatConfig) -> Doc {
let builder = FormatBuilder::new(input, cst, config);
builder.build(cst)
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TextEdit {
pub start: usize,
pub end: usize,
pub new_text: String,
}
pub fn compute_edits(input: &str, formatted: &str) -> Vec<TextEdit> {
if input == formatted {
Vec::new()
} else {
vec![TextEdit {
start: 0,
end: input.len(),
new_text: formatted.to_string(),
}]
}
}
#[cfg(test)]
mod tests {
use super::*;
fn parse_and_format(input: &str) -> String {
let cst = eure_parol::parse(input).expect("parse failed");
format_cst(input, &cst, &FormatConfig::default())
}
#[test]
fn test_format_simple() {
let formatted = parse_and_format("a = 1");
assert_eq!(formatted, "a = 1\n");
}
#[test]
fn test_format_preserves_content() {
let input = "name = \"hello\"\nage = 42";
let formatted = parse_and_format(input);
assert!(formatted.contains("name"));
assert!(formatted.contains("\"hello\""));
assert!(formatted.contains("age"));
assert!(formatted.contains("42"));
}
#[test]
fn test_format_array() {
let formatted = parse_and_format("= [1, 2, 3]");
assert_eq!(formatted, "= [1, 2, 3]\n");
}
#[test]
fn test_format_empty_array() {
let formatted = parse_and_format("= []");
assert_eq!(formatted, "= []\n");
}
#[test]
fn test_format_empty_object() {
let formatted = parse_and_format("= {}");
assert_eq!(formatted, "= {}\n");
}
#[test]
fn test_compute_edits_no_change() {
let edits = compute_edits("a = 1\n", "a = 1\n");
assert!(edits.is_empty());
}
#[test]
fn test_compute_edits_with_change() {
let edits = compute_edits("a=1", "a = 1\n");
assert_eq!(edits.len(), 1);
assert_eq!(edits[0].start, 0);
assert_eq!(edits[0].end, 3);
assert_eq!(edits[0].new_text, "a = 1\n");
}
}