use crate::component::OnceBlock;
use crate::{BlockComponent, Document, IterBlockComponent, Node, Render};
use std::fmt;
pub fn repeat(item: impl fmt::Display, size: usize) -> impl Render {
PadItem(item, size)
}
pub(crate) struct PadItem<T>(pub T, pub usize);
impl<T: fmt::Display> fmt::Display for PadItem<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for _ in 0..(self.1) {
self.0.fmt(f)?;
}
Ok(())
}
}
pub struct Each<U, Iterator: IntoIterator<Item = U>> {
pub items: Iterator,
}
impl<U, Iterator: IntoIterator<Item = U>> IterBlockComponent for Each<U, Iterator> {
type Item = U;
fn append(
self,
mut block: impl FnMut(U, Document) -> Document,
mut document: Document,
) -> Document {
for item in self.items {
document = block(item, document);
}
document
}
}
impl<U, I: IntoIterator<Item = U>> From<I> for Each<U, I> {
fn from(from: I) -> Each<U, I> {
Each { items: from }
}
}
#[allow(non_snake_case)]
pub fn Each<U, I: IntoIterator<Item = U>>(
items: impl Into<Each<U, I>>,
callback: impl Fn(U, Document) -> Document,
) -> impl Render {
IterBlockComponent::with(items.into(), callback)
}
pub struct Section {
pub name: &'static str,
}
impl BlockComponent for Section {
fn append(self, block: impl FnOnce(Document) -> Document, mut document: Document) -> Document {
document = document.add(Node::OpenSection(self.name));
document = block(document);
document = document.add(Node::CloseSection);
document
}
}
#[allow(non_snake_case)]
pub fn Section(name: &'static str, block: impl FnOnce(Document) -> Document) -> Document {
let document = Document::empty();
Section { name }.append(block, document)
}
pub struct Join<U, Iterator: IntoIterator<Item = U>> {
pub iterator: Iterator,
pub joiner: &'static str,
}
impl<U, I: IntoIterator<Item = U>> From<(I, &'static str)> for Join<U, I> {
fn from(from: (I, &'static str)) -> Join<U, I> {
Join {
iterator: from.0,
joiner: from.1,
}
}
}
#[allow(non_snake_case)]
pub fn Join<U, F, Iterator>(join: impl Into<Join<U, Iterator>>, callback: F) -> impl Render
where
F: Fn(U, Document) -> Document,
Iterator: IntoIterator<Item = U>,
{
IterBlockComponent::with(join.into(), callback)
}
impl<'item, U, Iterator> IterBlockComponent for Join<U, Iterator>
where
Iterator: IntoIterator<Item = U>,
{
type Item = U;
fn append(
self,
mut block: impl FnMut(Self::Item, Document) -> Document,
mut into: Document,
) -> Document {
let mut is_first = true;
for item in self.iterator {
if is_first {
is_first = false;
} else {
into = into.add(self.joiner);
}
into = block(item, into);
}
into
}
}
#[allow(non_snake_case)]
pub fn Line(item: impl Render) -> impl Render {
OnceBlock(|document| item.render(document).add_node(Node::Newline))
}
#[cfg(test)]
mod tests {
use crate::helpers::*;
#[test]
fn test_each() -> ::std::io::Result<()> {
struct Point(i32, i32);
let items = &vec![Point(10, 20), Point(5, 10), Point(6, 42)][..];
let document = tree! {
<Each items={items} as |item| {
<Line as {
"Point(" {item.0} "," {item.1} ")"
}>
}>
};
assert_eq!(
document.to_string()?,
"Point(10,20)\nPoint(5,10)\nPoint(6,42)\n"
);
Ok(())
}
#[test]
fn test_join() -> ::std::io::Result<()> {
struct Point(i32, i32);
let items = &vec![Point(10, 20), Point(5, 10), Point(6, 42)][..];
let document = tree! {
<Join iterator={items} joiner={"\n"} as |item| {
"Point(" {item.0} "," {item.1} ")"
}>
};
assert_eq!(
document.to_string()?,
"Point(10,20)\nPoint(5,10)\nPoint(6,42)"
);
Ok(())
}
}