use crate::grammar::parse_tree::Span;
use crate::message::horiz::Horiz;
use crate::message::indent::Indent;
use crate::message::styled::Styled;
use crate::message::text::Text;
use crate::message::vert::Vert;
use crate::message::wrap::Wrap;
use crate::message::{Content, Message};
use crate::style::Style;
pub struct MessageBuilder {
span: Span,
heading: Option<Box<dyn Content>>,
body: Option<Box<dyn Content>>,
}
pub struct HeadingCharacter {
message: MessageBuilder,
}
pub struct BodyCharacter {
message: MessageBuilder,
}
impl MessageBuilder {
pub fn new(span: Span) -> Self {
MessageBuilder {
span,
heading: None,
body: None,
}
}
pub fn heading(self) -> Builder<HeadingCharacter> {
Builder::new(HeadingCharacter { message: self })
}
pub fn body(self) -> Builder<BodyCharacter> {
Builder::new(BodyCharacter { message: self })
}
pub fn end(self) -> Message {
Message::new(
self.span,
self.heading.expect("never defined a heading"),
self.body.expect("never defined a body"),
)
}
}
impl Character for HeadingCharacter {
type End = MessageBuilder;
fn end(mut self, items: Vec<Box<dyn Content>>) -> MessageBuilder {
assert!(
self.message.heading.is_none(),
"already defined a heading for this message"
);
self.message.heading = Some(Box::new(Vert::new(items, 1)));
self.message
}
}
impl Character for BodyCharacter {
type End = MessageBuilder;
fn end(mut self, items: Vec<Box<dyn Content>>) -> MessageBuilder {
assert!(
self.message.body.is_none(),
"already defined a body for this message"
);
self.message.body = Some(Box::new(Vert::new(items, 2)));
self.message
}
}
pub struct InlineBuilder;
impl InlineBuilder {
pub fn new() -> Builder<InlineBuilder> {
Builder::new(InlineBuilder)
}
}
impl Character for InlineBuilder {
type End = Box<dyn Content>;
fn end(self, mut items: Vec<Box<dyn Content>>) -> Box<dyn Content> {
if items.len() == 1 {
items.pop().unwrap()
} else {
Box::new(Horiz::new(items, 1))
}
}
}
pub struct Builder<C: Character> {
items: Vec<Box<dyn Content>>,
character: C,
}
impl<C: Character> Builder<C> {
fn new(character: C) -> Self {
Builder {
items: vec![],
character,
}
}
pub fn push<I>(mut self, item: I) -> Self
where
I: Into<Box<dyn Content>>,
{
self.items.push(item.into());
self
}
fn pop(&mut self) -> Option<Box<dyn Content>> {
self.items.pop()
}
pub fn begin_vert(self, separate: usize) -> Builder<VertCharacter<C>> {
Builder::new(VertCharacter {
base: self,
separate,
})
}
pub fn begin_lines(self) -> Builder<VertCharacter<C>> {
self.begin_vert(1)
}
pub fn begin_horiz(self, separate: usize) -> Builder<HorizCharacter<C>> {
Builder::new(HorizCharacter {
base: self,
separate,
})
}
pub fn begin_adjacent(self) -> Builder<HorizCharacter<C>> {
self.begin_horiz(1)
}
pub fn begin_spaced(self) -> Builder<HorizCharacter<C>> {
self.begin_horiz(2)
}
pub fn begin_wrap(self) -> Builder<WrapCharacter<C>> {
Builder::new(WrapCharacter { base: self })
}
pub fn styled(mut self, style: Style) -> Self {
let content = self.pop().expect("bold must be applied to an item");
self.push(Box::new(Styled::new(style, content)))
}
pub fn indented_by(mut self, amount: usize) -> Self {
let content = self.pop().expect("indent must be applied to an item");
self.push(Box::new(Indent::new(amount, content)))
}
pub fn indented(self) -> Self {
self.indented_by(2)
}
pub fn text<T: ToString>(self, text: T) -> Self {
self.push(Box::new(Text::new(text.to_string())))
}
pub fn adjacent_text<T: ToString, U: ToString>(mut self, prefix: T, suffix: U) -> Self {
let item = self.pop().expect("adjacent text must be added to an item");
let prefix = prefix.to_string();
let suffix = suffix.to_string();
if !prefix.is_empty() && !suffix.is_empty() {
self.begin_adjacent()
.text(prefix)
.push(item)
.text(suffix)
.end()
} else if !suffix.is_empty() {
self.begin_adjacent().push(item).text(suffix).end()
} else if !prefix.is_empty() {
self.begin_adjacent().text(prefix).push(item).end()
} else {
self.push(item)
}
}
pub fn verbatimed(self) -> Self {
self.adjacent_text("`", "`")
}
pub fn punctuated<T: ToString>(self, text: T) -> Self {
self.adjacent_text("", text)
}
pub fn wrap_text<T: ToString>(self, text: T) -> Self {
self.begin_wrap().text(text).end()
}
pub fn end(self) -> C::End {
self.character.end(self.items)
}
}
pub trait Character {
type End;
fn end(self, items: Vec<Box<dyn Content>>) -> Self::End;
}
pub struct HorizCharacter<C: Character> {
base: Builder<C>,
separate: usize,
}
impl<C: Character> Character for HorizCharacter<C> {
type End = Builder<C>;
fn end(self, items: Vec<Box<dyn Content>>) -> Builder<C> {
self.base.push(Box::new(Horiz::new(items, self.separate)))
}
}
pub struct VertCharacter<C: Character> {
base: Builder<C>,
separate: usize,
}
impl<C: Character> Character for VertCharacter<C> {
type End = Builder<C>;
fn end(self, items: Vec<Box<dyn Content>>) -> Builder<C> {
self.base.push(Box::new(Vert::new(items, self.separate)))
}
}
pub struct WrapCharacter<C: Character> {
base: Builder<C>,
}
impl<C: Character> Character for WrapCharacter<C> {
type End = Builder<C>;
fn end(self, items: Vec<Box<dyn Content>>) -> Builder<C> {
self.base.push(Box::new(Wrap::new(items)))
}
}
impl<T> From<Box<T>> for Box<dyn Content>
where
T: Content + 'static,
{
fn from(b: Box<T>) -> Box<dyn Content> {
b
}
}