mod comments;
mod doc;
mod rules;
#[cfg(test)]
mod tests;
use std::fmt;
use pine_ast::Program;
use pine_core::{PineVersion, VersionError};
use pine_lexer::{Lexer, LexerError};
use pine_parser::{Parser, ParserError};
const MAX_WIDTH: usize = 100;
#[derive(Debug)]
pub enum FormatError {
Version(VersionError),
Lex(LexerError),
Parse(ParserError),
}
impl fmt::Display for FormatError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FormatError::Version(e) => write!(f, "{e}"),
FormatError::Lex(e) => write!(f, "{e}"),
FormatError::Parse(e) => write!(f, "{e}"),
}
}
}
impl std::error::Error for FormatError {}
pub fn format(source: &str) -> Result<String, FormatError> {
let version = PineVersion::detect(source)
.map_err(FormatError::Version)?
.unwrap_or(PineVersion::LATEST);
let tokens = Lexer::with_version(source, version)
.tokenize()
.map_err(FormatError::Lex)?;
let statements = Parser::new(tokens.clone())
.parse()
.map_err(FormatError::Parse)?;
let program = Program::new(statements);
let comments = comments::Comments::extract(&tokens);
let document = rules::Rules::new(comments).program(&program);
let laid_out = doc::layout(&document, MAX_WIDTH);
if laid_out.is_empty() {
return Ok(laid_out);
}
let mut out: String = laid_out
.lines()
.map(str::trim_end)
.collect::<Vec<_>>()
.join("\n");
out.push('\n');
Ok(out)
}