Skip to content

Commit

Permalink
Add a lifetime annotation to the Postprocesor type
Browse files Browse the repository at this point in the history
This lets the compiler reason about the lifetimes of objects used by the
postprocessor, if the callback captures variables.

See #175
  • Loading branch information
rsesek authored and zoni committed Sep 25, 2023
1 parent c27d7b9 commit cd5dbf6
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 4 deletions.
8 changes: 4 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,8 @@ pub type MarkdownEvents<'a> = Vec<Event<'a>>;
/// # exporter.run().unwrap();
/// ```
pub type Postprocessor =
dyn Fn(&mut Context, &mut MarkdownEvents) -> PostprocessorResult + Send + Sync;
pub type Postprocessor<'f> =
dyn Fn(&mut Context, &mut MarkdownEvents) -> PostprocessorResult + Send + Sync + 'f;
type Result<T, E = ExportError> = std::result::Result<T, E>;

const PERCENTENCODE_CHARS: &AsciiSet = &CONTROLS.add(b' ').add(b'(').add(b')').add(b'%').add(b'?');
Expand Down Expand Up @@ -231,8 +231,8 @@ pub struct Exporter<'a> {
vault_contents: Option<Vec<PathBuf>>,
walk_options: WalkOptions<'a>,
process_embeds_recursively: bool,
postprocessors: Vec<&'a Postprocessor>,
embed_postprocessors: Vec<&'a Postprocessor>,
postprocessors: Vec<&'a Postprocessor<'a>>,
embed_postprocessors: Vec<&'a Postprocessor<'a>>,
}

impl<'a> fmt::Debug for Exporter<'a> {
Expand Down
34 changes: 34 additions & 0 deletions tests/postprocessors_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ use obsidian_export::{Context, Exporter, MarkdownEvents, PostprocessorResult};
use pretty_assertions::assert_eq;
use pulldown_cmark::{CowStr, Event};
use serde_yaml::Value;
use std::collections::HashSet;
use std::fs::{read_to_string, remove_file};
use std::path::PathBuf;
use std::sync::Mutex;
use tempfile::TempDir;

/// This postprocessor replaces any instance of "foo" with "bar" in the note body.
Expand Down Expand Up @@ -105,6 +107,38 @@ fn test_postprocessor_change_destination() {
assert!(new_note_path.exists());
}

// Ensure postprocessor type definition has proper lifetimes to allow state (here: `parents`)
// to be passed in. Otherwise, this fails with an error like:
// error[E0597]: `parents` does not live long enough
// cast requires that `parents` is borrowed for `'static`
#[test]
fn test_postprocessor_stateful_callback() {
let tmp_dir = TempDir::new().expect("failed to make tempdir");
let mut exporter = Exporter::new(
PathBuf::from("tests/testdata/input/postprocessors"),
tmp_dir.path().to_path_buf(),
);

let parents: Mutex<HashSet<PathBuf>> = Default::default();
let callback = |ctx: &mut Context, _mdevents: &mut MarkdownEvents| -> PostprocessorResult {
parents
.lock()
.unwrap()
.insert(ctx.destination.parent().unwrap().to_path_buf());
PostprocessorResult::Continue
};
exporter.add_postprocessor(&callback);

exporter.run().unwrap();

let expected = tmp_dir.path().clone();

let parents = parents.lock().unwrap();
println!("{:?}", parents);
assert_eq!(1, parents.len());
assert!(parents.contains(expected));
}

// The purpose of this test to verify the `append_frontmatter` postprocessor is called to extend
// the frontmatter, and the `foo_to_bar` postprocessor is called to replace instances of "foo" with
// "bar" (only in the note body).
Expand Down

0 comments on commit cd5dbf6

Please sign in to comment.