Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

callbacks: constrain to input lifetime #499

Merged
merged 2 commits into from
Dec 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
15 changes: 11 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,15 @@ 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>
where
'c: 'o,
{
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
35 changes: 19 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,11 @@ 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>
where
'c: 'o,
{
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
Loading