Skip to content

Commit

Permalink
callbacks: constrain to input lifetime
Browse files Browse the repository at this point in the history
  • Loading branch information
liamwhite committed Dec 5, 2024
1 parent 0c6e6e6 commit 36b843a
Show file tree
Hide file tree
Showing 10 changed files with 62 additions and 48 deletions.
10 changes: 5 additions & 5 deletions src/cm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ pub fn format_document_with_plugins<'a>(
Ok(())
}

struct CommonMarkFormatter<'a, 'o> {
struct CommonMarkFormatter<'a, 'o, 'c> {
node: &'a AstNode<'a>,
options: &'o Options,
options: &'o Options<'c>,
v: Vec<u8>,
prefix: Vec<u8>,
column: usize,
Expand All @@ -72,7 +72,7 @@ enum Escaping {
Title,
}

impl<'a, 'o> Write for CommonMarkFormatter<'a, 'o> {
impl<'a, 'o, 'c> Write for CommonMarkFormatter<'a, 'o, 'c> {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.output(buf, false, Escaping::Literal);
Ok(buf.len())
Expand All @@ -83,8 +83,8 @@ impl<'a, 'o> Write for CommonMarkFormatter<'a, 'o> {
}
}

impl<'a, 'o> CommonMarkFormatter<'a, 'o> {
fn new(node: &'a AstNode<'a>, options: &'o Options) -> Self {
impl<'a, 'o, 'c> CommonMarkFormatter<'a, 'o, 'c> {
fn new(node: &'a AstNode<'a>, options: &'o Options<'c>) -> Self {
CommonMarkFormatter {
node,
options,
Expand Down
12 changes: 8 additions & 4 deletions src/html.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,9 @@ impl Anchorizer {
}
}

struct HtmlFormatter<'o> {
struct HtmlFormatter<'o, 'c> {
output: &'o mut WriteWithLast<'o>,
options: &'o Options,
options: &'o Options<'c>,
anchorizer: Anchorizer,
footnote_ix: u32,
written_footnote_ix: u32,
Expand Down Expand Up @@ -323,8 +323,12 @@ where
Ok(())
}

impl<'o> HtmlFormatter<'o> {
fn new(options: &'o Options, output: &'o mut WriteWithLast<'o>, plugins: &'o Plugins) -> Self {
impl<'o, 'c> HtmlFormatter<'o, 'c> {
fn new(
options: &'o Options<'c>,
output: &'o mut WriteWithLast<'o>,
plugins: &'o Plugins,
) -> Self {
HtmlFormatter {
options,
output,
Expand Down
6 changes: 3 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,11 @@ pub use xml::format_document as format_xml;
pub use xml::format_document_with_plugins as format_xml_with_plugins;

/// Legacy naming of [`ExtensionOptions`]
pub type ComrakExtensionOptions = ExtensionOptions;
pub type ComrakExtensionOptions<'c> = ExtensionOptions<'c>;
/// Legacy naming of [`Options`]
pub type ComrakOptions = Options;
pub type ComrakOptions<'c> = Options<'c>;
/// Legacy naming of [`ParseOptions`]
pub type ComrakParseOptions = ParseOptions;
pub type ComrakParseOptions<'c> = ParseOptions<'c>;
/// Legacy naming of [`Plugins`]
pub type ComrakPlugins<'a> = Plugins<'a>;
/// Legacy naming of [`RenderOptions`]
Expand Down
8 changes: 4 additions & 4 deletions src/parser/inlines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ const MAXBACKTICKS: usize = 80;
const MAX_LINK_LABEL_LENGTH: usize = 1000;
const MAX_MATH_DOLLARS: usize = 2;

pub struct Subject<'a: 'd, 'r, 'o, 'd, 'i> {
pub struct Subject<'a: 'd, 'r, 'o, 'd, 'i, 'c> {
pub arena: &'a Arena<AstNode<'a>>,
options: &'o Options,
options: &'o Options<'c>,
pub input: &'i [u8],
line: usize,
pub pos: usize,
Expand Down Expand Up @@ -110,10 +110,10 @@ struct WikilinkComponents<'i> {
link_label: Option<(&'i [u8], usize, usize)>,
}

impl<'a, 'r, 'o, 'd, 'i> Subject<'a, 'r, 'o, 'd, 'i> {
impl<'a, 'r, 'o, 'd, 'i, 'c> Subject<'a, 'r, 'o, 'd, 'i, 'c> {
pub fn new(
arena: &'a Arena<AstNode<'a>>,
options: &'o Options,
options: &'o Options<'c>,
input: &'i [u8],
line: usize,
refmap: &'r mut RefMap,
Expand Down
32 changes: 16 additions & 16 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,11 @@ pub fn parse_document<'a>(
since = "0.25.0",
note = "The broken link callback has been moved into ParseOptions."
)]
pub fn parse_document_with_broken_link_callback<'a>(
pub fn parse_document_with_broken_link_callback<'a, 'c>(
arena: &'a Arena<AstNode<'a>>,
buffer: &str,
options: &Options,
callback: Arc<dyn BrokenLinkCallback>,
callback: Arc<dyn BrokenLinkCallback + 'c>,
) -> &'a AstNode<'a> {
let mut options_with_callback = options.clone();
options_with_callback.parse.broken_link_callback = Some(callback);
Expand All @@ -105,7 +105,7 @@ pub trait BrokenLinkCallback: RefUnwindSafe + Send + Sync {
fn resolve(&self, broken_link_reference: BrokenLinkReference) -> Option<ResolvedReference>;
}

impl Debug for dyn BrokenLinkCallback {
impl<'c> Debug for dyn BrokenLinkCallback + 'c {
fn fmt(&self, formatter: &mut Formatter<'_>) -> Result<(), fmt::Error> {
formatter.write_str("<dyn BrokenLinkCallback>")
}
Expand Down Expand Up @@ -134,7 +134,7 @@ pub struct BrokenLinkReference<'l> {
pub original: &'l str,
}

pub struct Parser<'a, 'o> {
pub struct Parser<'a, 'o, 'c> {
arena: &'a Arena<AstNode<'a>>,
refmap: RefMap,
root: &'a AstNode<'a>,
Expand All @@ -153,18 +153,18 @@ pub struct Parser<'a, 'o> {
last_line_length: usize,
last_buffer_ended_with_cr: bool,
total_size: usize,
options: &'o Options,
options: &'o Options<'c>,
}

#[derive(Default, Debug, Clone)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
/// Umbrella options struct.
pub struct Options {
pub struct Options<'c> {
/// Enable CommonMark extensions.
pub extension: ExtensionOptions,
pub extension: ExtensionOptions<'c>,

/// Configure parse-time options.
pub parse: ParseOptions,
pub parse: ParseOptions<'c>,

/// Configure render-time options.
pub render: RenderOptions,
Expand All @@ -176,7 +176,7 @@ pub trait URLRewriter: RefUnwindSafe + Send + Sync {
fn to_html(&self, url: &str) -> String;
}

impl Debug for dyn URLRewriter {
impl<'c> Debug for dyn URLRewriter + 'c {
fn fmt(&self, formatter: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
formatter.write_str("<dyn URLRewriter>")
}
Expand All @@ -196,7 +196,7 @@ where
#[derive(Default, Debug, Clone, Builder)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
/// Options to select extensions.
pub struct ExtensionOptions {
pub struct ExtensionOptions<'c> {
/// Enables the
/// [strikethrough extension](https://github.github.com/gfm/#strikethrough-extension-)
/// from the GFM spec.
Expand Down Expand Up @@ -597,7 +597,7 @@ pub struct ExtensionOptions {
/// "<p><img src=\"https://safe.example.com?url=http://unsafe.example.com/bad.png\" alt=\"\" /></p>\n");
/// ```
#[cfg_attr(feature = "arbitrary", arbitrary(value = None))]
pub image_url_rewriter: Option<Arc<dyn URLRewriter>>,
pub image_url_rewriter: Option<Arc<dyn URLRewriter + 'c>>,

/// Wraps link URLs using a function or custom trait object.
///
Expand All @@ -614,14 +614,14 @@ pub struct ExtensionOptions {
/// "<p><a href=\"https://safe.example.com/norefer?url=http://unsafe.example.com/bad\">my link</a></p>\n");
/// ```
#[cfg_attr(feature = "arbitrary", arbitrary(value = None))]
pub link_url_rewriter: Option<Arc<dyn URLRewriter>>,
pub link_url_rewriter: Option<Arc<dyn URLRewriter + 'c>>,
}

#[non_exhaustive]
#[derive(Default, Clone, Debug, Builder)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
/// Options for parser functions.
pub struct ParseOptions {
pub struct ParseOptions<'c> {
/// Punctuation (quotes, full-stops and hyphens) are converted into 'smart' punctuation.
///
/// ```
Expand Down Expand Up @@ -703,7 +703,7 @@ pub struct ParseOptions {
/// <a href=\"https://www.rust-lang.org/\" title=\"The Rust Language\">link</a>. \
/// A [broken link] renders as text.</p>\n");
#[cfg_attr(feature = "arbitrary", arbitrary(default))]
pub broken_link_callback: Option<Arc<dyn BrokenLinkCallback>>,
pub broken_link_callback: Option<Arc<dyn BrokenLinkCallback + 'c>>,
}

#[non_exhaustive]
Expand Down Expand Up @@ -1110,8 +1110,8 @@ struct FootnoteDefinition<'a> {
total_references: u32,
}

impl<'a, 'o> Parser<'a, 'o> {
fn new(arena: &'a Arena<AstNode<'a>>, root: &'a AstNode<'a>, options: &'o Options) -> Self {
impl<'a, 'o, 'c> Parser<'a, 'o, 'c> {
fn new(arena: &'a Arena<AstNode<'a>>, root: &'a AstNode<'a>, options: &'o Options<'c>) -> Self {
Parser {
arena,
refmap: RefMap::new(),
Expand Down
8 changes: 4 additions & 4 deletions src/parser/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use super::inlines::count_newlines;
const MAX_AUTOCOMPLETED_CELLS: usize = 500_000;

pub fn try_opening_block<'a>(
parser: &mut Parser<'a, '_>,
parser: &mut Parser<'a, '_, '_>,
container: &'a AstNode<'a>,
line: &[u8],
) -> Option<(&'a AstNode<'a>, bool, bool)> {
Expand All @@ -30,7 +30,7 @@ pub fn try_opening_block<'a>(
}

fn try_opening_header<'a>(
parser: &mut Parser<'a, '_>,
parser: &mut Parser<'a, '_, '_>,
container: &'a AstNode<'a>,
line: &[u8],
) -> Option<(&'a AstNode<'a>, bool, bool)> {
Expand Down Expand Up @@ -133,7 +133,7 @@ fn try_opening_header<'a>(
}

fn try_opening_row<'a>(
parser: &mut Parser<'a, '_>,
parser: &mut Parser<'a, '_, '_>,
container: &'a AstNode<'a>,
alignments: &[TableAlignment],
line: &[u8],
Expand Down Expand Up @@ -280,7 +280,7 @@ fn row(string: &[u8], spoiler: bool) -> Option<Row> {
}

fn try_inserting_table_header_paragraph<'a>(
parser: &mut Parser<'a, '_>,
parser: &mut Parser<'a, '_, '_>,
container: &'a AstNode<'a>,
paragraph_offset: usize,
) {
Expand Down
4 changes: 2 additions & 2 deletions src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,9 @@ pub fn html(input: &str, expected: &str) {
}

#[track_caller]
fn html_opts_i<F>(input: &str, expected: &str, roundtrip: bool, opts: F)
fn html_opts_i<'c, F>(input: &str, expected: &str, roundtrip: bool, opts: F)
where
F: Fn(&mut Options),
F: FnOnce(&mut Options<'c>),
{
let mut options = Options::default();
opts(&mut options);
Expand Down
10 changes: 4 additions & 6 deletions src/tests/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,13 @@ fn exercise_full_api() {

// Ensure the closure can modify its context.
let blr_ctx_0 = Arc::new(Mutex::new(0));
let blr_ctx_1 = blr_ctx_0.clone();
#[allow(deprecated)]
let _: &AstNode = parse_document_with_broken_link_callback(
&arena,
"document",
&Options::default(),
Arc::new(move |blr: BrokenLinkReference| {
*blr_ctx_1.lock().unwrap() += 1;
Arc::new(|blr: BrokenLinkReference| {
*blr_ctx_0.lock().unwrap() += 1;
let _: &str = blr.normalized;
let _: &str = blr.original;
Some(ResolvedReference {
Expand Down Expand Up @@ -82,9 +81,8 @@ fn exercise_full_api() {
.relaxed_tasklist_matching(false)
.relaxed_autolinks(false);

let blr_ctx_1 = blr_ctx_0.clone();
let _parse = parse.broken_link_callback(Arc::new(move |blr: BrokenLinkReference| {
*blr_ctx_1.lock().unwrap() += 1;
let _parse = parse.broken_link_callback(Arc::new(|blr: BrokenLinkReference| {
*blr_ctx_0.lock().unwrap() += 1;
let _: &str = blr.normalized;
let _: &str = blr.original;
Some(ResolvedReference {
Expand Down
12 changes: 12 additions & 0 deletions src/tests/rewriter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,15 @@ fn link_url_rewriter() {
))
);
}

#[test]
fn can_borrow_from_context() {
let rewrite_to = "http://example.org";

html_opts_i(
"[my link](http://example.com/)",
"<p><a href=\"http://example.org\">my link</a></p>\n",
true,
|opts| opts.extension.link_url_rewriter = Some(Arc::new(|_: &str| rewrite_to.to_owned())),
);
}
8 changes: 4 additions & 4 deletions src/xml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ pub fn format_document_with_plugins<'a>(
XmlFormatter::new(options, output, plugins).format(root, false)
}

struct XmlFormatter<'o> {
struct XmlFormatter<'o, 'c> {
output: &'o mut dyn Write,
options: &'o Options,
options: &'o Options<'c>,
_plugins: &'o Plugins<'o>,
indent: u32,
}

impl<'o> XmlFormatter<'o> {
fn new(options: &'o Options, output: &'o mut dyn Write, plugins: &'o Plugins) -> Self {
impl<'o, 'c> XmlFormatter<'o, 'c> {
fn new(options: &'o Options<'c>, output: &'o mut dyn Write, plugins: &'o Plugins) -> Self {
XmlFormatter {
options,
output,
Expand Down

0 comments on commit 36b843a

Please sign in to comment.