Change the Postprocessor type from an alias to a trait

This creates a parallel EmbedPostprocessor trait, and it adds new
methods for registering trait objects into the postprocessor chains. The
trait is implemented for the callback Fn type that was previously
declared as the Postprocessor type.
This commit is contained in:
Robert Sesek 2023-10-08 13:59:54 -04:00
parent 7988b21f5d
commit 1fcdd9db85
2 changed files with 54 additions and 48 deletions

View File

@ -133,19 +133,26 @@ pub type MarkdownEvents<'a> = Vec<Event<'a>>;
/// # exporter.run().unwrap(); /// # exporter.run().unwrap();
/// ``` /// ```
pub type Postprocessor<'f> =
dyn Fn(&mut Context, &mut MarkdownEvents) -> PostprocessorResult + Send + Sync + 'f;
type Result<T, E = ExportError> = std::result::Result<T, E>; type Result<T, E = ExportError> = std::result::Result<T, E>;
/// Postprocess is a trait form of the [Postprocessor] callback that can be passed to /// Postprocessor that can be that can be passed to [Exporter::add_postprocessor_impl].
/// [Exporter::add_postprocessor_impl]. pub trait Postprocessor: Send + Sync {
pub trait Postprocess: Send + Sync {
fn postprocess(&self, ctx: &mut Context, events: &mut MarkdownEvents) -> PostprocessorResult; fn postprocess(&self, ctx: &mut Context, events: &mut MarkdownEvents) -> PostprocessorResult;
} }
/// EmbedPostprocess is a trait form of the [Postprocessor] callback that can be /// Postprocessor is implemented for any callback function type that matches the
/// passed to [Exporter::add_embed_postprocessor_impl]. /// signature.
pub trait EmbedPostprocess: Send + Sync { impl<F: Fn(&mut Context, &mut MarkdownEvents) -> PostprocessorResult + Send + Sync> Postprocessor
for F
{
fn postprocess(&self, ctx: &mut Context, events: &mut MarkdownEvents) -> PostprocessorResult {
self(ctx, events)
}
}
/// EmbedPostprocessor is like [Postprocessor] but for note embeds, and it is passed to
/// [Exporter::add_embed_postprocessor_impl].
pub trait EmbedPostprocessor: Send + Sync {
fn embed_postprocess( fn embed_postprocess(
&self, &self,
ctx: &mut Context, ctx: &mut Context,
@ -153,6 +160,18 @@ pub trait EmbedPostprocess: Send + Sync {
) -> PostprocessorResult; ) -> PostprocessorResult;
} }
impl<F: Fn(&mut Context, &mut MarkdownEvents) -> PostprocessorResult + Send + Sync>
EmbedPostprocessor for F
{
fn embed_postprocess(
&self,
ctx: &mut Context,
events: &mut MarkdownEvents,
) -> PostprocessorResult {
self(ctx, events)
}
}
const PERCENTENCODE_CHARS: &AsciiSet = &CONTROLS.add(b' ').add(b'(').add(b')').add(b'%').add(b'?'); const PERCENTENCODE_CHARS: &AsciiSet = &CONTROLS.add(b' ').add(b'(').add(b')').add(b'%').add(b'?');
const NOTE_RECURSION_LIMIT: usize = 10; const NOTE_RECURSION_LIMIT: usize = 10;
@ -232,23 +251,6 @@ pub enum PostprocessorResult {
StopAndSkipNote, StopAndSkipNote,
} }
#[derive(Clone)]
enum PostprocessorRef<'p> {
Function(&'p Postprocessor<'p>),
Trait(&'p dyn Postprocess),
EmbedTrait(&'p dyn EmbedPostprocess),
}
impl<'p> PostprocessorRef<'p> {
fn call(&'p self, ctx: &mut Context, events: &mut MarkdownEvents) -> PostprocessorResult {
match self {
PostprocessorRef::Function(f) => f(ctx, events),
PostprocessorRef::Trait(t) => t.postprocess(ctx, events),
PostprocessorRef::EmbedTrait(t) => t.embed_postprocess(ctx, events),
}
}
}
#[derive(Clone)] #[derive(Clone)]
/// Exporter provides the main interface to this library. /// Exporter provides the main interface to this library.
/// ///
@ -264,8 +266,8 @@ pub struct Exporter<'a> {
vault_contents: Option<Vec<PathBuf>>, vault_contents: Option<Vec<PathBuf>>,
walk_options: WalkOptions<'a>, walk_options: WalkOptions<'a>,
process_embeds_recursively: bool, process_embeds_recursively: bool,
postprocessors: Vec<PostprocessorRef<'a>>, postprocessors: Vec<&'a dyn Postprocessor>,
embed_postprocessors: Vec<PostprocessorRef<'a>>, embed_postprocessors: Vec<&'a dyn EmbedPostprocessor>,
} }
impl<'a> fmt::Debug for Exporter<'a> { impl<'a> fmt::Debug for Exporter<'a> {
@ -347,34 +349,38 @@ impl<'a> Exporter<'a> {
} }
/// Append a function to the chain of [postprocessors][Postprocessor] to run on exported Obsidian Markdown notes. /// Append a function to the chain of [postprocessors][Postprocessor] to run on exported Obsidian Markdown notes.
pub fn add_postprocessor(&mut self, processor: &'a Postprocessor) -> &mut Exporter<'a> { pub fn add_postprocessor(
self.postprocessors &mut self,
.push(PostprocessorRef::Function(processor)); processor: &'a (impl Fn(&mut Context, &mut MarkdownEvents) -> PostprocessorResult + Send + Sync),
) -> &mut Exporter<'a> {
self.postprocessors.push(processor);
self self
} }
/// Append a trait implementation of [Postprocess] to the chain of [postprocessors] to run on /// Append a trait object to the chain of [postprocessors] to run on Obsidian Markdown notes.
/// Obsidian Markdown notes. pub fn add_postprocessor_impl(
pub fn add_postprocessor_impl(&mut self, processor: &'a dyn Postprocess) -> &mut Exporter<'a> { &mut self,
self.postprocessors.push(PostprocessorRef::Trait(processor)); processor: &'a dyn Postprocessor,
) -> &mut Exporter<'a> {
self.postprocessors.push(processor);
self self
} }
/// Append a function to the chain of [postprocessors][Postprocessor] for embeds. /// Append a function to the chain of [postprocessors][EmbedPostprocessor] for embeds.
pub fn add_embed_postprocessor(&mut self, processor: &'a Postprocessor) -> &mut Exporter<'a> { pub fn add_embed_postprocessor(
self.embed_postprocessors &mut self,
.push(PostprocessorRef::Function(processor)); processor: &'a (impl Fn(&mut Context, &mut MarkdownEvents) -> PostprocessorResult + Send + Sync),
) -> &mut Exporter<'a> {
self.embed_postprocessors.push(processor);
self self
} }
/// Append a trait implementation of [EmbedPostprocess] to the chain of [postprocessors] for /// Append a trait object to the chain of [postprocessors] for embeds.
/// embeds.
pub fn add_embed_postprocessor_impl( pub fn add_embed_postprocessor_impl(
&mut self, &mut self,
processor: &'a dyn EmbedPostprocess, processor: &'a dyn EmbedPostprocessor,
) -> &mut Exporter<'a> { ) -> &mut Exporter<'a> {
self.embed_postprocessors self.embed_postprocessors.push(processor);
.push(PostprocessorRef::EmbedTrait(processor));
self self
} }
@ -454,7 +460,7 @@ impl<'a> Exporter<'a> {
let (frontmatter, mut markdown_events) = self.parse_obsidian_note(src, &context)?; let (frontmatter, mut markdown_events) = self.parse_obsidian_note(src, &context)?;
context.frontmatter = frontmatter; context.frontmatter = frontmatter;
for processor in &self.postprocessors { for processor in &self.postprocessors {
match processor.call(&mut context, &mut markdown_events) { match processor.postprocess(&mut context, &mut markdown_events) {
PostprocessorResult::StopHere => break, PostprocessorResult::StopHere => break,
PostprocessorResult::StopAndSkipNote => return Ok(()), PostprocessorResult::StopAndSkipNote => return Ok(()),
PostprocessorResult::Continue => (), PostprocessorResult::Continue => (),
@ -659,7 +665,7 @@ impl<'a> Exporter<'a> {
for processor in &self.embed_postprocessors { for processor in &self.embed_postprocessors {
// Postprocessors running on embeds shouldn't be able to change frontmatter (or // Postprocessors running on embeds shouldn't be able to change frontmatter (or
// any other metadata), so we give them a clone of the context. // any other metadata), so we give them a clone of the context.
match processor.call(&mut child_context, &mut events) { match processor.embed_postprocess(&mut child_context, &mut events) {
PostprocessorResult::StopHere => break, PostprocessorResult::StopHere => break,
PostprocessorResult::StopAndSkipNote => { PostprocessorResult::StopAndSkipNote => {
events = vec![]; events = vec![];

View File

@ -1,6 +1,6 @@
use obsidian_export::postprocessors::softbreaks_to_hardbreaks; use obsidian_export::postprocessors::softbreaks_to_hardbreaks;
use obsidian_export::{ use obsidian_export::{
Context, EmbedPostprocess, Exporter, MarkdownEvents, Postprocess, PostprocessorResult, Context, EmbedPostprocessor, Exporter, MarkdownEvents, Postprocessor, PostprocessorResult,
}; };
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use pulldown_cmark::{CowStr, Event}; use pulldown_cmark::{CowStr, Event};
@ -148,7 +148,7 @@ fn test_postprocessor_impl() {
parents: Mutex<HashSet<PathBuf>>, parents: Mutex<HashSet<PathBuf>>,
embeds: Mutex<u32>, embeds: Mutex<u32>,
} }
impl Postprocess for Impl { impl Postprocessor for Impl {
fn postprocess( fn postprocess(
&self, &self,
ctx: &mut Context, ctx: &mut Context,
@ -161,7 +161,7 @@ fn test_postprocessor_impl() {
PostprocessorResult::Continue PostprocessorResult::Continue
} }
} }
impl EmbedPostprocess for Impl { impl EmbedPostprocessor for Impl {
fn embed_postprocess( fn embed_postprocess(
&self, &self,
_ctx: &mut Context, _ctx: &mut Context,