use std::fmt;
use std::sync::Arc;
use crate::{expr, Expr, ExprKind, Normal, Number};
fn wxf_display(f: &mut fmt::Formatter, expr: &Expr, indent: Option<usize>) -> fmt::Result {
use base64::Engine;
match wolfram_serialize::to_wxf(expr, None) {
Ok(bytes) => {
let b64 = base64::engine::general_purpose::STANDARD.encode(&bytes);
let e = expr!(::BinaryDeserialize[::ByteArray[(b64)]]);
fmt_kind(f, e.kind(), indent)
},
Err(_) => write!(f, "Failure[\"BinarySerializeError\"]"),
}
}
fn is_compound(kind: &ExprKind) -> bool {
matches!(kind, ExprKind::Normal(_) | ExprKind::Association(_))
}
fn is_nested(kind: &ExprKind) -> bool {
match kind {
ExprKind::Normal(n) => n.contents.iter().any(|e| is_compound(e.kind())),
ExprKind::Association(a) => a.iter().any(|e| is_compound(e.value.kind())),
_ => false,
}
}
fn child_indent(indent: Option<usize>, breaks: bool) -> Option<usize> {
if breaks {
indent.map(|d| d + 1)
} else {
indent
}
}
fn fmt_seq<F>(
f: &mut fmt::Formatter,
indent: Option<usize>,
open: &str,
close: &str,
len: usize,
brk: bool,
mut item: F,
) -> fmt::Result
where
F: FnMut(&mut fmt::Formatter, usize) -> fmt::Result,
{
let depth = indent.unwrap_or(0);
f.write_str(open)?;
for i in 0..len {
if brk {
write!(f, "\n{}", " ".repeat(depth + 1))?;
} else if i > 0 {
f.write_str(", ")?;
}
item(f, i)?;
if brk && i + 1 < len {
f.write_str(",")?;
}
}
if brk {
write!(f, "\n{}", " ".repeat(depth))?;
}
f.write_str(close)
}
fn fmt_normal(f: &mut fmt::Formatter, n: &Normal, indent: Option<usize>) -> fmt::Result {
let ExprKind::Symbol(sym) = n.head.kind() else {
return fmt_call(f, n, indent);
};
match sym.as_str() {
"System`List" | "List" => fmt_list(f, n, indent),
"System`Rule" | "Rule" => fmt_infix(f, n, indent, "->"),
"System`RuleDelayed" | "RuleDelayed" => fmt_infix(f, n, indent, ":>"),
"System`Set" | "Set" => fmt_infix(f, n, indent, "="),
"System`Slot" | "Slot" => fmt_slot(f, n, indent, "#"),
"System`SlotSequence" | "SlotSequence" => fmt_slot(f, n, indent, "##"),
_ => fmt_call(f, n, indent),
}
}
fn fmt_delimited(
f: &mut fmt::Formatter,
n: &Normal,
indent: Option<usize>,
open: &str,
close: &str,
) -> fmt::Result {
let brk = indent.is_some() && n.contents.iter().any(|e| is_nested(e.kind()));
let inner = child_indent(indent, brk);
fmt_seq(f, indent, open, close, n.contents.len(), brk, |f, i| {
fmt_kind(f, n.contents[i].kind(), inner)
})
}
fn fmt_call(f: &mut fmt::Formatter, n: &Normal, indent: Option<usize>) -> fmt::Result {
fmt_delimited(f, n, indent, &format!("{}[", n.head), "]")
}
fn fmt_list(f: &mut fmt::Formatter, n: &Normal, indent: Option<usize>) -> fmt::Result {
fmt_delimited(f, n, indent, "{", "}")
}
fn fmt_infix(
f: &mut fmt::Formatter,
n: &Normal,
indent: Option<usize>,
op: &str,
) -> fmt::Result {
if n.contents.len() != 2 {
return fmt_call(f, n, indent);
}
fmt_kind(f, n.contents[0].kind(), indent)?;
write!(f, " {op} ")?;
fmt_kind(f, n.contents[1].kind(), indent)
}
fn fmt_slot(
f: &mut fmt::Formatter,
n: &Normal,
indent: Option<usize>,
prefix: &str,
) -> fmt::Result {
match n.contents.as_slice() {
[arg] => match arg.kind() {
ExprKind::String(name) => {
f.write_str(prefix)?;
f.write_str(name)
},
kind @ ExprKind::Integer(_) => {
f.write_str(prefix)?;
fmt_kind(f, kind, indent)
},
_ => fmt_call(f, n, indent),
},
_ => fmt_call(f, n, indent),
}
}
fn fmt_kind(f: &mut fmt::Formatter, kind: &ExprKind, indent: Option<usize>) -> fmt::Result {
match kind {
ExprKind::Normal(n) => fmt_normal(f, n, indent),
ExprKind::Association(a) => {
let brk = indent.is_some() && a.iter().any(|e| is_nested(e.value.kind()));
let inner = child_indent(indent, brk);
fmt_seq(f, indent, "<|", "|>", a.len(), brk, |f, i| {
let entry = &a[i];
let arrow = if entry.delayed { ":>" } else { "->" };
write!(f, "{} {arrow} ", entry.key)?;
fmt_kind(f, entry.value.kind(), inner)
})
},
ExprKind::Integer(int) => write!(f, "{int}"),
ExprKind::Real(real) => write!(f, "{:?}", **real),
ExprKind::String(string) => write!(f, "{string:?}"),
ExprKind::Symbol(symbol) => write!(f, "{symbol}"),
ExprKind::ByteArray(ba) => {
use base64::Engine;
let b64 = base64::engine::general_purpose::STANDARD.encode(ba.as_slice());
fmt_kind(f, expr!(::ByteArray[(b64)]).kind(), indent)
},
ExprKind::NumericArray(arr) => {
let expr = Expr {
inner: Arc::new(ExprKind::NumericArray(arr.clone())),
};
wxf_display(f, &expr, indent)
},
ExprKind::PackedArray(arr) => {
let expr = Expr {
inner: Arc::new(ExprKind::PackedArray(arr.clone())),
};
wxf_display(f, &expr, indent)
},
ExprKind::BigInteger(n) => write!(f, "{}", n.as_str()),
ExprKind::BigReal(r) => write!(f, "{}", r.as_str()),
}
}
impl fmt::Display for Expr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt_kind(f, self.kind(), None)
}
}
impl fmt::Display for ExprKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt_kind(f, self, None)
}
}
impl fmt::Debug for ExprKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt_kind(f, self, Some(0))
}
}
impl fmt::Display for Normal {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt_normal(f, self, None)
}
}
impl fmt::Display for Number {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt_kind(f, &ExprKind::from(*self), None)
}
}