use super::transformer::Transformer;
use crate::ast::*;
pub trait Transform {
fn transform_text<F>(self, f: F) -> Self
where
F: Fn(String) -> String;
fn transform_image_urls<F>(self, f: F) -> Self
where
F: Fn(String) -> String;
fn transform_link_urls<F>(self, f: F) -> Self
where
F: Fn(String) -> String;
fn transform_autolink_urls<F>(self, f: F) -> Self
where
F: Fn(String) -> String;
fn transform_code<F>(self, f: F) -> Self
where
F: Fn(String) -> String;
fn transform_html<F>(self, f: F) -> Self
where
F: Fn(String) -> String;
fn transform_with<T: Transformer>(self, transformer: T) -> Self;
fn transform_if_doc<P, F>(self, predicate: P, transform: F) -> Self
where
P: Fn(&Self) -> bool,
F: FnOnce(Self) -> Self,
Self: Sized;
}
impl Transform for Document {
fn transform_text<F>(self, f: F) -> Self
where
F: Fn(String) -> String,
{
let mut transformer = TextTransformer::new(f);
transformer.transform_document(self)
}
fn transform_image_urls<F>(self, f: F) -> Self
where
F: Fn(String) -> String,
{
let mut transformer = ImageUrlTransformer::new(f);
transformer.transform_document(self)
}
fn transform_link_urls<F>(self, f: F) -> Self
where
F: Fn(String) -> String,
{
let mut transformer = LinkUrlTransformer::new(f);
transformer.transform_document(self)
}
fn transform_autolink_urls<F>(self, f: F) -> Self
where
F: Fn(String) -> String,
{
let mut transformer = AutolinkTransformer::new(f);
transformer.transform_document(self)
}
fn transform_code<F>(self, f: F) -> Self
where
F: Fn(String) -> String,
{
let mut transformer = CodeTransformer::new(f);
transformer.transform_document(self)
}
fn transform_html<F>(self, f: F) -> Self
where
F: Fn(String) -> String,
{
let mut transformer = HtmlTransformer::new(f);
transformer.transform_document(self)
}
fn transform_with<T: Transformer>(self, mut transformer: T) -> Self {
transformer.transform_document(self)
}
fn transform_if_doc<P, F>(self, predicate: P, transform: F) -> Self
where
P: Fn(&Self) -> bool,
F: FnOnce(Self) -> Self,
{
if predicate(&self) {
transform(self)
} else {
self
}
}
}
struct TextTransformer<F> {
func: F,
}
impl<F> TextTransformer<F>
where
F: Fn(String) -> String,
{
fn new(func: F) -> Self {
Self { func }
}
}
impl<F> Transformer for TextTransformer<F>
where
F: Fn(String) -> String,
{
fn transform_inline(&mut self, inline: Inline) -> Inline {
match inline {
Inline::Text(text) => Inline::Text((self.func)(text)),
other => self.walk_transform_inline(other),
}
}
}
struct ImageUrlTransformer<F> {
func: F,
}
impl<F> ImageUrlTransformer<F>
where
F: Fn(String) -> String,
{
fn new(func: F) -> Self {
Self { func }
}
}
impl<F> Transformer for ImageUrlTransformer<F>
where
F: Fn(String) -> String,
{
fn transform_inline(&mut self, inline: Inline) -> Inline {
match inline {
Inline::Image(mut image) => {
image.destination = (self.func)(image.destination);
Inline::Image(image)
}
other => self.walk_transform_inline(other),
}
}
}
struct LinkUrlTransformer<F> {
func: F,
}
impl<F> LinkUrlTransformer<F>
where
F: Fn(String) -> String,
{
fn new(func: F) -> Self {
Self { func }
}
}
impl<F> Transformer for LinkUrlTransformer<F>
where
F: Fn(String) -> String,
{
fn transform_inline(&mut self, inline: Inline) -> Inline {
match inline {
Inline::Link(mut link) => {
link.destination = (self.func)(link.destination);
link.children = link
.children
.into_iter()
.map(|child| self.transform_inline(child))
.collect();
Inline::Link(link)
}
other => self.walk_transform_inline(other),
}
}
}
struct AutolinkTransformer<F> {
func: F,
}
impl<F> AutolinkTransformer<F>
where
F: Fn(String) -> String,
{
fn new(func: F) -> Self {
Self { func }
}
}
impl<F> Transformer for AutolinkTransformer<F>
where
F: Fn(String) -> String,
{
fn transform_inline(&mut self, inline: Inline) -> Inline {
match inline {
Inline::Autolink(url) => Inline::Autolink((self.func)(url)),
other => self.walk_transform_inline(other),
}
}
}
struct CodeTransformer<F> {
func: F,
}
impl<F> CodeTransformer<F>
where
F: Fn(String) -> String,
{
fn new(func: F) -> Self {
Self { func }
}
}
impl<F> Transformer for CodeTransformer<F>
where
F: Fn(String) -> String,
{
fn transform_inline(&mut self, inline: Inline) -> Inline {
match inline {
Inline::Code(code) => Inline::Code((self.func)(code)),
other => self.walk_transform_inline(other),
}
}
}
struct HtmlTransformer<F> {
func: F,
}
impl<F> HtmlTransformer<F>
where
F: Fn(String) -> String,
{
fn new(func: F) -> Self {
Self { func }
}
}
impl<F> Transformer for HtmlTransformer<F>
where
F: Fn(String) -> String,
{
fn transform_inline(&mut self, inline: Inline) -> Inline {
match inline {
Inline::Html(html) => Inline::Html((self.func)(html)),
other => self.walk_transform_inline(other),
}
}
fn transform_block(&mut self, block: Block) -> Block {
match block {
Block::HtmlBlock(html) => Block::HtmlBlock((self.func)(html)),
other => self.walk_transform_block(other),
}
}
}
pub trait FilterTransform {
fn remove_empty_paragraphs(self) -> Self;
fn remove_empty_text(self) -> Self;
fn normalize_whitespace(self) -> Self;
fn filter_blocks<F>(self, predicate: F) -> Self
where
F: Fn(&Block) -> bool;
}
impl FilterTransform for Document {
fn remove_empty_paragraphs(mut self) -> Self {
self.blocks
.retain(|block| !matches!(block, Block::Paragraph(inlines) if inlines.is_empty()));
self
}
fn remove_empty_text(self) -> Self {
let mut transformer = EmptyTextRemover;
transformer.transform_document(self)
}
fn normalize_whitespace(self) -> Self {
self.transform_text(|text| text.split_whitespace().collect::<Vec<_>>().join(" "))
}
fn filter_blocks<F>(mut self, predicate: F) -> Self
where
F: Fn(&Block) -> bool,
{
self.blocks.retain(|block| predicate(block));
self
}
}
struct EmptyTextRemover;
impl Transformer for EmptyTextRemover {
fn transform_inline(&mut self, inline: Inline) -> Inline {
match inline {
Inline::Text(text) if text.trim().is_empty() => Inline::Empty,
other => self.walk_transform_inline(other),
}
}
}