Add construct method for conditionial renderers

feature/processing-pipeline
trivernis 1 year ago
parent a2eeb4fe73
commit 6178a045e7
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

@ -11,6 +11,12 @@ pub struct Parallel<S: ProcessingStep>(S);
/// An adapter to map the result of the pipeline /// An adapter to map the result of the pipeline
pub struct Map<S: ProcessingStep, T: Send + Sync>(S, Box<dyn Fn(S::Output) -> T + Send + Sync>); pub struct Map<S: ProcessingStep, T: Send + Sync>(S, Box<dyn Fn(S::Output) -> T + Send + Sync>);
/// An adapter to dynamically construct the next step mapper depending on the previous one
pub struct Construct<S1: ProcessingStep, S2: ProcessingStep<Input = T>, T>(
S1,
Box<dyn Fn(S1::Output) -> (T, S2) + Send + Sync>,
);
/// A generic wrapper for processing pipelines /// A generic wrapper for processing pipelines
pub struct ProcessingPipeline<I: Send + Sync, O: Send + Sync>( pub struct ProcessingPipeline<I: Send + Sync, O: Send + Sync>(
Box<dyn ProcessingStep<Input = I, Output = O>>, Box<dyn ProcessingStep<Input = I, Output = O>>,
@ -87,10 +93,41 @@ impl<S: ProcessingStep, T: Send + Sync> ProcessingStep for Map<S, T> {
async fn process(&self, input: Self::Input) -> Result<Self::Output> { async fn process(&self, input: Self::Input) -> Result<Self::Output> {
let inner_result = self.0.process(input).await?; let inner_result = self.0.process(input).await?;
Ok(self.1(inner_result)) Ok(self.1(inner_result))
} }
} }
pub trait ProcessingConstruct: ProcessingStep + Sized {
fn construct<
F: Fn(Self::Output) -> (T, S) + Send + Sync + 'static,
S: ProcessingStep<Input = T>,
T: Send + Sync,
>(
self,
construct_fn: F,
) -> Construct<Self, S, T> {
Construct(self, Box::new(construct_fn))
}
}
impl<S: ProcessingStep> ProcessingConstruct for S {}
#[async_trait]
impl<S1: ProcessingStep, S2: ProcessingStep<Input = T>, T: Send + Sync> ProcessingStep
for Construct<S1, S2, T>
{
type Input = S1::Input;
type Output = S2::Output;
async fn process(&self, input: Self::Input) -> Result<Self::Output> {
let inner_output = self.0.process(input).await?;
let (new_input, step) = self.1(inner_output);
step.process(new_input).await
}
}
#[async_trait] #[async_trait]
impl<I: Send + Sync, O: Send + Sync> ProcessingStep for ProcessingPipeline<I, O> { impl<I: Send + Sync, O: Send + Sync> ProcessingStep for ProcessingPipeline<I, O> {
type Input = I; type Input = I;

@ -10,7 +10,7 @@ pub struct LoadDirContent;
#[async_trait] #[async_trait]
impl ProcessingStep for LoadDirContent { impl ProcessingStep for LoadDirContent {
type Input = FolderData; type Input = FolderData;
type Output = Vec<(PathBuf, String)>; type Output = (Vec<PathBuf>, String);
#[tracing::instrument(name = "load dir", level = "trace", skip_all)] #[tracing::instrument(name = "load dir", level = "trace", skip_all)]
async fn process(&self, input: Self::Input) -> Result<Self::Output> { async fn process(&self, input: Self::Input) -> Result<Self::Output> {
@ -27,10 +27,6 @@ impl ProcessingStep for LoadDirContent {
.to_owned() .to_owned()
.unwrap_or(dir_name.into()); .unwrap_or(dir_name.into());
Ok(input Ok((input.pages, default_template))
.pages
.into_iter()
.map(|p| (p, default_template.clone()))
.collect())
} }
} }

@ -49,19 +49,25 @@ impl ContentRenderer {
} }
let mut tera = Tera::new(&self.template_glob).into_diagnostic()?; let mut tera = Tera::new(&self.template_glob).into_diagnostic()?;
super::processors::register_all(&mut tera); super::processors::register_all(&mut tera);
let out_dir = self.ctx.dirs.output_dir.to_owned(); let out_dir = self.ctx.dirs.output_dir.to_owned();
let styles = Arc::clone(&self.styles);
let ctx = Arc::clone(&self.ctx);
LoadDirContent LoadDirContent
.chain( .construct(move |(files, default_template)| {
RenderPage { let step = RenderPage {
tera, tera: tera.clone(),
styles: self.styles.clone(), styles: styles.clone(),
ctx: self.ctx.clone(), ctx: ctx.clone(),
default_template,
} }
.map(map_path_to_output(out_dir)) .map(map_path_to_output(out_dir.clone()))
.chain(SaveFile) .chain(SaveFile)
.parallel(), .parallel();
)
(files, step)
})
.parallel() .parallel()
.process(dirs) .process(dirs)
.await?; .await?;

@ -13,34 +13,32 @@ pub struct RenderPage {
pub tera: Tera, pub tera: Tera,
pub styles: Arc<Mutex<Stylesheets>>, pub styles: Arc<Mutex<Stylesheets>>,
pub ctx: Arc<Context>, pub ctx: Arc<Context>,
pub default_template: String,
} }
#[async_trait] #[async_trait]
impl ProcessingStep for RenderPage { impl ProcessingStep for RenderPage {
type Input = (PathBuf, String); type Input = PathBuf;
type Output = (PathBuf, String); type Output = (PathBuf, String);
#[tracing::instrument(name = "render page", level = "trace", skip_all)] #[tracing::instrument(name = "render page", level = "trace", skip_all)]
async fn process(&self, (page_path, default_template): Self::Input) -> Result<Self::Output> { async fn process(&self, page_path: Self::Input) -> Result<Self::Output> {
let page = load_page(&page_path).await?; let page = load_page(&page_path).await?;
let mut context = TeraContext::new(); let mut context = TeraContext::new();
let mut template_name = default_template; let mut template_name = None;
let mut style_name = template_name.to_owned();
match page { match page {
crate::data::Page::Data(data) => { crate::data::Page::Data(data) => {
if let Some(tmpl) = data.metadata.template { template_name = data.metadata.template;
template_name = tmpl.to_owned();
style_name = tmpl;
}
context.insert("data", &data.data); context.insert("data", &data.data);
} }
crate::data::Page::Content(content) => context.insert("content", &content), crate::data::Page::Content(content) => context.insert("content", &content),
} }
let template_name = template_name.as_ref().unwrap_or(&self.default_template);
{ {
let mut styles = self.styles.lock().await; let mut styles = self.styles.lock().await;
let style_embed = styles let style_embed = styles
.get_style_embed(&style_name, &self.ctx.dirs.output_dir) .get_style_embed(template_name, &self.ctx.dirs.output_dir)
.await?; .await?;
context.insert("style", &style_embed); context.insert("style", &style_embed);
}; };

Loading…
Cancel
Save