#![allow(clippy::approx_constant)]
use crate::frontend::ast::{Expr, ExprKind};
use crate::quality::formatter_config::FormatterConfig;
use anyhow::Result;
pub struct Formatter {
config: FormatterConfig,
source: Option<String>,
}
impl Formatter {
pub fn new() -> Self {
Self::with_config(FormatterConfig::default())
}
pub fn with_config(config: FormatterConfig) -> Self {
Self {
config,
source: None,
}
}
pub fn set_source(&mut self, source: impl Into<String>) {
self.source = Some(source.into());
}
pub fn format(&self, ast: &Expr) -> Result<String> {
if self.should_ignore(ast) {
if let Some(original) = self.get_original_text(ast) {
return Ok(original);
}
}
if let ExprKind::Block(exprs) = &ast.kind {
let mut result = String::new();
for (i, expr) in exprs.iter().enumerate() {
if i > 0 {
result.push('\n');
}
result.push_str(&self.format_expr(expr, 0));
}
Ok(result)
} else {
Ok(self.format_expr(ast, 0))
}
}
fn format_type(ty_kind: &crate::frontend::ast::TypeKind) -> String {
use crate::frontend::ast::TypeKind;
match ty_kind {
TypeKind::Named(name) => name.clone(),
TypeKind::Generic { base, params } => {
let params_str = params
.iter()
.map(|t| Self::format_type(&t.kind))
.collect::<Vec<_>>()
.join(", ");
format!("{base}<{params_str}>")
}
TypeKind::Function { params, ret } => {
let params_str = params
.iter()
.map(|t| Self::format_type(&t.kind))
.collect::<Vec<_>>()
.join(", ");
format!("({}) -> {}", params_str, Self::format_type(&ret.kind))
}
TypeKind::Tuple(types) => {
format!(
"({})",
types
.iter()
.map(|t| Self::format_type(&t.kind))
.collect::<Vec<_>>()
.join(", ")
)
}
TypeKind::Array { elem_type, size } => {
format!("[{}; {}]", Self::format_type(&elem_type.kind), size)
}
_ => format!("{ty_kind:?}"),
}
}
fn should_ignore(&self, expr: &Expr) -> bool {
expr.leading_comments.iter().any(|comment| {
use crate::frontend::ast::CommentKind;
match &comment.kind {
CommentKind::Line(text) => {
let trimmed = text.trim();
trimmed == "ruchy-fmt-ignore" || trimmed == "ruchy-fmt-ignore-next"
}
_ => false,
}
})
}
fn get_original_text(&self, expr: &Expr) -> Option<String> {
self.source.as_ref().map(|src| {
let start = if expr.leading_comments.is_empty() {
expr.span.start
} else {
expr.leading_comments[0].span.start
};
let mut end = Self::find_rightmost_span_end(expr);
let bytes = src.as_bytes();
let mut brace_depth = 0;
let mut in_expression = false;
let mut scan_pos = start;
while scan_pos < bytes.len() {
while scan_pos < bytes.len()
&& (bytes[scan_pos] == b' ' || bytes[scan_pos] == b'\t')
{
scan_pos += 1;
}
if scan_pos + 1 < bytes.len()
&& bytes[scan_pos] == b'/'
&& bytes[scan_pos + 1] == b'/'
{
while scan_pos < bytes.len() && bytes[scan_pos] != b'\n' {
scan_pos += 1;
}
if scan_pos < bytes.len() {
scan_pos += 1; }
} else {
break;
}
}
while scan_pos < bytes.len()
&& (bytes[scan_pos] == b' ' || bytes[scan_pos] == b'\t' || bytes[scan_pos] == b'\n')
{
scan_pos += 1;
}
if scan_pos < bytes.len() && bytes[scan_pos] == b'{' {
brace_depth = 1;
in_expression = true;
scan_pos += 1;
}
if in_expression {
while scan_pos < bytes.len() && brace_depth > 0 {
if bytes[scan_pos] == b'{' {
brace_depth += 1;
} else if bytes[scan_pos] == b'}' {
brace_depth -= 1;
if brace_depth == 0 {
end = scan_pos + 1;
break;
}
}
scan_pos += 1;
}
} else {
while end < bytes.len() {
if bytes[end] == b'\n' {
break;
}
end += 1;
}
}
let start = start.min(src.len());
let end = end.min(src.len());
src[start..end].to_string()
})
}
fn find_rightmost_span_end(expr: &Expr) -> usize {
use ExprKind::{Binary, Block, Function, Let};
let mut max_end = expr.span.end;
match &expr.kind {
Let { value, body, .. } => {
max_end = max_end.max(Self::find_rightmost_span_end(value));
max_end = max_end.max(Self::find_rightmost_span_end(body));
}
Binary { left, right, .. } => {
max_end = max_end.max(Self::find_rightmost_span_end(left));
max_end = max_end.max(Self::find_rightmost_span_end(right));
}
Function { body, .. } => {
max_end = max_end.max(Self::find_rightmost_span_end(body));
}
Block(exprs) => {
if let Some(last) = exprs.last() {
max_end = max_end.max(Self::find_rightmost_span_end(last));
}
}
_ => {
}
}
max_end
}
fn format_expr(&self, expr: &Expr, indent: usize) -> String {
if self.should_ignore(expr) {
if let Some(original) = self.get_original_text(expr) {
return original;
}
}
let indent_str = if self.config.use_tabs {
"\t".repeat(indent)
} else {
" ".repeat(indent * self.config.indent_width)
};
let mut result = String::new();
for comment in &expr.leading_comments {
result.push_str(&self.format_comment(comment, indent));
result.push('\n');
}
let expr_str = match &expr.kind {
ExprKind::Literal(lit) => match lit {
crate::frontend::ast::Literal::Integer(n, _) => n.to_string(),
crate::frontend::ast::Literal::Float(f) => f.to_string(),
crate::frontend::ast::Literal::String(s) => {
format!("\"{}\"", s.replace('"', "\\\""))
}
crate::frontend::ast::Literal::Bool(b) => b.to_string(),
crate::frontend::ast::Literal::Char(c) => format!("'{c}'"),
crate::frontend::ast::Literal::Byte(b) => format!("b'{}'", *b as char),
crate::frontend::ast::Literal::Unit => "()".to_string(),
crate::frontend::ast::Literal::Null => "null".to_string(),
crate::frontend::ast::Literal::Atom(s) => format!(":{s}"),
},
ExprKind::Identifier(name) => name.clone(),
ExprKind::Let {
name, value, body, ..
} => {
let is_sequential_statement = matches!(
body.kind,
ExprKind::Literal(crate::frontend::ast::Literal::Unit)
| ExprKind::Block(_)
| ExprKind::Call { .. }
| ExprKind::MethodCall { .. }
| ExprKind::Let { .. } );
if is_sequential_statement {
let mut result = format!("let {} = {}", name, self.format_expr(value, indent));
if let ExprKind::Block(body_exprs) = &body.kind {
let indent_str = if self.config.use_tabs {
"\t".repeat(indent)
} else {
" ".repeat(indent * self.config.indent_width)
};
for expr in body_exprs {
result.push('\n');
result.push_str(&indent_str);
result.push_str(&self.format_expr(expr, indent));
}
} else if !matches!(
body.kind,
ExprKind::Literal(crate::frontend::ast::Literal::Unit)
) {
let indent_str = if self.config.use_tabs {
"\t".repeat(indent)
} else {
" ".repeat(indent * self.config.indent_width)
};
result.push('\n');
result.push_str(&indent_str);
result.push_str(&self.format_expr(body, indent));
}
result
} else {
format!(
"let {} = {} in {}",
name,
self.format_expr(value, indent),
self.format_expr(body, indent)
)
}
}
ExprKind::Binary { left, op, right } => {
format!(
"{} {} {}",
self.format_expr(left, indent),
op, self.format_expr(right, indent)
)
}
ExprKind::Block(exprs) => {
let mut result = String::from("{\n");
let inner_indent_str = if self.config.use_tabs {
"\t".repeat(indent + 1)
} else {
" ".repeat((indent + 1) * self.config.indent_width)
};
for expr in exprs {
result.push_str(&format!(
"{}{}\n",
inner_indent_str,
self.format_expr(expr, indent + 1)
));
}
result.push_str(&format!("{indent_str}}}"));
result
}
ExprKind::Function {
name,
params,
return_type,
body,
..
} => {
let mut result = format!("fun {name}");
result.push('(');
for (i, param) in params.iter().enumerate() {
if i > 0 {
result.push_str(", ");
}
if let crate::frontend::ast::Pattern::Identifier(param_name) = ¶m.pattern {
result.push_str(param_name);
if let crate::frontend::ast::TypeKind::Named(type_name) = ¶m.ty.kind {
if type_name != "Any" {
result.push_str(": ");
result.push_str(type_name);
}
} else {
result.push_str(": ");
result.push_str(&Self::format_type(¶m.ty.kind));
}
}
}
result.push(')');
if let Some(ret_ty) = return_type {
result.push_str(" -> ");
result.push_str(&Self::format_type(&ret_ty.kind));
}
result.push(' ');
result.push_str(&self.format_expr(body.as_ref(), indent));
result
}
ExprKind::If {
condition,
then_branch,
else_branch,
} => {
let mut result = "if ".to_string();
result.push_str(&self.format_expr(condition, indent));
result.push(' ');
result.push_str(&self.format_expr(then_branch, indent));
if let Some(else_expr) = else_branch {
result.push_str(" else ");
result.push_str(&self.format_expr(else_expr, indent));
}
result
}
ExprKind::Call { func, args } => {
let mut result = self.format_expr(func, indent);
result.push('(');
for (i, arg) in args.iter().enumerate() {
if i > 0 {
result.push_str(", ");
}
result.push_str(&self.format_expr(arg, indent));
}
result.push(')');
result
}
ExprKind::MethodCall {
receiver,
method,
args,
..
} => {
let mut result = self.format_expr(receiver, indent);
result.push('.');
result.push_str(method);
result.push('(');
for (i, arg) in args.iter().enumerate() {
if i > 0 {
result.push_str(", ");
}
result.push_str(&self.format_expr(arg, indent));
}
result.push(')');
result
}
ExprKind::For {
var,
pattern,
iter,
body,
..
} => {
let mut result = "for ".to_string();
if let Some(pat) = pattern {
if let crate::frontend::ast::Pattern::Identifier(name) = pat {
result.push_str(name);
} else {
result.push_str(&format!("{pat:?}"));
}
} else {
result.push_str(var);
}
result.push_str(" in ");
result.push_str(&self.format_expr(iter, indent));
result.push(' ');
result.push_str(&self.format_expr(body, indent));
result
}
ExprKind::IndexAccess { object, index } => {
format!(
"{}[{}]",
self.format_expr(object, indent),
self.format_expr(index, indent)
)
}
ExprKind::Assign { target, value } => {
format!(
"{} = {}",
self.format_expr(target, indent),
self.format_expr(value, indent)
)
}
ExprKind::Return { value } => {
if let Some(val) = value {
format!("return {}", self.format_expr(val, indent))
} else {
"return".to_string()
}
}
ExprKind::FieldAccess { object, field } => {
format!("{}.{}", self.format_expr(object, indent), field)
}
ExprKind::While {
condition, body, ..
} => {
format!(
"while {} {}",
self.format_expr(condition, indent),
self.format_expr(body, indent)
)
}
ExprKind::Break { value, .. } => {
if let Some(val) = value {
format!("break {}", self.format_expr(val, indent))
} else {
"break".to_string()
}
}
ExprKind::Continue { .. } => "continue".to_string(),
ExprKind::Range {
start,
end,
inclusive,
} => {
let op = if *inclusive { "..=" } else { ".." };
format!(
"{}{}{}",
self.format_expr(start, indent),
op,
self.format_expr(end, indent)
)
}
ExprKind::Unary { op, operand } => {
format!("{}{}", op, self.format_expr(operand, indent))
}
ExprKind::List(items) => {
let formatted_items: Vec<String> = items
.iter()
.map(|item| self.format_expr(item, indent))
.collect();
format!("[{}]", formatted_items.join(", "))
}
ExprKind::Tuple(items) => {
let formatted_items: Vec<String> = items
.iter()
.map(|item| self.format_expr(item, indent))
.collect();
format!("({})", formatted_items.join(", "))
}
ExprKind::Match { expr, arms } => {
let mut result = format!("match {} {{\n", self.format_expr(expr, indent));
for arm in arms {
let pattern_str = format!("{:?}", arm.pattern); result.push_str(&format!(
"{} {} => {},\n",
" ".repeat(indent * self.config.indent_width),
pattern_str,
self.format_expr(&arm.body, indent + 1)
));
}
result.push_str(&format!(
"{}}}",
" ".repeat(indent * self.config.indent_width)
));
result
}
ExprKind::CompoundAssign { target, op, value } => {
format!(
"{} {}= {}",
self.format_expr(target, indent),
op,
self.format_expr(value, indent)
)
}
ExprKind::Lambda { params, body } => {
let params_str = params
.iter()
.map(|p| self.format_pattern(&p.pattern))
.collect::<Vec<_>>()
.join(", ");
format!("|{}| {}", params_str, self.format_expr(body, indent))
}
ExprKind::ObjectLiteral { fields } => {
if fields.is_empty() {
"{}".to_string()
} else {
let fields_str = fields
.iter()
.map(|f| match f {
crate::frontend::ast::ObjectField::KeyValue { key, value } => {
format!("{}: {}", key, self.format_expr(value, indent))
}
crate::frontend::ast::ObjectField::Spread { expr } => {
format!("...{}", self.format_expr(expr, indent))
}
})
.collect::<Vec<_>>()
.join(", ");
format!("{{ {fields_str} }}")
}
}
ExprKind::StructLiteral { name, fields, base } => {
let fields_str = fields
.iter()
.map(|(key, val)| format!("{}: {}", key, self.format_expr(val, indent)))
.collect::<Vec<_>>()
.join(", ");
if let Some(base_expr) = base {
format!(
"{} {{ {}, ..{} }}",
name,
fields_str,
self.format_expr(base_expr, indent)
)
} else {
format!("{name} {{ {fields_str} }}")
}
}
ExprKind::Ternary {
condition,
true_expr,
false_expr,
} => {
format!(
"{} ? {} : {}",
self.format_expr(condition, indent),
self.format_expr(true_expr, indent),
self.format_expr(false_expr, indent)
)
}
ExprKind::Throw { expr } => {
format!("throw {}", self.format_expr(expr, indent))
}
ExprKind::TryCatch {
try_block,
catch_clauses,
finally_block,
} => {
let mut result = format!("try {}", self.format_expr(try_block, indent));
for catch_clause in catch_clauses {
result.push_str(&format!(
" catch ({}) {}",
self.format_pattern(&catch_clause.pattern),
self.format_expr(&catch_clause.body, indent)
));
}
if let Some(finally) = finally_block {
result.push_str(&format!(" finally {}", self.format_expr(finally, indent)));
}
result
}
ExprKind::Await { expr } => {
format!("await {}", self.format_expr(expr, indent))
}
ExprKind::AsyncBlock { body } => {
format!("async {}", self.format_expr(body, indent))
}
ExprKind::TypeCast { expr, target_type } => {
format!("{} as {}", self.format_expr(expr, indent), target_type)
}
ExprKind::ArrayInit { value, size } => {
format!(
"[{}; {}]",
self.format_expr(value, indent),
self.format_expr(size, indent)
)
}
ExprKind::Ok { value } => {
format!("Ok({})", self.format_expr(value, indent))
}
ExprKind::Err { error } => {
format!("Err({})", self.format_expr(error, indent))
}
ExprKind::Some { value } => {
format!("Some({})", self.format_expr(value, indent))
}
ExprKind::None => "None".to_string(),
ExprKind::Try { expr } => {
format!("{}?", self.format_expr(expr, indent))
}
ExprKind::Spawn { actor } => {
format!("spawn {}", self.format_expr(actor, indent))
}
ExprKind::AsyncLambda { params, body } => {
let params_str = params.join(", ");
format!("async |{}| {}", params_str, self.format_expr(body, indent))
}
ExprKind::IfLet {
pattern,
expr,
then_branch,
else_branch,
} => {
let mut result = format!(
"if let {} = {} {}",
self.format_pattern(pattern),
self.format_expr(expr, indent),
self.format_expr(then_branch, indent)
);
if let Some(else_expr) = else_branch {
result.push_str(&format!(" else {}", self.format_expr(else_expr, indent)));
}
result
}
ExprKind::OptionalFieldAccess { object, field } => {
format!("{}?.{}", self.format_expr(object, indent), field)
}
ExprKind::Slice { object, start, end } => {
let start_str = start
.as_ref()
.map_or(String::new(), |e| self.format_expr(e, indent));
let end_str = end
.as_ref()
.map_or(String::new(), |e| self.format_expr(e, indent));
format!(
"{}[{}..{}]",
self.format_expr(object, indent),
start_str,
end_str
)
}
ExprKind::Struct {
name,
type_params,
fields,
is_pub,
..
} => {
let pub_str = if *is_pub { "pub " } else { "" };
let type_params_str = if type_params.is_empty() {
String::new()
} else {
format!("<{}>", type_params.join(", "))
};
let fields_str = fields
.iter()
.map(|f| format!("{}: {}", f.name, Self::format_type(&f.ty.kind)))
.collect::<Vec<_>>()
.join(", ");
format!("{pub_str}struct {name}{type_params_str} {{ {fields_str} }}")
}
ExprKind::TupleStruct {
name,
type_params,
fields,
is_pub,
..
} => {
let pub_str = if *is_pub { "pub " } else { "" };
let type_params_str = if type_params.is_empty() {
String::new()
} else {
format!("<{}>", type_params.join(", "))
};
let fields_str = fields
.iter()
.map(|ty| Self::format_type(&ty.kind))
.collect::<Vec<_>>()
.join(", ");
format!("{pub_str}struct {name}{type_params_str}({fields_str})")
}
ExprKind::Enum {
name,
type_params,
variants,
is_pub,
} => {
let pub_str = if *is_pub { "pub " } else { "" };
let type_params_str = if type_params.is_empty() {
String::new()
} else {
format!("<{}>", type_params.join(", "))
};
let variants_str = variants
.iter()
.map(|v| self.format_enum_variant(v))
.collect::<Vec<_>>()
.join(", ");
format!("{pub_str}enum {name}{type_params_str} {{ {variants_str} }}")
}
ExprKind::Trait {
name,
type_params,
methods,
is_pub,
..
} => {
let pub_str = if *is_pub { "pub " } else { "" };
let type_params_str = if type_params.is_empty() {
String::new()
} else {
format!("<{}>", type_params.join(", "))
};
let methods_str = methods
.iter()
.map(|m| self.format_trait_method(m))
.collect::<Vec<_>>()
.join(" ");
format!("{pub_str}trait {name}{type_params_str} {{ {methods_str} }}")
}
ExprKind::Impl {
type_params,
trait_name,
for_type,
methods,
..
} => {
let type_params_str = if type_params.is_empty() {
String::new()
} else {
format!("<{}>", type_params.join(", "))
};
let trait_part = trait_name
.as_ref()
.map_or(String::new(), |t| format!("{t} for "));
let methods_str = methods
.iter()
.map(|m| self.format_impl_method(m))
.collect::<Vec<_>>()
.join(" ");
format!("impl{type_params_str} {trait_part}{for_type} {{ {methods_str} }}")
}
ExprKind::Class {
name,
type_params,
fields,
..
} => {
let type_params_str = if type_params.is_empty() {
String::new()
} else {
format!("<{}>", type_params.join(", "))
};
let fields_str = fields
.iter()
.map(|f| format!("{}: {}", f.name, Self::format_type(&f.ty.kind)))
.collect::<Vec<_>>()
.join(", ");
format!("class {name}{type_params_str} {{ {fields_str} }}")
}
ExprKind::Module { name, body } => {
format!("mod {} {}", name, self.format_expr(body, indent))
}
ExprKind::ModuleDeclaration { name } => {
format!("mod {name};")
}
ExprKind::Import { module, items } => {
if let Some(item_list) = items {
format!("import {}::{{{}}}", module, item_list.join(", "))
} else {
format!("import {module}")
}
}
ExprKind::Export { expr, is_default } => {
if *is_default {
format!("export default {}", self.format_expr(expr, indent))
} else {
format!("export {}", self.format_expr(expr, indent))
}
}
ExprKind::LetPattern {
pattern,
value,
body,
..
} => {
format!(
"let {} = {} in {}",
self.format_pattern(pattern),
self.format_expr(value, indent),
self.format_expr(body, indent)
)
}
ExprKind::WhileLet {
pattern,
expr,
body,
..
} => {
format!(
"while let {} = {} {}",
self.format_pattern(pattern),
self.format_expr(expr, indent),
self.format_expr(body, indent)
)
}
ExprKind::StringInterpolation { parts } => {
let parts_str = parts
.iter()
.map(|part| match part {
crate::frontend::ast::StringPart::Text(s) => s.clone(),
crate::frontend::ast::StringPart::Expr(e) => {
format!("{{{}}}", self.format_expr(e, indent))
}
crate::frontend::ast::StringPart::ExprWithFormat { expr, format_spec } => {
format!("{{{}:{}}}", self.format_expr(expr, indent), format_spec)
}
})
.collect::<String>();
format!("f\"{parts_str}\"")
}
ExprKind::Actor {
name,
state,
handlers,
} => {
let state_str = state
.iter()
.map(|f| format!("{}: {}", f.name, Self::format_type(&f.ty.kind)))
.collect::<Vec<_>>()
.join(", ");
let handlers_str = handlers
.iter()
.map(|h| format!("handle {}", h.message_type))
.collect::<Vec<_>>()
.join(" ");
format!("actor {name} {{ {state_str} {handlers_str} }}")
}
ExprKind::Effect { name, operations } => {
let ops_str = operations
.iter()
.map(|op| {
let params_str = op
.params
.iter()
.map(|p| format!("{:?}: {:?}", p.pattern, p.ty))
.collect::<Vec<_>>()
.join(", ");
let ret_str = op
.return_type
.as_ref()
.map(|t| format!(" -> {:?}", t.kind))
.unwrap_or_default();
format!("{}({}){}", op.name, params_str, ret_str)
})
.collect::<Vec<_>>()
.join(", ");
format!("effect {name} {{ {ops_str} }}")
}
ExprKind::Handle { expr, handlers } => {
let expr_str = self.format_expr(expr, indent);
let handlers_str = handlers
.iter()
.map(|h| {
let params_str = if h.params.is_empty() {
String::new()
} else {
format!(
"({})",
h.params
.iter()
.map(|p| format!("{p:?}"))
.collect::<Vec<_>>()
.join(", ")
)
};
let body_str = self.format_expr(&h.body, indent);
format!("{}{} => {}", h.operation, params_str, body_str)
})
.collect::<Vec<_>>()
.join(", ");
format!("handle {expr_str} with {{ {handlers_str} }}")
}
ExprKind::Send { actor, message } => {
format!(
"send({}, {})",
self.format_expr(actor, indent),
self.format_expr(message, indent)
)
}
ExprKind::Loop { body, .. } => {
format!(
"loop {{\n{}\n{}}}",
self.format_expr(body, indent + 1),
" ".repeat(indent * self.config.indent_width)
)
}
ExprKind::Pipeline { expr, stages } => {
let mut result = self.format_expr(expr, indent);
for stage in stages {
result.push_str(" |> ");
result.push_str(&self.format_expr(&stage.op, indent));
}
result
}
ExprKind::PreIncrement { target } => {
format!("++{}", self.format_expr(target, indent))
}
ExprKind::PostIncrement { target } => {
format!("{}++", self.format_expr(target, indent))
}
ExprKind::PreDecrement { target } => {
format!("--{}", self.format_expr(target, indent))
}
ExprKind::PostDecrement { target } => {
format!("{}--", self.format_expr(target, indent))
}
ExprKind::ActorSend { actor, message } => {
format!(
"{} <- {}",
self.format_expr(actor, indent),
self.format_expr(message, indent)
)
}
ExprKind::ActorQuery { actor, message } => {
format!(
"{} <? {}",
self.format_expr(actor, indent),
self.format_expr(message, indent)
)
}
ExprKind::Ask { actor, message, .. } => {
format!(
"ask {} {}",
self.format_expr(actor, indent),
self.format_expr(message, indent)
)
}
ExprKind::ListComprehension { element, clauses } => {
let clauses_str = clauses
.iter()
.map(|clause| {
let cond = clause
.condition
.as_ref()
.map(|c| format!(" if {}", self.format_expr(c, indent)))
.unwrap_or_default();
format!(
"{} in {}{}",
clause.variable,
self.format_expr(&clause.iterable, indent),
cond
)
})
.collect::<Vec<_>>()
.join(", ");
format!(
"[{} for {}]",
self.format_expr(element, indent),
clauses_str
)
}
ExprKind::DictComprehension {
key,
value,
clauses,
} => {
let clauses_str = clauses
.iter()
.map(|clause| {
let cond = clause
.condition
.as_ref()
.map(|c| format!(" if {}", self.format_expr(c, indent)))
.unwrap_or_default();
format!(
"{} in {}{}",
clause.variable,
self.format_expr(&clause.iterable, indent),
cond
)
})
.collect::<Vec<_>>()
.join(", ");
format!(
"{{{}: {} for {}}}",
self.format_expr(key, indent),
self.format_expr(value, indent),
clauses_str
)
}
ExprKind::SetComprehension { element, clauses } => {
let clauses_str = clauses
.iter()
.map(|clause| {
let cond = clause
.condition
.as_ref()
.map(|c| format!(" if {}", self.format_expr(c, indent)))
.unwrap_or_default();
format!(
"{} in {}{}",
clause.variable,
self.format_expr(&clause.iterable, indent),
cond
)
})
.collect::<Vec<_>>()
.join(", ");
format!(
"{{{} for {}}}",
self.format_expr(element, indent),
clauses_str
)
}
ExprKind::ImportAll { module, .. } => {
format!("import {module}::*")
}
ExprKind::ImportDefault { module, name } => {
format!("import default {name} from {module}")
}
ExprKind::ExportList { names } => {
format!("export {{ {} }}", names.join(", "))
}
ExprKind::ExportDefault { expr } => {
format!("export default {}", self.format_expr(expr, indent))
}
ExprKind::Command { program, args, .. } => {
let full_cmd = if args.is_empty() {
program.clone()
} else {
format!("{} {}", program, args.join(" "))
};
format!("`{full_cmd}`")
}
ExprKind::QualifiedName { module, name } => {
format!("{module}::{name}")
}
ExprKind::TypeAlias { name, target_type } => {
format!("type {} = {}", name, Self::format_type(&target_type.kind))
}
ExprKind::Spread { expr } => {
format!("...{}", self.format_expr(expr, indent))
}
ExprKind::OptionalMethodCall {
receiver,
method,
args,
} => {
let args_str = args
.iter()
.map(|arg| self.format_expr(arg, indent))
.collect::<Vec<_>>()
.join(", ");
format!(
"{}?.{}({})",
self.format_expr(receiver, indent),
method,
args_str
)
}
ExprKind::Extension {
target_type,
methods,
} => {
let indent_str = " ".repeat(indent * self.config.indent_width);
let methods_str = methods
.iter()
.map(|method| {
let params_str = method
.params
.iter()
.map(|p| self.format_pattern(&p.pattern))
.collect::<Vec<_>>()
.join(", ");
format!(
"{} fun {}({}) {{ }}",
indent_str, method.name, params_str
)
})
.collect::<Vec<_>>()
.join("\n");
format!("extension {target_type} {{\n{methods_str}\n{indent_str}}}")
}
ExprKind::ReExport { items, module } => {
format!("export {{ {} }} from {}", items.join(", "), module)
}
ExprKind::Macro { name, args } => {
let args_str = args
.iter()
.map(|arg| self.format_expr(arg, indent))
.collect::<Vec<_>>()
.join(", ");
format!("macro {name}({args_str}) {{ }}")
}
ExprKind::MacroInvocation { name, args } => {
let args_str = args
.iter()
.map(|arg| self.format_expr(arg, indent))
.collect::<Vec<_>>()
.join(", ");
format!("{name}!({args_str})")
}
ExprKind::VecRepeat { value, count } => {
let value_str = self.format_expr(value, indent);
let count_str = self.format_expr(count, indent);
format!("vec![{value_str}; {count_str}]")
}
ExprKind::DataFrame { columns } => {
let columns_str = columns
.iter()
.map(|col| {
let values_str = col
.values
.iter()
.map(|v| self.format_expr(v, indent))
.collect::<Vec<_>>()
.join(", ");
format!("\"{}\" => [{}]", col.name, values_str)
})
.collect::<Vec<_>>()
.join(", ");
format!("df![{columns_str}]")
}
ExprKind::DataFrameOperation { source, operation } => {
format!("{}.{:?}", self.format_expr(source, indent), operation)
}
ExprKind::Lazy { expr } => {
format!("lazy {}", self.format_expr(expr, indent))
}
ExprKind::Set(_) => {
format!("/* UNIMPLEMENTED: {:?} */", expr.kind)
}
};
result.push_str(&expr_str);
if let Some(trailing) = &expr.trailing_comment {
result.push(' ');
result.push_str(&self.format_comment(trailing, 0)); }
result
}
fn format_comment(&self, comment: &crate::frontend::ast::Comment, indent: usize) -> String {
let indent_str = if self.config.use_tabs {
"\t".repeat(indent)
} else {
" ".repeat(indent * self.config.indent_width)
};
match &comment.kind {
crate::frontend::ast::CommentKind::Line(text) => {
format!("{indent_str}//{text}")
}
crate::frontend::ast::CommentKind::Doc(text) => {
format!("{indent_str}///{text}")
}
crate::frontend::ast::CommentKind::Block(text) => {
format!("{indent_str}/*{text}*/")
}
}
}
fn format_pattern(&self, pattern: &crate::frontend::ast::Pattern) -> String {
use crate::frontend::ast::Pattern;
match pattern {
Pattern::Wildcard => "_".to_string(),
Pattern::Literal(lit) => self.format_literal(lit),
Pattern::Identifier(name) => name.clone(),
Pattern::QualifiedName(parts) => parts.join("::"),
Pattern::Tuple(patterns) => {
let inner = patterns
.iter()
.map(|p| self.format_pattern(p))
.collect::<Vec<_>>()
.join(", ");
format!("({inner})")
}
Pattern::List(patterns) => {
let inner = patterns
.iter()
.map(|p| self.format_pattern(p))
.collect::<Vec<_>>()
.join(", ");
format!("[{inner}]")
}
Pattern::Struct {
name,
fields,
has_rest,
} => {
let fields_str = fields
.iter()
.map(|f| self.format_struct_pattern_field(f))
.collect::<Vec<_>>()
.join(", ");
if *has_rest {
format!("{name} {{ {fields_str}, .. }}")
} else {
format!("{name} {{ {fields_str} }}")
}
}
Pattern::TupleVariant { path, patterns } => {
let path_str = path.join("::");
let patterns_str = patterns
.iter()
.map(|p| self.format_pattern(p))
.collect::<Vec<_>>()
.join(", ");
format!("{path_str}({patterns_str})")
}
Pattern::Range {
start,
end,
inclusive,
} => {
let op = if *inclusive { "..=" } else { ".." };
format!(
"{}{}{}",
self.format_pattern(start),
op,
self.format_pattern(end)
)
}
Pattern::Or(patterns) => patterns
.iter()
.map(|p| self.format_pattern(p))
.collect::<Vec<_>>()
.join(" | "),
Pattern::Rest => "..".to_string(),
Pattern::RestNamed(name) => format!("..{name}"),
Pattern::AtBinding { name, pattern } => {
format!("{} @ {}", name, self.format_pattern(pattern))
}
Pattern::WithDefault { pattern, default } => {
format!(
"{} = {}",
self.format_pattern(pattern),
self.format_expr(default, 0)
)
}
Pattern::Mut(pattern) => {
format!("mut {}", self.format_pattern(pattern))
}
Pattern::Ok(pattern) => {
format!("Ok({})", self.format_pattern(pattern))
}
Pattern::Err(pattern) => {
format!("Err({})", self.format_pattern(pattern))
}
Pattern::Some(pattern) => {
format!("Some({})", self.format_pattern(pattern))
}
Pattern::None => "None".to_string(),
}
}
fn format_struct_pattern_field(
&self,
field: &crate::frontend::ast::StructPatternField,
) -> String {
if let Some(pattern) = &field.pattern {
format!("{}: {}", field.name, self.format_pattern(pattern))
} else {
field.name.clone()
}
}
fn format_literal(&self, literal: &crate::frontend::ast::Literal) -> String {
use crate::frontend::ast::Literal;
match literal {
Literal::Integer(val, suffix) => {
if let Some(suffix) = suffix {
format!("{val}{suffix}")
} else {
val.to_string()
}
}
Literal::Float(val) => val.to_string(),
Literal::String(s) => format!("\"{s}\""),
Literal::Bool(b) => b.to_string(),
Literal::Char(c) => format!("'{c}'"),
Literal::Byte(b) => format!("{b}u8"),
crate::frontend::ast::Literal::Unit => "()".to_string(),
crate::frontend::ast::Literal::Null => "null".to_string(),
crate::frontend::ast::Literal::Atom(s) => format!(":{s}"),
}
}
fn format_enum_variant(&self, variant: &crate::frontend::ast::EnumVariant) -> String {
use crate::frontend::ast::EnumVariantKind;
match &variant.kind {
EnumVariantKind::Unit => variant.name.clone(),
EnumVariantKind::Tuple(types) => {
let types_str = types
.iter()
.map(|t| Self::format_type(&t.kind))
.collect::<Vec<_>>()
.join(", ");
format!("{}({})", variant.name, types_str)
}
EnumVariantKind::Struct(fields) => {
let fields_str = fields
.iter()
.map(|f| format!("{}: {}", f.name, Self::format_type(&f.ty.kind)))
.collect::<Vec<_>>()
.join(", ");
format!("{} {{ {} }}", variant.name, fields_str)
}
}
}
fn format_trait_method(&self, method: &crate::frontend::ast::TraitMethod) -> String {
let params_str = method
.params
.iter()
.map(|p| {
format!(
"{}: {}",
self.format_pattern(&p.pattern),
Self::format_type(&p.ty.kind)
)
})
.collect::<Vec<_>>()
.join(", ");
let return_str = method.return_type.as_ref().map_or(String::new(), |t| {
format!(" -> {}", Self::format_type(&t.kind))
});
format!("fun {}({}){}; ", method.name, params_str, return_str)
}
fn format_impl_method(&self, method: &crate::frontend::ast::ImplMethod) -> String {
let params_str = method
.params
.iter()
.map(|p| {
format!(
"{}: {}",
self.format_pattern(&p.pattern),
Self::format_type(&p.ty.kind)
)
})
.collect::<Vec<_>>()
.join(", ");
let return_str = method.return_type.as_ref().map_or(String::new(), |t| {
format!(" -> {}", Self::format_type(&t.kind))
});
format!(
"fun {}({}){} {}",
method.name,
params_str,
return_str,
self.format_expr(&method.body, 0)
)
}
}
impl Default for Formatter {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::frontend::ast::*;
fn create_simple_literal(value: i64) -> Expr {
Expr::new(
ExprKind::Literal(Literal::Integer(value, None)),
Default::default(),
)
}
fn create_identifier(name: &str) -> Expr {
Expr::new(ExprKind::Identifier(name.to_string()), Default::default())
}
#[test]
fn test_formatter_new() {
let formatter = Formatter::new();
assert_eq!(formatter.config.indent_width, 4);
assert!(!formatter.config.use_tabs);
}
#[test]
fn test_formatter_default() {
let formatter = Formatter::default();
assert_eq!(formatter.config.indent_width, 4);
assert!(!formatter.config.use_tabs);
}
#[test]
fn test_format_integer_literal() {
let formatter = Formatter::new();
let expr = create_simple_literal(42);
let result = formatter
.format(&expr)
.expect("operation should succeed in test");
assert_eq!(result, "42");
}
#[test]
fn test_format_float_literal() {
let formatter = Formatter::new();
let expr = Expr::new(ExprKind::Literal(Literal::Float(3.15)), Default::default());
let result = formatter
.format(&expr)
.expect("operation should succeed in test");
assert_eq!(result, "3.15");
}
#[test]
fn test_format_string_literal() {
let formatter = Formatter::new();
let expr = Expr::new(
ExprKind::Literal(Literal::String("hello".to_string())),
Default::default(),
);
let result = formatter
.format(&expr)
.expect("operation should succeed in test");
assert_eq!(result, "\"hello\"");
}
#[test]
fn test_format_bool_literal() {
let formatter = Formatter::new();
let expr = Expr::new(ExprKind::Literal(Literal::Bool(true)), Default::default());
let result = formatter
.format(&expr)
.expect("operation should succeed in test");
assert_eq!(result, "true");
let expr = Expr::new(ExprKind::Literal(Literal::Bool(false)), Default::default());
let result = formatter
.format(&expr)
.expect("operation should succeed in test");
assert_eq!(result, "false");
}
#[test]
fn test_format_char_literal() {
let formatter = Formatter::new();
let expr = Expr::new(ExprKind::Literal(Literal::Char('a')), Default::default());
let result = formatter
.format(&expr)
.expect("operation should succeed in test");
assert_eq!(result, "'a'");
}
#[test]
fn test_format_unit_literal() {
let formatter = Formatter::new();
let expr = Expr::new(ExprKind::Literal(Literal::Unit), Default::default());
let result = formatter
.format(&expr)
.expect("operation should succeed in test");
assert_eq!(result, "()");
}
#[test]
fn test_format_identifier() {
let formatter = Formatter::new();
let expr = create_identifier("my_var");
let result = formatter
.format(&expr)
.expect("operation should succeed in test");
assert_eq!(result, "my_var");
}
#[test]
fn test_format_binary_expression() {
let formatter = Formatter::new();
let left = create_simple_literal(1);
let right = create_simple_literal(2);
let expr = Expr::new(
ExprKind::Binary {
left: Box::new(left),
op: BinaryOp::Add,
right: Box::new(right),
},
Default::default(),
);
let result = formatter
.format(&expr)
.expect("operation should succeed in test");
assert_eq!(result, "1 + 2"); }
#[test]
fn test_format_let_expression() {
let formatter = Formatter::new();
let value = create_simple_literal(42);
let body = create_identifier("x");
let expr = Expr::new(
ExprKind::Let {
name: "x".to_string(),
value: Box::new(value),
body: Box::new(body),
type_annotation: Some(Type {
kind: TypeKind::Named("Int".to_string()),
span: Default::default(),
}),
is_mutable: false,
else_block: None,
},
Default::default(),
);
let result = formatter
.format(&expr)
.expect("operation should succeed in test");
assert_eq!(result, "let x = 42 in x");
}
#[test]
fn test_format_block_expression() {
let formatter = Formatter::new();
let exprs = vec![create_simple_literal(1), create_simple_literal(2)];
let expr = Expr::new(ExprKind::Block(exprs), Default::default());
let result = formatter
.format(&expr)
.expect("operation should succeed in test");
assert!(!result.is_empty());
}
#[test]
fn test_format_if_expression() {
let formatter = Formatter::new();
let condition = Expr::new(ExprKind::Literal(Literal::Bool(true)), Default::default());
let then_branch = create_simple_literal(1);
let else_branch = create_simple_literal(2);
let expr = Expr::new(
ExprKind::If {
condition: Box::new(condition),
then_branch: Box::new(then_branch),
else_branch: Some(Box::new(else_branch)),
},
Default::default(),
);
let result = formatter
.format(&expr)
.expect("operation should succeed in test");
assert_eq!(result, "if true 1 else 2");
}
#[test]
fn test_format_if_without_else() {
let formatter = Formatter::new();
let condition = Expr::new(ExprKind::Literal(Literal::Bool(true)), Default::default());
let then_branch = create_simple_literal(1);
let expr = Expr::new(
ExprKind::If {
condition: Box::new(condition),
then_branch: Box::new(then_branch),
else_branch: None,
},
Default::default(),
);
let result = formatter
.format(&expr)
.expect("operation should succeed in test");
assert_eq!(result, "if true 1");
}
#[test]
fn test_format_function_simple() {
let formatter = Formatter::new();
let body = create_simple_literal(42);
let expr = Expr::new(
ExprKind::Function {
name: "test".to_string(),
type_params: vec![],
params: vec![],
return_type: None,
body: Box::new(body),
is_async: false,
is_pub: false,
},
Default::default(),
);
let result = formatter
.format(&expr)
.expect("operation should succeed in test");
assert!(!result.is_empty());
}
#[test]
fn test_format_function_with_params() {
let formatter = Formatter::new();
let body = create_identifier("x");
let param = Param {
pattern: Pattern::Identifier("x".to_string()),
ty: Type {
kind: TypeKind::Named("Int".to_string()),
span: Default::default(),
},
span: Default::default(),
is_mutable: false,
default_value: None,
};
let expr = Expr::new(
ExprKind::Function {
name: "identity".to_string(),
type_params: vec![],
params: vec![param],
return_type: Some(Type {
kind: TypeKind::Named("Int".to_string()),
span: Default::default(),
}),
body: Box::new(body),
is_async: false,
is_pub: false,
},
Default::default(),
);
let result = formatter
.format(&expr)
.expect("operation should succeed in test");
assert!(!result.is_empty());
}
#[test]
fn test_format_type_named() {
let _formatter = Formatter::new();
let type_kind = TypeKind::Named("String".to_string());
let result = Formatter::format_type(&type_kind);
assert_eq!(result, "String");
}
#[test]
fn test_format_type_fallback() {
let _formatter = Formatter::new();
let type_kind = TypeKind::List(Box::new(Type {
kind: TypeKind::Named("Int".to_string()),
span: Default::default(),
}));
let result = Formatter::format_type(&type_kind);
assert!(result.contains("List"));
}
#[test]
fn test_format_with_tabs() {
let mut formatter = Formatter::new();
formatter.config.use_tabs = true;
let exprs = vec![create_simple_literal(1)];
let expr = Expr::new(ExprKind::Block(exprs), Default::default());
let result = formatter
.format(&expr)
.expect("operation should succeed in test");
assert!(!result.is_empty());
}
#[test]
fn test_format_with_spaces() {
let mut formatter = Formatter::new();
formatter.config.use_tabs = false;
formatter.config.indent_width = 2;
let exprs = vec![create_simple_literal(1)];
let expr = Expr::new(ExprKind::Block(exprs), Default::default());
let result = formatter
.format(&expr)
.expect("operation should succeed in test");
assert!(!result.is_empty());
}
#[test]
fn test_format_nested_expressions() {
let formatter = Formatter::new();
let inner = Expr::new(
ExprKind::Binary {
left: Box::new(create_simple_literal(1)),
op: BinaryOp::Add,
right: Box::new(create_simple_literal(2)),
},
Default::default(),
);
let outer = Expr::new(
ExprKind::Binary {
left: Box::new(inner),
op: BinaryOp::Multiply,
right: Box::new(create_simple_literal(3)),
},
Default::default(),
);
let result = formatter
.format(&outer)
.expect("operation should succeed in test");
assert!(result.contains("1 + 2"));
assert!(result.contains("* 3"));
}
#[test]
fn test_format_multiple_params() {
let formatter = Formatter::new();
let body = create_simple_literal(0);
let param1 = Param {
pattern: Pattern::Identifier("x".to_string()),
ty: Type {
kind: TypeKind::Named("Int".to_string()),
span: Default::default(),
},
span: Default::default(),
is_mutable: false,
default_value: None,
};
let param2 = Param {
pattern: Pattern::Identifier("y".to_string()),
ty: Type {
kind: TypeKind::Named("Float".to_string()),
span: Default::default(),
},
span: Default::default(),
is_mutable: false,
default_value: None,
};
let expr = Expr::new(
ExprKind::Function {
name: "test".to_string(),
type_params: vec![],
params: vec![param1, param2],
return_type: None,
body: Box::new(body),
is_async: false,
is_pub: false,
},
Default::default(),
);
let result = formatter
.format(&expr)
.expect("operation should succeed in test");
assert!(result.contains("x: Int, y: Float"));
}
#[test]
fn test_format_empty_block() {
let formatter = Formatter::new();
let expr = Expr::new(ExprKind::Block(vec![]), Default::default());
let result = formatter.format(&expr);
assert!(result.is_ok());
}
#[test]
fn test_format_string_with_quotes() {
let formatter = Formatter::new();
let expr = Expr::new(
ExprKind::Literal(Literal::String("hello \"world\"".to_string())),
Default::default(),
);
let result = formatter
.format(&expr)
.expect("operation should succeed in test");
assert_eq!(result, "\"hello \\\"world\\\"\"");
}
#[test]
fn test_format_special_characters() {
let formatter = Formatter::new();
let expr = Expr::new(ExprKind::Literal(Literal::Char('\n')), Default::default());
let result = formatter
.format(&expr)
.expect("operation should succeed in test");
assert_eq!(result, "'\n'");
}
#[test]
fn test_format_fallback_case() {
let formatter = Formatter::new();
let expr = Expr::new(
ExprKind::StringInterpolation { parts: vec![] },
Default::default(),
);
let result = formatter
.format(&expr)
.expect("operation should succeed in test");
assert!(!result.is_empty());
}
#[test]
fn test_formatter_field_access() {
let formatter = Formatter::new();
assert_eq!(formatter.config.indent_width, 4);
assert!(!formatter.config.use_tabs);
}
#[test]
fn test_format_deeply_nested_block() {
let formatter = Formatter::new();
let inner_block = Expr::new(
ExprKind::Block(vec![create_simple_literal(1)]),
Default::default(),
);
let outer_block = Expr::new(ExprKind::Block(vec![inner_block]), Default::default());
let result = formatter
.format(&outer_block)
.expect("operation should succeed in test");
assert!(!result.is_empty());
}
#[test]
fn test_format_null_literal() {
let formatter = Formatter::new();
let expr = Expr::new(ExprKind::Literal(Literal::Null), Default::default());
let result = formatter.format(&expr).expect("should format");
assert_eq!(result, "null");
}
#[test]
fn test_format_atom_literal() {
let formatter = Formatter::new();
let expr = Expr::new(
ExprKind::Literal(Literal::Atom("foo".to_string())),
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert_eq!(result, ":foo");
}
#[test]
fn test_format_byte_literal() {
let formatter = Formatter::new();
let expr = Expr::new(ExprKind::Literal(Literal::Byte(b'x')), Default::default());
let result = formatter.format(&expr).expect("should format");
assert_eq!(result, "b'x'");
}
#[test]
fn test_format_with_tabs_coverage() {
let config = FormatterConfig {
use_tabs: true,
indent_width: 4,
..Default::default()
};
let formatter = Formatter::with_config(config);
let inner_block = Expr::new(
ExprKind::Block(vec![create_simple_literal(42)]),
Default::default(),
);
let outer_block = Expr::new(ExprKind::Block(vec![inner_block]), Default::default());
let result = formatter.format(&outer_block).expect("should format");
assert!(result.contains('\t'));
}
#[test]
fn test_format_binary_expression_coverage() {
let formatter = Formatter::new();
let left = create_simple_literal(1);
let right = create_simple_literal(2);
let expr = Expr::new(
ExprKind::Binary {
left: Box::new(left),
op: crate::frontend::ast::BinaryOp::Add,
right: Box::new(right),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains('+'));
assert!(result.contains('1'));
assert!(result.contains('2'));
}
#[test]
fn test_format_let_statement() {
let formatter = Formatter::new();
let value = create_simple_literal(42);
let body = Expr::new(ExprKind::Literal(Literal::Unit), Default::default());
let expr = Expr::new(
ExprKind::Let {
name: "x".to_string(),
is_mutable: false,
type_annotation: None,
value: Box::new(value),
body: Box::new(body),
else_block: None,
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("let"));
assert!(result.contains("x"));
assert!(result.contains("42"));
}
#[test]
fn test_format_let_with_block_body() {
let formatter = Formatter::new();
let value = create_simple_literal(10);
let block_content = create_simple_literal(20);
let body = Expr::new(ExprKind::Block(vec![block_content]), Default::default());
let expr = Expr::new(
ExprKind::Let {
name: "y".to_string(),
is_mutable: false,
type_annotation: None,
value: Box::new(value),
body: Box::new(body),
else_block: None,
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("let"));
assert!(result.contains("y"));
}
#[test]
fn test_format_type_named_coverage() {
use crate::frontend::ast::{Type, TypeKind};
let ty = Type {
kind: TypeKind::Named("i32".to_string()),
span: Default::default(),
};
let result = Formatter::format_type(&ty.kind);
assert_eq!(result, "i32");
}
#[test]
fn test_format_type_generic() {
use crate::frontend::ast::{Type, TypeKind};
let inner = Type {
kind: TypeKind::Named("String".to_string()),
span: Default::default(),
};
let ty = Type {
kind: TypeKind::Generic {
base: "Vec".to_string(),
params: vec![inner],
},
span: Default::default(),
};
let result = Formatter::format_type(&ty.kind);
assert_eq!(result, "Vec<String>");
}
#[test]
fn test_format_type_tuple() {
use crate::frontend::ast::{Type, TypeKind};
let int_type = Type {
kind: TypeKind::Named("i32".to_string()),
span: Default::default(),
};
let str_type = Type {
kind: TypeKind::Named("String".to_string()),
span: Default::default(),
};
let ty = Type {
kind: TypeKind::Tuple(vec![int_type, str_type]),
span: Default::default(),
};
let result = Formatter::format_type(&ty.kind);
assert!(result.contains("i32"));
assert!(result.contains("String"));
}
#[test]
fn test_format_type_array() {
use crate::frontend::ast::{Type, TypeKind};
let elem_type = Type {
kind: TypeKind::Named("u8".to_string()),
span: Default::default(),
};
let ty = Type {
kind: TypeKind::Array {
elem_type: Box::new(elem_type),
size: 10,
},
span: Default::default(),
};
let result = Formatter::format_type(&ty.kind);
assert!(result.contains("u8"));
assert!(result.contains("10"));
}
#[test]
fn test_format_type_function() {
use crate::frontend::ast::{Type, TypeKind};
let param_type = Type {
kind: TypeKind::Named("i32".to_string()),
span: Default::default(),
};
let ret_type = Type {
kind: TypeKind::Named("bool".to_string()),
span: Default::default(),
};
let ty = Type {
kind: TypeKind::Function {
params: vec![param_type],
ret: Box::new(ret_type),
},
span: Default::default(),
};
let result = Formatter::format_type(&ty.kind);
assert!(result.contains("i32"));
assert!(result.contains("->"));
assert!(result.contains("bool"));
}
#[test]
fn test_set_source() {
let mut formatter = Formatter::new();
formatter.set_source("let x = 42");
assert!(formatter.source.is_some());
}
#[test]
fn test_format_call_expression() {
let formatter = Formatter::new();
let func = create_identifier("print");
let arg = create_simple_literal(42);
let expr = Expr::new(
ExprKind::Call {
func: Box::new(func),
args: vec![arg],
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("print"));
assert!(result.contains("42"));
}
#[test]
fn test_format_array_literal() {
let code = "[1, 2]";
let mut parser = crate::frontend::parser::Parser::new(code);
if let Ok(ast) = parser.parse() {
let formatter = Formatter::new();
let result = formatter.format(&ast).expect("should format");
assert!(result.contains('['));
assert!(result.contains(']'));
}
}
#[test]
fn test_format_tuple_literal() {
let code = "(1, 2)";
let mut parser = crate::frontend::parser::Parser::new(code);
if let Ok(ast) = parser.parse() {
let formatter = Formatter::new();
let result = formatter.format(&ast).expect("should format");
assert!(result.contains('(') || result.contains(')'));
}
}
#[test]
fn test_format_if_expression_coverage() {
let formatter = Formatter::new();
let condition = Expr::new(ExprKind::Literal(Literal::Bool(true)), Default::default());
let then_branch = create_simple_literal(1);
let else_branch = create_simple_literal(2);
let expr = Expr::new(
ExprKind::If {
condition: Box::new(condition),
then_branch: Box::new(then_branch),
else_branch: Some(Box::new(else_branch)),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("if"));
}
#[test]
fn test_format_unary_negation() {
let formatter = Formatter::new();
let operand = create_simple_literal(42);
let expr = Expr::new(
ExprKind::Unary {
op: crate::frontend::ast::UnaryOp::Negate,
operand: Box::new(operand),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains('-') || result.contains("42"));
}
#[test]
fn test_format_unary_not() {
let formatter = Formatter::new();
let operand = Expr::new(ExprKind::Literal(Literal::Bool(true)), Default::default());
let expr = Expr::new(
ExprKind::Unary {
op: crate::frontend::ast::UnaryOp::Not,
operand: Box::new(operand),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains('!') || result.contains("true"));
}
#[test]
fn test_format_field_access() {
let formatter = Formatter::new();
let obj = create_identifier("point");
let expr = Expr::new(
ExprKind::FieldAccess {
object: Box::new(obj),
field: "x".to_string(),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("point"));
assert!(result.contains('x'));
}
#[test]
fn test_format_index_access() {
let formatter = Formatter::new();
let arr = create_identifier("arr");
let idx = create_simple_literal(0);
let expr = Expr::new(
ExprKind::IndexAccess {
object: Box::new(arr),
index: Box::new(idx),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("arr"));
assert!(result.contains('['));
}
#[test]
fn test_format_return() {
let formatter = Formatter::new();
let value = create_simple_literal(42);
let expr = Expr::new(
ExprKind::Return {
value: Some(Box::new(value)),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("return") || result.contains("42"));
}
#[test]
fn test_format_break() {
let formatter = Formatter::new();
let expr = Expr::new(
ExprKind::Break {
label: None,
value: None,
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("break"));
}
#[test]
fn test_format_continue() {
let formatter = Formatter::new();
let expr = Expr::new(ExprKind::Continue { label: None }, Default::default());
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("continue"));
}
#[test]
fn test_format_range() {
let code = "0..10";
let mut parser = crate::frontend::parser::Parser::new(code);
if let Ok(ast) = parser.parse() {
let formatter = Formatter::new();
let result = formatter.format(&ast).expect("should format");
assert!(result.contains("..") || (result.contains('0') && result.contains("10")));
}
}
#[test]
fn test_format_object_literal() {
let code = "{ x: 42 }";
let mut parser = crate::frontend::parser::Parser::new(code);
if let Ok(ast) = parser.parse() {
let formatter = Formatter::new();
let result = formatter.format(&ast).expect("should format");
assert!(result.contains('{') || result.contains('x'));
}
}
#[test]
fn test_formatter_with_different_indent_widths() {
for width in [2, 4, 8] {
let config = FormatterConfig {
indent_width: width,
use_tabs: false,
..Default::default()
};
let formatter = Formatter::with_config(config);
let block = Expr::new(
ExprKind::Block(vec![create_simple_literal(1)]),
Default::default(),
);
let result = formatter.format(&block).expect("should format");
assert!(!result.is_empty());
}
}
}
#[cfg(test)]
#[allow(clippy::expect_used)]
mod property_tests_formatter {
use super::*;
use proptest::prelude::*;
proptest! {
#![proptest_config(ProptestConfig::with_cases(50))]
#[test]
fn prop_formatter_new_never_panics(_dummy: u8) {
let _formatter = Formatter::new();
prop_assert!(true);
}
#[test]
fn prop_set_source_never_panics(source in "[a-zA-Z0-9_ ]{0,100}") {
let mut formatter = Formatter::new();
formatter.set_source(source);
prop_assert!(true);
}
#[test]
fn prop_formatter_config_default_valid(_dummy: u8) {
let config = FormatterConfig::default();
prop_assert!(config.indent_width > 0);
}
#[test]
fn prop_indent_config_valid(indent in 1usize..8) {
let config = FormatterConfig {
indent_width: indent,
..Default::default()
};
let _formatter = Formatter::with_config(config);
prop_assert!(true);
}
#[test]
fn prop_format_parsed_integer(n in -1000i64..1000) {
let code = format!("{n}");
let mut parser = crate::frontend::parser::Parser::new(&code);
if let Ok(ast) = parser.parse() {
let formatter = Formatter::new();
let result = formatter.format(&ast);
prop_assert!(result.is_ok());
}
}
#[test]
fn prop_format_parsed_bool(b in proptest::bool::ANY) {
let code = if b { "true" } else { "false" };
let mut parser = crate::frontend::parser::Parser::new(code);
if let Ok(ast) = parser.parse() {
let formatter = Formatter::new();
let result = formatter.format(&ast);
prop_assert!(result.is_ok());
}
}
#[test]
fn prop_format_parsed_string(s in "[a-zA-Z0-9]{0,20}") {
let code = format!("\"{s}\"");
let mut parser = crate::frontend::parser::Parser::new(&code);
if let Ok(ast) = parser.parse() {
let formatter = Formatter::new();
let result = formatter.format(&ast);
prop_assert!(result.is_ok());
}
}
#[test]
fn prop_format_parsed_identifier(name in "[a-z][a-z0-9_]{0,10}") {
let mut parser = crate::frontend::parser::Parser::new(&name);
if let Ok(ast) = parser.parse() {
let formatter = Formatter::new();
let result = formatter.format(&ast);
prop_assert!(result.is_ok());
}
}
#[test]
fn prop_format_parsed_let(n in -100i64..100) {
let code = format!("let x = {n}");
let mut parser = crate::frontend::parser::Parser::new(&code);
if let Ok(ast) = parser.parse() {
let formatter = Formatter::new();
let result = formatter.format(&ast);
prop_assert!(result.is_ok());
}
}
}
}
#[cfg(test)]
mod formatter_tests_r164 {
use super::*;
use crate::frontend::ast::*;
fn create_simple_literal(value: i64) -> Expr {
Expr::new(
ExprKind::Literal(Literal::Integer(value, None)),
Default::default(),
)
}
fn create_identifier(name: &str) -> Expr {
Expr::new(ExprKind::Identifier(name.to_string()), Default::default())
}
fn create_bool_literal(b: bool) -> Expr {
Expr::new(ExprKind::Literal(Literal::Bool(b)), Default::default())
}
#[test]
fn test_format_while_loop_r164() {
let formatter = Formatter::new();
let condition = create_bool_literal(true);
let body = Expr::new(
ExprKind::Block(vec![create_simple_literal(1)]),
Default::default(),
);
let expr = Expr::new(
ExprKind::While {
condition: Box::new(condition),
body: Box::new(body),
label: None,
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("while"));
}
#[test]
fn test_format_for_loop_r164() {
let formatter = Formatter::new();
let iter = create_identifier("items");
let body = Expr::new(
ExprKind::Block(vec![create_simple_literal(1)]),
Default::default(),
);
let expr = Expr::new(
ExprKind::For {
var: "x".to_string(),
pattern: Some(Pattern::Identifier("x".to_string())),
iter: Box::new(iter),
body: Box::new(body),
label: None,
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("for"));
assert!(result.contains("in"));
}
#[test]
fn test_format_method_call_r164() {
let formatter = Formatter::new();
let receiver = create_identifier("obj");
let expr = Expr::new(
ExprKind::MethodCall {
receiver: Box::new(receiver),
method: "get".to_string(),
args: vec![create_simple_literal(0)],
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("obj"));
assert!(result.contains("get"));
}
#[test]
fn test_format_lambda_r164() {
let formatter = Formatter::new();
let body = create_simple_literal(42);
let param = Param {
pattern: Pattern::Identifier("x".to_string()),
ty: Type {
kind: TypeKind::Named("Any".to_string()),
span: Default::default(),
},
span: Default::default(),
is_mutable: false,
default_value: None,
};
let expr = Expr::new(
ExprKind::Lambda {
params: vec![param],
body: Box::new(body),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("|"));
assert!(result.contains("42"));
}
#[test]
fn test_format_ternary_r164() {
let formatter = Formatter::new();
let condition = create_bool_literal(true);
let true_expr = create_simple_literal(1);
let false_expr = create_simple_literal(2);
let expr = Expr::new(
ExprKind::Ternary {
condition: Box::new(condition),
true_expr: Box::new(true_expr),
false_expr: Box::new(false_expr),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("?"));
assert!(result.contains(":"));
}
#[test]
fn test_format_assign_r164() {
let formatter = Formatter::new();
let target = create_identifier("x");
let value = create_simple_literal(42);
let expr = Expr::new(
ExprKind::Assign {
target: Box::new(target),
value: Box::new(value),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("x"));
assert!(result.contains("="));
assert!(result.contains("42"));
}
#[test]
fn test_format_compound_assign_r164() {
let formatter = Formatter::new();
let target = create_identifier("x");
let value = create_simple_literal(1);
let expr = Expr::new(
ExprKind::CompoundAssign {
target: Box::new(target),
op: BinaryOp::Add,
value: Box::new(value),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("+=") || (result.contains("x") && result.contains("1")));
}
#[test]
fn test_format_return_no_value_r164() {
let formatter = Formatter::new();
let expr = Expr::new(ExprKind::Return { value: None }, Default::default());
let result = formatter.format(&expr).expect("should format");
assert_eq!(result, "return");
}
#[test]
fn test_format_break_with_value_r164() {
let formatter = Formatter::new();
let value = create_simple_literal(42);
let expr = Expr::new(
ExprKind::Break {
label: None,
value: Some(Box::new(value)),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("break"));
assert!(result.contains("42"));
}
#[test]
fn test_format_list_literal_r164() {
let formatter = Formatter::new();
let items = vec![
create_simple_literal(1),
create_simple_literal(2),
create_simple_literal(3),
];
let expr = Expr::new(ExprKind::List(items), Default::default());
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("["));
assert!(result.contains("]"));
assert!(result.contains("1"));
assert!(result.contains("2"));
assert!(result.contains("3"));
}
#[test]
fn test_format_tuple_literal_direct_r164() {
let formatter = Formatter::new();
let items = vec![create_simple_literal(1), create_simple_literal(2)];
let expr = Expr::new(ExprKind::Tuple(items), Default::default());
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("("));
assert!(result.contains(")"));
}
#[test]
fn test_format_range_exclusive_r164() {
let formatter = Formatter::new();
let start = create_simple_literal(0);
let end = create_simple_literal(10);
let expr = Expr::new(
ExprKind::Range {
start: Box::new(start),
end: Box::new(end),
inclusive: false,
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains(".."));
assert!(!result.contains("..="));
}
#[test]
fn test_format_range_inclusive_r164() {
let formatter = Formatter::new();
let start = create_simple_literal(0);
let end = create_simple_literal(10);
let expr = Expr::new(
ExprKind::Range {
start: Box::new(start),
end: Box::new(end),
inclusive: true,
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("..="));
}
#[test]
fn test_format_throw_r164() {
let formatter = Formatter::new();
let error = Expr::new(
ExprKind::Literal(Literal::String("error".to_string())),
Default::default(),
);
let expr = Expr::new(
ExprKind::Throw {
expr: Box::new(error),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("throw"));
}
#[test]
fn test_format_await_r164() {
let formatter = Formatter::new();
let inner = create_identifier("future");
let expr = Expr::new(
ExprKind::Await {
expr: Box::new(inner),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("await"));
assert!(result.contains("future"));
}
#[test]
fn test_format_async_block_r164() {
let formatter = Formatter::new();
let body = create_simple_literal(42);
let expr = Expr::new(
ExprKind::AsyncBlock {
body: Box::new(body),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("async"));
}
#[test]
fn test_format_ok_variant_r164() {
let formatter = Formatter::new();
let value = create_simple_literal(42);
let expr = Expr::new(
ExprKind::Ok {
value: Box::new(value),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("Ok"));
assert!(result.contains("42"));
}
#[test]
fn test_format_err_variant_r164() {
let formatter = Formatter::new();
let error = Expr::new(
ExprKind::Literal(Literal::String("error".to_string())),
Default::default(),
);
let expr = Expr::new(
ExprKind::Err {
error: Box::new(error),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("Err"));
}
#[test]
fn test_format_some_variant_r164() {
let formatter = Formatter::new();
let value = create_simple_literal(42);
let expr = Expr::new(
ExprKind::Some {
value: Box::new(value),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("Some"));
}
#[test]
fn test_format_none_variant_r164() {
let formatter = Formatter::new();
let expr = Expr::new(ExprKind::None, Default::default());
let result = formatter.format(&expr).expect("should format");
assert_eq!(result, "None");
}
#[test]
fn test_format_try_expr_r164() {
let formatter = Formatter::new();
let inner = create_identifier("result");
let expr = Expr::new(
ExprKind::Try {
expr: Box::new(inner),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("?"));
}
#[test]
fn test_format_spawn_r164() {
let formatter = Formatter::new();
let actor = create_identifier("my_actor");
let expr = Expr::new(
ExprKind::Spawn {
actor: Box::new(actor),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("spawn"));
}
#[test]
fn test_format_optional_field_access_r164() {
let formatter = Formatter::new();
let obj = create_identifier("maybe_obj");
let expr = Expr::new(
ExprKind::OptionalFieldAccess {
object: Box::new(obj),
field: "value".to_string(),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("?."));
}
#[test]
fn test_format_type_cast_r164() {
let formatter = Formatter::new();
let value = create_simple_literal(42);
let expr = Expr::new(
ExprKind::TypeCast {
expr: Box::new(value),
target_type: "f64".to_string(),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("as"));
assert!(result.contains("f64"));
}
#[test]
fn test_format_array_init_r164() {
let formatter = Formatter::new();
let value = create_simple_literal(0);
let size = create_simple_literal(10);
let expr = Expr::new(
ExprKind::ArrayInit {
value: Box::new(value),
size: Box::new(size),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("["));
assert!(result.contains(";"));
assert!(result.contains("]"));
}
#[test]
fn test_format_qualified_name_r164() {
let formatter = Formatter::new();
let expr = Expr::new(
ExprKind::QualifiedName {
module: "std".to_string(),
name: "println".to_string(),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("std"));
assert!(result.contains("::"));
assert!(result.contains("println"));
}
#[test]
fn test_format_spread_r164() {
let formatter = Formatter::new();
let inner = create_identifier("arr");
let expr = Expr::new(
ExprKind::Spread {
expr: Box::new(inner),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("..."));
}
#[test]
fn test_format_pre_increment_r164() {
let formatter = Formatter::new();
let target = create_identifier("x");
let expr = Expr::new(
ExprKind::PreIncrement {
target: Box::new(target),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("++"));
}
#[test]
fn test_format_post_increment_r164() {
let formatter = Formatter::new();
let target = create_identifier("x");
let expr = Expr::new(
ExprKind::PostIncrement {
target: Box::new(target),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("++"));
}
#[test]
fn test_format_pre_decrement_r164() {
let formatter = Formatter::new();
let target = create_identifier("x");
let expr = Expr::new(
ExprKind::PreDecrement {
target: Box::new(target),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("--"));
}
#[test]
fn test_format_post_decrement_r164() {
let formatter = Formatter::new();
let target = create_identifier("x");
let expr = Expr::new(
ExprKind::PostDecrement {
target: Box::new(target),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("--"));
}
#[test]
fn test_format_import_r164() {
let formatter = Formatter::new();
let expr = Expr::new(
ExprKind::Import {
module: "std::io".to_string(),
items: Some(vec!["read".to_string(), "write".to_string()]),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("import"));
}
#[test]
fn test_format_module_declaration_r164() {
let formatter = Formatter::new();
let expr = Expr::new(
ExprKind::ModuleDeclaration {
name: "utils".to_string(),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("mod"));
assert!(result.contains("utils"));
}
#[test]
fn test_format_export_r164() {
let formatter = Formatter::new();
let inner = create_identifier("my_fn");
let expr = Expr::new(
ExprKind::Export {
expr: Box::new(inner),
is_default: false,
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("export"));
}
#[test]
fn test_format_export_default_r164() {
let formatter = Formatter::new();
let inner = create_identifier("main");
let expr = Expr::new(
ExprKind::Export {
expr: Box::new(inner),
is_default: true,
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("export"));
assert!(result.contains("default"));
}
#[test]
fn test_format_loop_r164() {
let formatter = Formatter::new();
let body = create_simple_literal(1);
let expr = Expr::new(
ExprKind::Loop {
body: Box::new(body),
label: None,
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("loop"));
}
#[test]
fn test_format_binary_sub_r164() {
let formatter = Formatter::new();
let left = create_simple_literal(10);
let right = create_simple_literal(5);
let expr = Expr::new(
ExprKind::Binary {
left: Box::new(left),
op: BinaryOp::Subtract,
right: Box::new(right),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("-"));
}
#[test]
fn test_format_binary_mul_r164() {
let formatter = Formatter::new();
let left = create_simple_literal(3);
let right = create_simple_literal(4);
let expr = Expr::new(
ExprKind::Binary {
left: Box::new(left),
op: BinaryOp::Multiply,
right: Box::new(right),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("*"));
}
#[test]
fn test_format_binary_div_r164() {
let formatter = Formatter::new();
let left = create_simple_literal(10);
let right = create_simple_literal(2);
let expr = Expr::new(
ExprKind::Binary {
left: Box::new(left),
op: BinaryOp::Divide,
right: Box::new(right),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("/"));
}
#[test]
fn test_format_binary_mod_r164() {
let formatter = Formatter::new();
let left = create_simple_literal(10);
let right = create_simple_literal(3);
let expr = Expr::new(
ExprKind::Binary {
left: Box::new(left),
op: BinaryOp::Modulo,
right: Box::new(right),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("%"));
}
#[test]
fn test_format_binary_eq_r164() {
let formatter = Formatter::new();
let left = create_simple_literal(1);
let right = create_simple_literal(1);
let expr = Expr::new(
ExprKind::Binary {
left: Box::new(left),
op: BinaryOp::Equal,
right: Box::new(right),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("=="));
}
#[test]
fn test_format_binary_ne_r164() {
let formatter = Formatter::new();
let left = create_simple_literal(1);
let right = create_simple_literal(2);
let expr = Expr::new(
ExprKind::Binary {
left: Box::new(left),
op: BinaryOp::NotEqual,
right: Box::new(right),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("!="));
}
#[test]
fn test_format_binary_lt_r164() {
let formatter = Formatter::new();
let left = create_simple_literal(1);
let right = create_simple_literal(2);
let expr = Expr::new(
ExprKind::Binary {
left: Box::new(left),
op: BinaryOp::Less,
right: Box::new(right),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("<"));
}
#[test]
fn test_format_binary_gt_r164() {
let formatter = Formatter::new();
let left = create_simple_literal(2);
let right = create_simple_literal(1);
let expr = Expr::new(
ExprKind::Binary {
left: Box::new(left),
op: BinaryOp::Greater,
right: Box::new(right),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains(">"));
}
#[test]
fn test_format_binary_and_r164() {
let formatter = Formatter::new();
let left = create_bool_literal(true);
let right = create_bool_literal(false);
let expr = Expr::new(
ExprKind::Binary {
left: Box::new(left),
op: BinaryOp::And,
right: Box::new(right),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("&&"));
}
#[test]
fn test_format_binary_or_r164() {
let formatter = Formatter::new();
let left = create_bool_literal(true);
let right = create_bool_literal(false);
let expr = Expr::new(
ExprKind::Binary {
left: Box::new(left),
op: BinaryOp::Or,
right: Box::new(right),
},
Default::default(),
);
let result = formatter.format(&expr).expect("should format");
assert!(result.contains("||"));
}
}