markdown_ppp/ast_transform/
pipeline.rs1use super::transformer::Transformer;
27use crate::ast::*;
28
29pub struct TransformPipeline {
49 steps: Vec<Box<dyn FnOnce(Document) -> Document>>,
50}
51
52impl TransformPipeline {
53 pub fn new() -> Self {
55 Self { steps: Vec::new() }
56 }
57
58 pub fn transform_text<F>(mut self, f: F) -> Self
60 where
61 F: Fn(String) -> String + 'static,
62 {
63 self.steps.push(Box::new(move |doc| {
64 crate::ast_transform::Transform::transform_text(doc, f)
65 }));
66 self
67 }
68
69 pub fn transform_image_urls<F>(mut self, f: F) -> Self
71 where
72 F: Fn(String) -> String + 'static,
73 {
74 self.steps.push(Box::new(move |doc| {
75 crate::ast_transform::Transform::transform_image_urls(doc, f)
76 }));
77 self
78 }
79
80 pub fn transform_link_urls<F>(mut self, f: F) -> Self
82 where
83 F: Fn(String) -> String + 'static,
84 {
85 self.steps.push(Box::new(move |doc| {
86 crate::ast_transform::Transform::transform_link_urls(doc, f)
87 }));
88 self
89 }
90
91 pub fn transform_autolink_urls<F>(mut self, f: F) -> Self
93 where
94 F: Fn(String) -> String + 'static,
95 {
96 self.steps.push(Box::new(move |doc| {
97 crate::ast_transform::Transform::transform_autolink_urls(doc, f)
98 }));
99 self
100 }
101
102 pub fn transform_code<F>(mut self, f: F) -> Self
104 where
105 F: Fn(String) -> String + 'static,
106 {
107 self.steps.push(Box::new(move |doc| {
108 crate::ast_transform::Transform::transform_code(doc, f)
109 }));
110 self
111 }
112
113 pub fn transform_html<F>(mut self, f: F) -> Self
115 where
116 F: Fn(String) -> String + 'static,
117 {
118 self.steps.push(Box::new(move |doc| {
119 crate::ast_transform::Transform::transform_html(doc, f)
120 }));
121 self
122 }
123
124 pub fn transform_with<T: Transformer + 'static>(mut self, transformer: T) -> Self {
126 self.steps.push(Box::new(move |doc| {
127 crate::ast_transform::Transform::transform_with(doc, transformer)
128 }));
129 self
130 }
131
132 pub fn custom<F>(mut self, f: F) -> Self
134 where
135 F: FnOnce(Document) -> Document + 'static,
136 {
137 self.steps.push(Box::new(f));
138 self
139 }
140
141 pub fn when<F>(mut self, condition: bool, builder: F) -> Self
143 where
144 F: FnOnce(TransformPipeline) -> TransformPipeline,
145 {
146 if condition {
147 let sub_pipeline = builder(TransformPipeline::new());
148 self.steps
149 .push(Box::new(move |doc| sub_pipeline.apply(doc)));
150 }
151 self
152 }
153
154 pub fn when_doc<P, F>(mut self, predicate: P, builder: F) -> Self
156 where
157 P: Fn(&Document) -> bool + 'static,
158 F: FnOnce(TransformPipeline) -> TransformPipeline + 'static,
159 {
160 self.steps.push(Box::new(move |doc| {
161 if predicate(&doc) {
162 let sub_pipeline = builder(TransformPipeline::new());
163 sub_pipeline.apply(doc)
164 } else {
165 doc
166 }
167 }));
168 self
169 }
170
171 pub fn remove_empty_paragraphs(mut self) -> Self {
173 self.steps.push(Box::new(|doc| {
174 crate::ast_transform::FilterTransform::remove_empty_paragraphs(doc)
175 }));
176 self
177 }
178
179 pub fn remove_empty_text(mut self) -> Self {
181 self.steps.push(Box::new(|doc| {
182 crate::ast_transform::FilterTransform::remove_empty_text(doc)
183 }));
184 self
185 }
186
187 pub fn normalize_whitespace(mut self) -> Self {
189 self.steps.push(Box::new(|doc| {
190 crate::ast_transform::FilterTransform::normalize_whitespace(doc)
191 }));
192 self
193 }
194
195 pub fn filter_blocks<F>(mut self, predicate: F) -> Self
197 where
198 F: Fn(&Block) -> bool + 'static,
199 {
200 self.steps.push(Box::new(move |doc| {
201 crate::ast_transform::FilterTransform::filter_blocks(doc, predicate)
202 }));
203 self
204 }
205
206 pub fn apply(self, mut doc: Document) -> Document {
208 for step in self.steps {
209 doc = step(doc);
210 }
211 doc
212 }
213}
214
215impl Default for TransformPipeline {
216 fn default() -> Self {
217 Self::new()
218 }
219}
220
221pub trait PipeExt {
223 fn pipe<F, R>(self, f: F) -> R
225 where
226 F: FnOnce(Self) -> R,
227 Self: Sized,
228 {
229 f(self)
230 }
231
232 fn compose<F, G, R>(self, f: F, g: G) -> R
234 where
235 F: FnOnce(Self) -> R,
236 G: FnOnce(R) -> R,
237 Self: Sized,
238 {
239 g(f(self))
240 }
241}
242
243impl PipeExt for Document {}
244
245#[macro_export]
265macro_rules! pipeline {
266 ($doc:expr => $($transform:expr),* $(,)?) => {{
267 let mut doc = $doc;
268 $(
269 doc = $transform(doc);
270 )*
271 doc
272 }};
273}
274
275pub use pipeline;