use std::iter::FromIterator;
use ansi_term::Style;
use itertools::Itertools;
use {Doc, DocInner, Sparkly};
impl Doc {
pub fn empty() -> Doc {
Doc::from(DocInner::Text("".into()))
}
pub fn line() -> Doc {
Doc::from(DocInner::Line(None))
}
pub fn line_or(s: &'static str) -> Doc {
Doc::from(DocInner::Line(Some(s)))
}
pub fn lines<I: IntoIterator<Item = T>, T: Sparkly>(iter: I) -> Doc {
Doc::from(DocInner::Line(None)).join(iter)
}
pub fn nbsp() -> Doc {
Doc::from(" ")
}
pub fn space() -> Doc {
Doc::line_or(" ")
}
pub fn split_point() -> Doc {
Doc::line_or("")
}
pub fn text<T: ToString>(t: T, sty: Style) -> Doc {
Doc::from(t.to_string()).style(sty)
}
}
impl Doc {
pub fn append(self, right: Doc) -> Doc {
Doc::from(DocInner::Append(
Box::new(self.inner),
Box::new(right.inner),
))
}
pub fn bracket(self, l: &'static str, r: &'static str) -> Doc {
Doc::from(l)
.append(Doc::split_point())
.append(self)
.nest(4)
.append(Doc::split_point())
.append(Doc::from(r))
.group()
}
pub fn group(self) -> Doc {
let inner = self.inner;
Doc::from(DocInner::Alt(
Box::new(inner.clone().flatten()),
Box::new(inner),
))
}
pub fn join<I: IntoIterator<Item = T>, T: Sparkly>(self, iter: I) -> Doc {
iter.into_iter()
.map(|t| t.to_doc())
.fold1(|l, r| l.append(self.clone()).append(r))
.unwrap_or_else(Doc::empty)
}
pub fn nest(self, n: usize) -> Doc {
Doc::from(DocInner::Nest(n, Box::new(self.inner)))
}
pub fn style(self, style: Style) -> Doc {
Doc::from(DocInner::Style(style, Box::new(self.inner)))
}
}
impl DocInner {
fn flatten(self) -> DocInner {
match self {
DocInner::Append(l, r) => {
let l = l.flatten();
let r = r.flatten();
DocInner::Append(Box::new(l), Box::new(r))
}
DocInner::Nest(_, x) => x.flatten(),
DocInner::Text(s) => DocInner::Text(s),
DocInner::Line(Some(s)) => DocInner::Text(s.into()),
DocInner::Line(None) => DocInner::Line(None),
DocInner::Alt(x, _) => x.flatten(),
DocInner::Style(s, d) => DocInner::Style(s, Box::new(d.flatten())),
}
}
}
impl From<&'static str> for Doc {
fn from(s: &'static str) -> Doc {
let inner = if s.contains('\n') {
let parts = s.rsplit('\n');
let mut doc = Doc::empty().inner;
for s in parts {
doc = DocInner::Append(
Box::new(DocInner::Append(
Box::new(DocInner::Text(s.into())),
Box::new(DocInner::Line(None)),
)),
Box::new(doc),
);
}
doc
} else {
DocInner::Text(s.into())
};
Doc { inner }
}
}
impl From<String> for Doc {
fn from(s: String) -> Doc {
let inner = if s.contains('\n') {
let parts = s.rsplit('\n');
let mut doc = Doc::empty().inner;
for s in parts {
doc = DocInner::Append(
Box::new(DocInner::Append(
Box::new(DocInner::Text(s.to_string().into())),
Box::new(DocInner::Line(None)),
)),
Box::new(doc),
);
}
doc
} else {
DocInner::Text(s.to_string().into())
};
Doc { inner }
}
}
impl FromIterator<Doc> for Doc {
fn from_iter<T: IntoIterator<Item = Doc>>(iter: T) -> Doc {
let mut inner = Doc::empty().inner;
for d in iter {
inner = DocInner::Append(Box::new(inner), Box::new(d.inner));
}
Doc::from(inner)
}
}