mod format;
use format::{FormatDesc, ProcessedFormat};
#[derive(Clone, Debug)]
pub struct Document(FormatDesc);
impl Document {
pub(crate) fn map(self, f: impl FnOnce(FormatDesc) -> FormatDesc) -> Document {
Document(f(self.0))
}
pub(crate) fn flatten_with(&self, c: &str) -> Document {
self.clone().map(|x| x.flatten_with(c))
}
pub(crate) fn flatten(&self) -> Document {
self.clone().map(|x| x.flatten())
}
}
pub trait Format {
fn format(&self) -> Document;
fn pretty(&self, w: i32) -> ProcessedFormat {
self.format().0.pretty(w)
}
}
impl Format for Document {
fn format(&self) -> Document {
self.clone()
}
}
#[inline(always)]
pub fn nil() -> Document {
Document(FormatDesc::Nil)
}
#[inline(always)]
pub fn line() -> Document {
Document(FormatDesc::Line)
}
#[inline(always)]
pub fn text(s: impl Into<String>) -> Document {
Document(FormatDesc::Text(s.into()))
}
#[inline(always)]
pub fn nest(i: i32, x: Document) -> Document {
Document(FormatDesc::Nest(i, Box::new(x.0)))
}
#[inline(always)]
pub fn cat(x: Document, y: Document) -> Document {
Document(FormatDesc::Cat(Box::new(x.0), Box::new(y.0)))
}
#[inline(always)]
pub(crate) fn union(x: Document, y: Document) -> Document {
Document(FormatDesc::Union(Box::new(x.0), Box::new(y.0)))
}
impl std::ops::BitAnd<Document> for Document {
type Output = Document;
#[inline(always)]
fn bitand(self, rhs: Document) -> Self::Output {
cat(self, rhs)
}
}
impl std::ops::Add<Document> for Document {
type Output = Document;
#[inline(always)]
fn add(self, rhs: Document) -> Self::Output {
self & text(" ") & rhs
}
}
impl std::ops::Div<Document> for Document {
type Output = Document;
#[inline(always)]
fn div(self, rhs: Document) -> Self::Output {
self & line() & rhs
}
}
impl std::ops::Mul<Document> for Document {
type Output = Document;
#[inline(always)]
fn mul(self, rhs: Document) -> Self::Output {
self & union(text(" "), line()) & rhs
}
}
#[inline(always)]
pub fn group(x: Document) -> Document {
union(x.flatten(), x)
}
#[inline(always)]
pub fn group_with(c: &str, x: Document) -> Document {
union(x.flatten_with(c), x)
}
#[inline(always)]
pub fn bracket(i: i32, l: impl Into<String>, x: Document, r: impl Into<String>) -> Document {
group(text(l) & nest(i, line() & x) / text(r))
}
#[inline(always)]
pub fn fold(xs: &[impl Format], op: impl FnMut(Document, Document) -> Document) -> Document {
xs.iter().map(Format::format).reduce(op).unwrap_or(nil())
}
#[inline(always)]
pub fn spread(xs: &[impl Format]) -> Document {
fold(xs, |lhs, rhs| lhs + rhs)
}
#[inline(always)]
pub fn stack(xs: &[impl Format]) -> Document {
fold(xs, |lhs, rhs| lhs / rhs)
}
pub fn fill(xs: &[impl Format]) -> Document {
match &xs[..] {
[] => nil(),
[x] => x.format(),
[x, y, z @ ..] => {
let x = x.format();
let z1 = [y]
.iter()
.map(|y| y.format().flatten())
.chain(z.iter().map(Format::format))
.collect::<Vec<_>>();
let z2 = [y]
.iter()
.map(|y| y.format())
.chain(z.iter().map(Format::format))
.collect::<Vec<_>>();
union(x.flatten() + fill(&z1), x / fill(&z2))
}
}
}