pub mod latex;
pub mod simple;
pub mod wolfram;
pub use latex::LaTeXFormatter;
pub use simple::SimpleFormatter;
pub use wolfram::WolframFormatter;
use crate::core::Expression;
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum MathLanguage {
#[default]
LaTeX,
Wolfram,
Simple,
Human,
Json,
Markdown,
}
impl MathLanguage {
pub fn as_str(self) -> &'static str {
match self {
Self::LaTeX => "latex",
Self::Wolfram => "wolfram",
Self::Simple => "human", Self::Human => "human",
Self::Json => "json",
Self::Markdown => "markdown",
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum FormattingError {
RecursionLimitExceeded { depth: usize, limit: usize },
UnsupportedExpression {
expr_type: String,
target_format: MathLanguage,
},
TooManyTerms { count: usize, limit: usize },
InvalidMathConstruct { reason: String },
MemoryLimitExceeded,
SerializationError { message: String },
}
impl fmt::Display for FormattingError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::RecursionLimitExceeded { depth, limit } => {
write!(f, "Recursion limit exceeded: {} > {}", depth, limit)
}
Self::UnsupportedExpression {
expr_type,
target_format,
} => {
write!(
f,
"Unsupported expression type '{}' for format {:?}",
expr_type, target_format
)
}
Self::TooManyTerms { count, limit } => {
write!(f, "Too many terms: {} > {}", count, limit)
}
Self::InvalidMathConstruct { reason } => {
write!(f, "Invalid mathematical construct: {}", reason)
}
Self::MemoryLimitExceeded => {
write!(f, "Memory limit exceeded during formatting")
}
Self::SerializationError { message } => {
write!(f, "Serialization error: {}", message)
}
}
}
}
impl std::error::Error for FormattingError {}
pub trait FormattingContext: Default + Clone {
fn target_format(&self) -> MathLanguage {
MathLanguage::default()
}
}
pub trait ExpressionFormatter<C: FormattingContext> {
fn format(&self, context: &C) -> Result<String, FormattingError>;
}
impl<C: FormattingContext> ExpressionFormatter<C> for Expression {
fn format(&self, context: &C) -> Result<String, FormattingError> {
match context.target_format() {
MathLanguage::Simple | MathLanguage::Human => {
let simple_context = simple::SimpleContext::default();
self.to_simple(&simple_context)
}
MathLanguage::Wolfram => {
let wolfram_context = wolfram::WolframContext::default();
self.to_wolfram(&wolfram_context)
}
MathLanguage::Json => {
serde_json::to_string_pretty(self).map_err(|e| {
FormattingError::SerializationError {
message: e.to_string(),
}
})
}
MathLanguage::Markdown => {
let latex_context = latex::LaTeXContext::default();
let latex_result = self.to_latex(latex_context)?;
Ok(format!("$${}$$", latex_result))
}
_ => {
let latex_context = latex::LaTeXContext::default();
self.to_latex(latex_context)
}
}
}
}
impl Expression {
pub fn format(&self) -> Result<String, FormattingError> {
let latex_context = latex::LaTeXContext::default();
self.to_latex(latex_context)
}
pub fn format_as(&self, language: MathLanguage) -> Result<String, FormattingError> {
match language {
MathLanguage::Simple | MathLanguage::Human => {
let simple_context = simple::SimpleContext::default();
self.to_simple(&simple_context)
}
MathLanguage::Wolfram => {
let wolfram_context = wolfram::WolframContext::default();
self.to_wolfram(&wolfram_context)
}
MathLanguage::Json => serde_json::to_string_pretty(self).map_err(|e| {
FormattingError::SerializationError {
message: e.to_string(),
}
}),
MathLanguage::Markdown => {
let latex_context = latex::LaTeXContext::default();
let latex_result = self.to_latex(latex_context)?;
Ok(format!("$${}$$", latex_result))
}
_ => {
let latex_context = latex::LaTeXContext::default();
self.to_latex(latex_context)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::Expression;
use crate::expr;
#[derive(Debug, Default, Clone)]
struct TestContext;
impl FormattingContext for TestContext {}
#[test]
fn test_format_defaults_to_latex() {
let x_expr = expr!(x);
let result = ExpressionFormatter::format(&x_expr, &TestContext);
assert!(result.is_ok());
}
#[test]
fn test_format_without_context() {
let x_expr = expr!(x);
let result = x_expr.format();
assert!(result.is_ok());
let latex_result = x_expr.format_as(MathLanguage::LaTeX);
assert!(latex_result.is_ok());
let simple_result = x_expr.format_as(MathLanguage::Simple);
assert!(simple_result.is_ok());
}
#[test]
fn test_comprehensive_formatting() {
use crate::core::expression::RelationType;
let interval = Expression::interval(expr!(0), expr!(10), true, false);
let latex_interval = interval.format().unwrap();
assert!(latex_interval.contains("[0"));
assert!(latex_interval.contains("10)"));
let simple_interval = interval.format_as(MathLanguage::Simple).unwrap();
assert!(simple_interval.contains("[0"));
assert!(simple_interval.contains("10)"));
let relation = Expression::relation(expr!(x), expr!(5), RelationType::Greater);
let latex_relation = relation.format().unwrap();
assert!(latex_relation.contains("x"));
assert!(latex_relation.contains("5"));
let simple_relation = relation.format_as(MathLanguage::Simple).unwrap();
assert!(simple_relation.contains("x > 5"));
let piecewise = Expression::piecewise(
vec![(expr!(x), expr!(1)), (expr!(y), expr!(2))],
Some(expr!(0)),
);
let latex_piecewise = piecewise.format().unwrap();
assert!(latex_piecewise.contains("\\begin{cases}"));
let simple_piecewise = piecewise.format_as(MathLanguage::Simple).unwrap();
assert!(simple_piecewise.contains("if"));
assert!(simple_piecewise.contains("otherwise"));
}
}