diff --git a/examples/lib.rs b/examples/lib.rs index 14cfa647..7923782c 100644 --- a/examples/lib.rs +++ b/examples/lib.rs @@ -1,4 +1,4 @@ -fn main() -> Result<(), String> { +fn main() -> Result<(), markdown::message::Message> { // Turn on debugging. // You can show it with `RUST_LOG=debug cargo run --features log --example lib` env_logger::init(); diff --git a/generate/src/main.rs b/generate/src/main.rs index 3635afbe..fc4e0fb0 100644 --- a/generate/src/main.rs +++ b/generate/src/main.rs @@ -72,12 +72,12 @@ async fn commonmark() { // > 👉 **Important**: this module is generated by `generate/src/main.rs`. // > It is generate from the latest CommonMark website. -use markdown::{{to_html_with_options, CompileOptions, Options}}; +use markdown::{{message, to_html_with_options, CompileOptions, Options}}; use pretty_assertions::assert_eq; #[rustfmt::skip] #[test] -fn commonmark() -> Result<(), String> {{ +fn commonmark() -> Result<(), message::Message> {{ let danger = Options {{ compile: CompileOptions {{ allow_dangerous_html: true, diff --git a/readme.md b/readme.md index b1a4be74..ceb32940 100644 --- a/readme.md +++ b/readme.md @@ -107,7 +107,7 @@ Yields: Extensions (in this case GFM): ```rs -fn main() -> Result<(), String> { +fn main() -> Result<(), markdown::message::Message> { println!( "{}", markdown::to_html_with_options( @@ -135,7 +135,7 @@ Yields: Syntax tree ([mdast][]): ```rs -fn main() -> Result<(), String> { +fn main() -> Result<(), markdown::message::Message> { println!( "{:?}", markdown::to_mdast("# Hey, *you*!", &markdown::ParseOptions::default())? @@ -294,8 +294,8 @@ user-provided markdown opens you up to XSS attacks. An aspect related to XSS for security is syntax errors: markdown itself has no syntax errors. Some syntax extensions (specifically, only MDX) do include syntax errors. -For that reason, `to_html_with_options` returns `Result`, of -which the error is a simple string indicating where the problem happened, what +For that reason, `to_html_with_options` returns `Result`, of +which the error is a struct indicating where the problem happened, what occurred, and what was expected instead. Make sure to handle your errors when using MDX. diff --git a/src/configuration.rs b/src/configuration.rs index f080e229..811e556a 100644 --- a/src/configuration.rs +++ b/src/configuration.rs @@ -482,7 +482,7 @@ pub struct CompileOptions { /// /// ``` /// use markdown::{to_html, to_html_with_options, CompileOptions, Options}; - /// # fn main() -> Result<(), String> { + /// # fn main() -> Result<(), markdown::message::Message> { /// /// // `markdown-rs` is safe by default: /// assert_eq!( @@ -526,7 +526,7 @@ pub struct CompileOptions { /// /// ``` /// use markdown::{to_html, to_html_with_options, CompileOptions, Options}; - /// # fn main() -> Result<(), String> { + /// # fn main() -> Result<(), markdown::message::Message> { /// /// // `markdown-rs` is safe by default: /// assert_eq!( @@ -570,7 +570,7 @@ pub struct CompileOptions { /// /// ``` /// use markdown::{to_html, to_html_with_options, CompileOptions, LineEnding, Options}; - /// # fn main() -> Result<(), String> { + /// # fn main() -> Result<(), markdown::message::Message> { /// /// // `markdown-rs` uses `\n` by default: /// assert_eq!( @@ -612,7 +612,7 @@ pub struct CompileOptions { /// /// ``` /// use markdown::{to_html_with_options, CompileOptions, Options, ParseOptions}; - /// # fn main() -> Result<(), String> { + /// # fn main() -> Result<(), markdown::message::Message> { /// /// // `"Footnotes"` is used by default: /// assert_eq!( @@ -657,7 +657,7 @@ pub struct CompileOptions { /// /// ``` /// use markdown::{to_html_with_options, CompileOptions, Options, ParseOptions}; - /// # fn main() -> Result<(), String> { + /// # fn main() -> Result<(), markdown::message::Message> { /// /// // `"h2"` is used by default: /// assert_eq!( @@ -705,7 +705,7 @@ pub struct CompileOptions { /// /// ``` /// use markdown::{to_html_with_options, CompileOptions, Options, ParseOptions}; - /// # fn main() -> Result<(), String> { + /// # fn main() -> Result<(), markdown::message::Message> { /// /// // `"class=\"sr-only\""` is used by default: /// assert_eq!( @@ -748,7 +748,7 @@ pub struct CompileOptions { /// /// ``` /// use markdown::{to_html_with_options, CompileOptions, Options, ParseOptions}; - /// # fn main() -> Result<(), String> { + /// # fn main() -> Result<(), markdown::message::Message> { /// /// // `"Back to content"` is used by default: /// assert_eq!( @@ -804,7 +804,7 @@ pub struct CompileOptions { /// /// ``` /// use markdown::{to_html_with_options, CompileOptions, Options, ParseOptions}; - /// # fn main() -> Result<(), String> { + /// # fn main() -> Result<(), markdown::message::Message> { /// /// // `"user-content-"` is used by default: /// assert_eq!( @@ -843,7 +843,7 @@ pub struct CompileOptions { /// /// ``` /// use markdown::{to_html_with_options, CompileOptions, Options, ParseOptions}; - /// # fn main() -> Result<(), String> { + /// # fn main() -> Result<(), markdown::message::Message> { /// /// // With `gfm_task_list_item_checkable`, generated `` /// // tags do not contain the attribute `disabled=""` and are thus toggleable by @@ -880,7 +880,7 @@ pub struct CompileOptions { /// /// ``` /// use markdown::{to_html_with_options, CompileOptions, Options, ParseOptions}; - /// # fn main() -> Result<(), String> { + /// # fn main() -> Result<(), markdown::message::Message> { /// /// // With `allow_dangerous_html`, `markdown-rs` passes HTML through untouched: /// assert_eq!( @@ -975,7 +975,7 @@ pub struct ParseOptions { /// /// ``` /// use markdown::{to_html, to_html_with_options, Constructs, Options, ParseOptions}; - /// # fn main() -> Result<(), String> { + /// # fn main() -> Result<(), markdown::message::Message> { /// /// // `markdown-rs` follows CommonMark by default: /// assert_eq!( @@ -1020,7 +1020,7 @@ pub struct ParseOptions { /// /// ``` /// use markdown::{to_html_with_options, Constructs, Options, ParseOptions}; - /// # fn main() -> Result<(), String> { + /// # fn main() -> Result<(), markdown::message::Message> { /// /// // `markdown-rs` supports single tildes by default: /// assert_eq!( @@ -1075,7 +1075,7 @@ pub struct ParseOptions { /// /// ``` /// use markdown::{to_html_with_options, Constructs, Options, ParseOptions}; - /// # fn main() -> Result<(), String> { + /// # fn main() -> Result<(), markdown::message::Message> { /// /// // `markdown-rs` supports single dollars by default: /// assert_eq!( diff --git a/src/construct/content.rs b/src/construct/content.rs index e29dac9b..2c5d6e54 100644 --- a/src/construct/content.rs +++ b/src/construct/content.rs @@ -24,11 +24,12 @@ //! [paragraph]: crate::construct::paragraph use crate::event::{Content, Kind, Link, Name}; +use crate::message; use crate::resolve::Name as ResolveName; use crate::state::{Name as StateName, State}; use crate::subtokenize::{subtokenize, Subresult}; use crate::tokenizer::Tokenizer; -use alloc::{string::String, vec}; +use alloc::vec; /// Before a content chunk. /// @@ -110,7 +111,7 @@ pub fn definition_after(tokenizer: &mut Tokenizer) -> State { /// Merge `Content` chunks, which currently span a single line, into actual /// `Content`s that span multiple lines. -pub fn resolve(tokenizer: &mut Tokenizer) -> Result, String> { +pub fn resolve(tokenizer: &mut Tokenizer) -> Result, message::Message> { let mut index = 0; while index < tokenizer.events.len() { diff --git a/src/construct/document.rs b/src/construct/document.rs index 5591b125..204e9347 100644 --- a/src/construct/document.rs +++ b/src/construct/document.rs @@ -10,11 +10,12 @@ //! * [GFM: Footnote definition][crate::construct::gfm_footnote_definition] use crate::event::{Content, Event, Kind, Link, Name}; +use crate::message; use crate::state::{Name as StateName, State}; use crate::subtokenize::divide_events; use crate::tokenizer::{Container, ContainerState, Tokenizer}; use crate::util::skip; -use alloc::{boxed::Box, string::String, vec::Vec}; +use alloc::{boxed::Box, vec::Vec}; /// Phases where we can exit containers. #[derive(Debug, PartialEq)] @@ -470,7 +471,7 @@ pub fn flow_end(tokenizer: &mut Tokenizer) -> State { } /// Close containers (and flow if needed). -fn exit_containers(tokenizer: &mut Tokenizer, phase: &Phase) -> Result<(), String> { +fn exit_containers(tokenizer: &mut Tokenizer, phase: &Phase) -> Result<(), message::Message> { let mut stack_close = tokenizer .tokenize_state .document_container_stack diff --git a/src/construct/mdx_esm.rs b/src/construct/mdx_esm.rs index 13d7991f..277eecb1 100644 --- a/src/construct/mdx_esm.rs +++ b/src/construct/mdx_esm.rs @@ -30,11 +30,12 @@ //! [parse_options]: crate::ParseOptions use crate::event::Name; +use crate::message; use crate::state::{Name as StateName, State}; use crate::tokenizer::Tokenizer; use crate::util::{mdx_collect::collect, slice::Slice}; use crate::MdxSignal; -use alloc::format; +use alloc::boxed::Box; /// Start of MDX ESM. /// @@ -205,7 +206,7 @@ fn parse_esm(tokenizer: &mut Tokenizer) -> State { // Parse and handle what was signaled back. match parse(&result.value) { MdxSignal::Ok => State::Ok, - MdxSignal::Error(message, relative) => { + MdxSignal::Error(message, relative, source, rule_id) => { let point = tokenizer .parse_state .location @@ -213,16 +214,23 @@ fn parse_esm(tokenizer: &mut Tokenizer) -> State { .expect("expected location index if aware mdx is on") .relative_to_point(&result.stops, relative) .expect("expected non-empty string"); - State::Error(format!("{}:{}: {}", point.line, point.column, message)) + State::Error(message::Message { + place: Some(Box::new(message::Place::Point(point))), + reason: message, + source, + rule_id, + }) } - MdxSignal::Eof(message) => { + MdxSignal::Eof(message, source, rule_id) => { if tokenizer.current.is_none() { - State::Error(format!( - "{}:{}: {}", - tokenizer.point.line, tokenizer.point.column, message - )) + State::Error(message::Message { + place: Some(Box::new(message::Place::Point(tokenizer.point.to_unist()))), + reason: message, + source, + rule_id, + }) } else { - tokenizer.tokenize_state.mdx_last_parse_error = Some(message); + tokenizer.tokenize_state.mdx_last_parse_error = Some((message, *source, *rule_id)); State::Retry(StateName::MdxEsmContinuationStart) } } diff --git a/src/construct/partial_mdx_expression.rs b/src/construct/partial_mdx_expression.rs index f5d1c503..22d0eb5b 100644 --- a/src/construct/partial_mdx_expression.rs +++ b/src/construct/partial_mdx_expression.rs @@ -58,11 +58,12 @@ use crate::construct::partial_space_or_tab::space_or_tab_min_max; use crate::event::Name; +use crate::message; use crate::state::{Name as StateName, State}; use crate::tokenizer::Tokenizer; use crate::util::{constant::TAB_SIZE, mdx_collect::collect}; use crate::{MdxExpressionKind, MdxExpressionParse, MdxSignal}; -use alloc::format; +use alloc::boxed::Box; /// Start of an MDX expression. /// @@ -89,12 +90,15 @@ pub fn start(tokenizer: &mut Tokenizer) -> State { pub fn before(tokenizer: &mut Tokenizer) -> State { match tokenizer.current { None => { - State::Error(format!( - "{}:{}: {}", - tokenizer.point.line, tokenizer.point.column, - tokenizer.tokenize_state.mdx_last_parse_error.take() - .unwrap_or_else(|| "Unexpected end of file in expression, expected a corresponding closing brace for `{`".into()) - )) + let problem = tokenizer.tokenize_state.mdx_last_parse_error.take() + .unwrap_or_else(|| ("Unexpected end of file in expression, expected a corresponding closing brace for `{`".into(), "markdown-rs".into(), "unexpected-eof".into())); + + State::Error(message::Message { + place: Some(Box::new(message::Place::Point(tokenizer.point.to_unist()))), + reason: problem.0, + rule_id: Box::new(problem.2), + source: Box::new(problem.1), + }) } Some(b'\n') => { tokenizer.enter(Name::LineEnding); @@ -167,10 +171,14 @@ pub fn eol_after(tokenizer: &mut Tokenizer) -> State { || tokenizer.tokenize_state.token_2 == Name::MdxJsxFlowTag) && tokenizer.lazy { - State::Error(format!( - "{}:{}: Unexpected lazy line in expression in container, expected line to be prefixed with `>` when in a block quote, whitespace when in a list, etc", - tokenizer.point.line, tokenizer.point.column - )) + State::Error( + message::Message { + place: Some(Box::new(message::Place::Point(tokenizer.point.to_unist()))), + reason: "Unexpected lazy line in expression in container, expected line to be prefixed with `>` when in a block quote, whitespace when in a list, etc".into(), + source: Box::new("markdown-rs".into()), + rule_id: Box::new("unexpected-lazy".into()), + } + ) } else if matches!(tokenizer.current, Some(b'\t' | b' ')) { tokenizer.attempt(State::Next(StateName::MdxExpressionBefore), State::Nok); // Idea: investigate if we’d need to use more complex stripping. @@ -220,21 +228,24 @@ fn parse_expression(tokenizer: &mut Tokenizer, parse: &MdxExpressionParse) -> St // Parse and handle what was signaled back. match parse(&result.value, &kind) { MdxSignal::Ok => State::Ok, - MdxSignal::Error(message, relative) => { + MdxSignal::Error(reason, relative, source, rule_id) => { let point = tokenizer .parse_state .location .as_ref() .expect("expected location index if aware mdx is on") .relative_to_point(&result.stops, relative) - .map_or((tokenizer.point.line, tokenizer.point.column), |d| { - (d.line, d.column) - }); + .unwrap_or_else(|| tokenizer.point.to_unist()); - State::Error(format!("{}:{}: {}", point.0, point.1, message)) + State::Error(message::Message { + place: Some(Box::new(message::Place::Point(point))), + reason, + rule_id, + source, + }) } - MdxSignal::Eof(message) => { - tokenizer.tokenize_state.mdx_last_parse_error = Some(message); + MdxSignal::Eof(reason, source, rule_id) => { + tokenizer.tokenize_state.mdx_last_parse_error = Some((reason, *source, *rule_id)); tokenizer.enter(Name::MdxExpressionData); tokenizer.consume(); State::Next(StateName::MdxExpressionInside) diff --git a/src/construct/partial_mdx_jsx.rs b/src/construct/partial_mdx_jsx.rs index 8bf0742f..87c266ae 100644 --- a/src/construct/partial_mdx_jsx.rs +++ b/src/construct/partial_mdx_jsx.rs @@ -162,6 +162,7 @@ //! [interleaving]: https://mdxjs.com/docs/what-is-mdx/#interleaving use crate::event::Name; +use crate::message; use crate::state::{Name as StateName, State}; use crate::tokenizer::Tokenizer; use crate::util::{ @@ -171,7 +172,7 @@ use crate::util::{ }, identifier::{id_cont, id_start}, }; -use alloc::format; +use alloc::{boxed::Box, format}; use core::str; /// Start of MDX: JSX. @@ -1092,10 +1093,14 @@ pub fn es_whitespace_inside(tokenizer: &mut Tokenizer) -> State { pub fn es_whitespace_eol_after(tokenizer: &mut Tokenizer) -> State { // Lazy continuation in a flow tag is a syntax error. if tokenizer.tokenize_state.token_1 == Name::MdxJsxFlowTag && tokenizer.lazy { - State::Error(format!( - "{}:{}: Unexpected lazy line in jsx in container, expected line to be prefixed with `>` when in a block quote, whitespace when in a list, etc", - tokenizer.point.line, tokenizer.point.column - )) + State::Error( + message::Message { + place: Some(Box::new(message::Place::Point(tokenizer.point.to_unist()))), + reason: "Unexpected lazy line in jsx in container, expected line to be prefixed with `>` when in a block quote, whitespace when in a list, etc".into(), + rule_id: Box::new("unexpected-lazy".into()), + source: Box::new("markdown-rs".into()), + } + ) } else { State::Retry(StateName::MdxJsxEsWhitespaceStart) } @@ -1114,16 +1119,26 @@ fn id_cont_opt(code: Option) -> bool { /// Crash because something happened `at`, with info on what was `expect`ed /// instead. fn crash(tokenizer: &Tokenizer, at: &str, expect: &str) -> State { - State::Error(format!( - "{}:{}: Unexpected {} {}, expected {}", - tokenizer.point.line, - tokenizer.point.column, - format_char_opt(if tokenizer.current.is_none() { - None - } else { - char_after_index(tokenizer.parse_state.bytes, tokenizer.point.index) - }), - at, - expect - )) + State::Error(message::Message { + place: Some(Box::new(message::Place::Point(tokenizer.point.to_unist()))), + reason: format!( + "Unexpected {} {}, expected {}", + format_char_opt(if tokenizer.current.is_none() { + None + } else { + char_after_index(tokenizer.parse_state.bytes, tokenizer.point.index) + }), + at, + expect + ), + rule_id: Box::new(format!( + "unexpected-{}", + if tokenizer.current.is_none() { + "eof" + } else { + "character" + } + )), + source: Box::new("markdown-rs".into()), + }) } diff --git a/src/event.rs b/src/event.rs index 3f4363d5..dacb2c09 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1,5 +1,6 @@ //! Semantic labels of things happening. +use crate::unist; use crate::util::constant::TAB_SIZE; /// Semantic label of a span. @@ -3505,8 +3506,17 @@ pub struct Point { } impl Point { + /// Create a unist point. + pub fn to_unist(&self) -> unist::Point { + unist::Point { + line: self.line, + column: self.column, + offset: self.index, + } + } + /// Create a new point, that is shifted from the close earlier current - /// point, to `index.` + /// point, to `index`. pub fn shift_to(&self, bytes: &[u8], index: usize) -> Point { let mut next = self.clone(); debug_assert!(index > next.index, "expected to shift forward"); diff --git a/src/lib.rs b/src/lib.rs index 584861ef..f1266e81 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,6 +28,7 @@ #![allow(clippy::missing_panics_doc)] #![allow(clippy::must_use_candidate)] #![allow(clippy::too_many_lines)] +#![allow(clippy::result_large_err)] #![doc( html_logo_url = "https://raw.githubusercontent.com/wooorm/markdown-rs/8924580/media/logo-monochromatic.svg?sanitize=true" )] @@ -46,6 +47,7 @@ mod tokenizer; mod util; pub mod mdast; // To do: externalize? +pub mod message; // To do: externalize. pub mod unist; // To do: externalize. #[doc(hidden)] @@ -99,7 +101,7 @@ pub fn to_html(value: &str) -> String { /// /// ``` /// use markdown::{to_html_with_options, CompileOptions, Options}; -/// # fn main() -> Result<(), String> { +/// # fn main() -> Result<(), markdown::message::Message> { /// /// // Use GFM: /// let result = to_html_with_options("~hi~hello!", &Options::gfm())?; @@ -120,7 +122,7 @@ pub fn to_html(value: &str) -> String { /// # Ok(()) /// # } /// ``` -pub fn to_html_with_options(value: &str, options: &Options) -> Result { +pub fn to_html_with_options(value: &str, options: &Options) -> Result { let (events, parse_state) = parser::parse(value, &options.parse)?; Ok(to_html::compile( &events, @@ -143,7 +145,7 @@ pub fn to_html_with_options(value: &str, options: &Options) -> Result Result<(), String> { +/// # fn main() -> Result<(), markdown::message::Message> { /// /// let tree = to_mdast("# Hey, *you*!", &ParseOptions::default())?; /// @@ -152,7 +154,7 @@ pub fn to_html_with_options(value: &str, options: &Options) -> Result Result { +pub fn to_mdast(value: &str, options: &ParseOptions) -> Result { let (events, parse_state) = parser::parse(value, options)?; let node = to_mdast::compile(&events, parse_state.bytes)?; Ok(node) diff --git a/src/message.rs b/src/message.rs new file mode 100644 index 00000000..4b6e8221 --- /dev/null +++ b/src/message.rs @@ -0,0 +1,46 @@ +use crate::unist::{Point, Position}; +use alloc::{boxed::Box, fmt, string::String}; + +#[derive(Clone, Debug, PartialEq)] +pub struct Message { + /// Place of message. + pub place: Option>, + /// Reason for message (should use markdown). + pub reason: String, + /// Category of message. + pub rule_id: Box, + /// Namespace of message. + pub source: Box, +} + +impl fmt::Display for Message { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(ref place) = self.place { + write!(f, "{}: ", place)?; + }; + + write!(f, "{} ({}:{})", self.reason, self.source, self.rule_id) + } +} + +/// Somewhere. +#[derive(Clone, Debug, PartialEq)] +pub enum Place { + /// Between two points. + Position(Position), + /// At a point. + Point(Point), +} + +impl fmt::Display for Place { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Place::Position(position) => write!( + f, + "{}:{}-{}:{}", + position.start.line, position.start.column, position.end.line, position.end.column + ), + Place::Point(point) => write!(f, "{}:{}", point.line, point.column), + } + } +} diff --git a/src/parser.rs b/src/parser.rs index e40aa5a3..9bcfa0ac 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,6 +1,7 @@ //! Turn bytes of markdown into events. use crate::event::{Event, Point}; +use crate::message; use crate::state::{Name as StateName, State}; use crate::subtokenize::subtokenize; use crate::tokenizer::Tokenizer; @@ -32,7 +33,7 @@ pub struct ParseState<'a> { pub fn parse<'a>( value: &'a str, options: &'a ParseOptions, -) -> Result<(Vec, ParseState<'a>), String> { +) -> Result<(Vec, ParseState<'a>), message::Message> { let bytes = value.as_bytes(); let mut parse_state = ParseState { diff --git a/src/resolve.rs b/src/resolve.rs index 813ce523..297a2ef3 100644 --- a/src/resolve.rs +++ b/src/resolve.rs @@ -1,9 +1,9 @@ //! Resolve events. use crate::construct; +use crate::message; use crate::subtokenize::Subresult; use crate::tokenizer::Tokenizer; -use alloc::string::String; /// Names of resolvers. #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -63,7 +63,7 @@ pub enum Name { } /// Call the corresponding resolver. -pub fn call(tokenizer: &mut Tokenizer, name: Name) -> Result, String> { +pub fn call(tokenizer: &mut Tokenizer, name: Name) -> Result, message::Message> { let result = match name { Name::Label => construct::label_end::resolve(tokenizer), Name::Attention => construct::attention::resolve(tokenizer), diff --git a/src/state.rs b/src/state.rs index e7387e62..84c963b5 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,16 +1,16 @@ //! States of the state machine. use crate::construct; +use crate::message; use crate::tokenizer::Tokenizer; -use alloc::string::String; /// Result of a state. -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub enum State { /// Syntax error. /// /// Only used by MDX. - Error(String), + Error(message::Message), /// Move to [`Name`][] next. Next(Name), /// Retry in [`Name`][]. @@ -28,13 +28,13 @@ impl State { /// or on an attempt ([`State::Nok`]). /// /// But it turns the final result into an error if crashed. - pub fn to_result(&self) -> Result<(), String> { + pub fn to_result(&self) -> Result<(), message::Message> { match self { State::Nok | State::Next(_) | State::Retry(_) => { unreachable!("cannot turn intermediate state into result") } State::Ok => Ok(()), - State::Error(x) => Err(x.into()), + State::Error(x) => Err(x.clone()), } } } diff --git a/src/subtokenize.rs b/src/subtokenize.rs index 348224fd..467ca463 100644 --- a/src/subtokenize.rs +++ b/src/subtokenize.rs @@ -18,6 +18,7 @@ //! any level that can include references can be parsed. use crate::event::{Content, Event, Kind, Name, VOID_EVENTS}; +use crate::message; use crate::parser::ParseState; use crate::state::{Name as StateName, State}; use crate::tokenizer::Tokenizer; @@ -78,7 +79,7 @@ pub fn subtokenize( events: &mut Vec, parse_state: &ParseState, filter: &Option, -) -> Result { +) -> Result { let mut map = EditMap::new(); let mut index = 0; let mut value = Subresult { diff --git a/src/to_mdast.rs b/src/to_mdast.rs index 99a0c2ac..7f297567 100644 --- a/src/to_mdast.rs +++ b/src/to_mdast.rs @@ -1,6 +1,6 @@ //! Turn events into a syntax tree. -use crate::event::{Event, Kind, Name, Point as EventPoint}; +use crate::event::{Event, Kind, Name}; use crate::mdast::{ AttributeContent, AttributeValue, AttributeValueExpression, BlockQuote, Break, Code, Definition, Delete, Emphasis, FootnoteDefinition, FootnoteReference, Heading, Html, Image, @@ -9,6 +9,7 @@ use crate::mdast::{ MdxjsEsm, Node, Paragraph, ReferenceKind, Root, Strong, Table, TableCell, TableRow, Text, ThematicBreak, Toml, Yaml, }; +use crate::message; use crate::unist::{Point, Position}; use crate::util::{ character_reference::{ @@ -20,6 +21,7 @@ use crate::util::{ slice::{Position as SlicePosition, Slice}, }; use alloc::{ + boxed::Box, format, string::{String, ToString}, vec, @@ -114,12 +116,12 @@ impl<'a> CompileContext<'a> { start: if events.is_empty() { Point::new(1, 1, 0) } else { - point_from_event(&events[0]) + events[0].point.to_unist() }, end: if events.is_empty() { Point::new(1, 1, 0) } else { - point_from_event(&events[events.len() - 1]) + events[events.len() - 1].point.to_unist() }, }), }); @@ -203,9 +205,9 @@ impl<'a> CompileContext<'a> { event_stack.push(self.index); } - fn tail_pop(&mut self) -> Result<(), String> { + fn tail_pop(&mut self) -> Result<(), message::Message> { let ev = &self.events[self.index]; - let end = point_from_event(ev); + let end = ev.point.to_unist(); let (tree, stack, event_stack) = self.trees.last_mut().expect("Cannot get tail w/o tree"); let node = delve_mut(tree, stack); let pos = node.position_mut().expect("Cannot pop manually added node"); @@ -223,7 +225,7 @@ impl<'a> CompileContext<'a> { } /// Turn events and bytes into a syntax tree. -pub fn compile(events: &[Event], bytes: &[u8]) -> Result { +pub fn compile(events: &[Event], bytes: &[u8]) -> Result { let mut context = CompileContext::new(events, bytes); let mut index = 0; @@ -244,7 +246,7 @@ pub fn compile(events: &[Event], bytes: &[u8]) -> Result { } /// Handle the event at `index`. -fn handle(context: &mut CompileContext, index: usize) -> Result<(), String> { +fn handle(context: &mut CompileContext, index: usize) -> Result<(), message::Message> { context.index = index; if context.events[index].kind == Kind::Enter { @@ -257,7 +259,7 @@ fn handle(context: &mut CompileContext, index: usize) -> Result<(), String> { } /// Handle [`Enter`][Kind::Enter]. -fn enter(context: &mut CompileContext) -> Result<(), String> { +fn enter(context: &mut CompileContext) -> Result<(), message::Message> { match context.events[context.index].name { Name::AutolinkEmail | Name::AutolinkProtocol @@ -335,7 +337,7 @@ fn enter(context: &mut CompileContext) -> Result<(), String> { } /// Handle [`Exit`][Kind::Exit]. -fn exit(context: &mut CompileContext) -> Result<(), String> { +fn exit(context: &mut CompileContext) -> Result<(), message::Message> { match context.events[context.index].name { Name::Autolink | Name::BlockQuote @@ -775,7 +777,7 @@ fn on_enter_math_flow(context: &mut CompileContext) { /// Handle [`Enter`][Kind::Enter]:{[`MdxJsxFlowTag`][Name::MdxJsxFlowTag],[`MdxJsxTextTag`][Name::MdxJsxTextTag]}. fn on_enter_mdx_jsx_tag(context: &mut CompileContext) { - let point = point_from_event(&context.events[context.index]); + let point = context.events[context.index].point.to_unist(); context.jsx_tag = Some(JsxTag { name: None, attributes: vec![], @@ -788,35 +790,41 @@ fn on_enter_mdx_jsx_tag(context: &mut CompileContext) { } /// Handle [`Enter`][Kind::Enter]:[`MdxJsxTagClosingMarker`][Name::MdxJsxTagClosingMarker]. -fn on_enter_mdx_jsx_tag_closing_marker(context: &mut CompileContext) -> Result<(), String> { +fn on_enter_mdx_jsx_tag_closing_marker( + context: &mut CompileContext, +) -> Result<(), message::Message> { if context.jsx_tag_stack.is_empty() { let event = &context.events[context.index]; - Err(format!( - "{}:{}: Unexpected closing slash `/` in tag, expected an open tag first (mdx-jsx:unexpected-closing-slash)", - event.point.line, - event.point.column, - )) + Err(message::Message { + place: Some(Box::new(message::Place::Point(event.point.to_unist()))), + reason: "Unexpected closing slash `/` in tag, expected an open tag first".into(), + rule_id: Box::new("unexpected-closing-slash".into()), + source: Box::new("markdown-rs".into()), + }) } else { Ok(()) } } /// Handle [`Enter`][Kind::Enter]:{[`MdxJsxTagAttribute`][Name::MdxJsxTagAttribute],[`MdxJsxTagAttributeExpression`][Name::MdxJsxTagAttributeExpression]}. -fn on_enter_mdx_jsx_tag_any_attribute(context: &mut CompileContext) -> Result<(), String> { +fn on_enter_mdx_jsx_tag_any_attribute( + context: &mut CompileContext, +) -> Result<(), message::Message> { if context.jsx_tag.as_ref().expect("expected tag").close { let event = &context.events[context.index]; - Err(format!( - "{}:{}: Unexpected attribute in closing tag, expected the end of the tag (mdx-jsx:unexpected-attribute)", - event.point.line, - event.point.column, - )) + Err(message::Message { + place: Some(Box::new(message::Place::Point(event.point.to_unist()))), + reason: "Unexpected attribute in closing tag, expected the end of the tag".into(), + rule_id: Box::new("unexpected-attribute".into()), + source: Box::new("markdown-rs".into()), + }) } else { Ok(()) } } /// Handle [`Enter`][Kind::Enter]:[`MdxJsxTagAttribute`][Name::MdxJsxTagAttribute]. -fn on_enter_mdx_jsx_tag_attribute(context: &mut CompileContext) -> Result<(), String> { +fn on_enter_mdx_jsx_tag_attribute(context: &mut CompileContext) -> Result<(), message::Message> { on_enter_mdx_jsx_tag_any_attribute(context)?; context @@ -833,7 +841,9 @@ fn on_enter_mdx_jsx_tag_attribute(context: &mut CompileContext) -> Result<(), St } /// Handle [`Enter`][Kind::Enter]:[`MdxJsxTagAttributeExpression`][Name::MdxJsxTagAttributeExpression]. -fn on_enter_mdx_jsx_tag_attribute_expression(context: &mut CompileContext) -> Result<(), String> { +fn on_enter_mdx_jsx_tag_attribute_expression( + context: &mut CompileContext, +) -> Result<(), message::Message> { on_enter_mdx_jsx_tag_any_attribute(context)?; let CollectResult { value, stops } = collect( @@ -884,15 +894,19 @@ fn on_enter_mdx_jsx_tag_attribute_value_expression(context: &mut CompileContext) } /// Handle [`Enter`][Kind::Enter]:[`MdxJsxTagSelfClosingMarker`][Name::MdxJsxTagSelfClosingMarker]. -fn on_enter_mdx_jsx_tag_self_closing_marker(context: &mut CompileContext) -> Result<(), String> { +fn on_enter_mdx_jsx_tag_self_closing_marker( + context: &mut CompileContext, +) -> Result<(), message::Message> { let tag = context.jsx_tag.as_ref().expect("expected tag"); if tag.close { let event = &context.events[context.index]; - Err(format!( - "{}:{}: Unexpected self-closing slash `/` in closing tag, expected the end of the tag (mdx-jsx:unexpected-self-closing-slash)", - event.point.line, - event.point.column, - )) + Err(message::Message { + place: Some(Box::new(message::Place::Point(event.point.to_unist()))), + reason: "Unexpected self-closing slash `/` in closing tag, expected the end of the tag" + .into(), + rule_id: Box::new("unexpected-self-closing-slash".into()), + source: Box::new("markdown-rs".into()), + }) } else { Ok(()) } @@ -907,13 +921,13 @@ fn on_enter_paragraph(context: &mut CompileContext) { } /// Handle [`Exit`][Kind::Exit]:`*`. -fn on_exit(context: &mut CompileContext) -> Result<(), String> { +fn on_exit(context: &mut CompileContext) -> Result<(), message::Message> { context.tail_pop()?; Ok(()) } /// Handle [`Exit`][Kind::Exit]:[`AutolinkProtocol`][Name::AutolinkProtocol]. -fn on_exit_autolink_protocol(context: &mut CompileContext) -> Result<(), String> { +fn on_exit_autolink_protocol(context: &mut CompileContext) -> Result<(), message::Message> { on_exit_data(context)?; let value = Slice::from_position( context.bytes, @@ -928,7 +942,7 @@ fn on_exit_autolink_protocol(context: &mut CompileContext) -> Result<(), String> } /// Handle [`Exit`][Kind::Exit]:[`AutolinkEmail`][Name::AutolinkEmail]. -fn on_exit_autolink_email(context: &mut CompileContext) -> Result<(), String> { +fn on_exit_autolink_email(context: &mut CompileContext) -> Result<(), message::Message> { on_exit_data(context)?; let value = Slice::from_position( context.bytes, @@ -1010,7 +1024,7 @@ fn on_exit_raw_flow_fence(context: &mut CompileContext) { } /// Handle [`Exit`][Kind::Exit]:{[`CodeFenced`][Name::CodeFenced],[`MathFlow`][Name::MathFlow]}. -fn on_exit_raw_flow(context: &mut CompileContext) -> Result<(), String> { +fn on_exit_raw_flow(context: &mut CompileContext) -> Result<(), message::Message> { let value = trim_eol(context.resume().to_string(), true, true); match context.tail_mut() { @@ -1025,7 +1039,7 @@ fn on_exit_raw_flow(context: &mut CompileContext) -> Result<(), String> { } /// Handle [`Exit`][Kind::Exit]:[`CodeIndented`][Name::CodeIndented]. -fn on_exit_code_indented(context: &mut CompileContext) -> Result<(), String> { +fn on_exit_code_indented(context: &mut CompileContext) -> Result<(), message::Message> { let value = context.resume().to_string(); if let Node::Code(node) = context.tail_mut() { @@ -1039,7 +1053,7 @@ fn on_exit_code_indented(context: &mut CompileContext) -> Result<(), String> { } /// Handle [`Exit`][Kind::Exit]:{[`CodeText`][Name::CodeText],[`MathText`][Name::MathText]}. -fn on_exit_raw_text(context: &mut CompileContext) -> Result<(), String> { +fn on_exit_raw_text(context: &mut CompileContext) -> Result<(), message::Message> { let mut value = context.resume().to_string(); // To do: share with `to_html`. @@ -1077,7 +1091,7 @@ fn on_exit_raw_text(context: &mut CompileContext) -> Result<(), String> { } /// Handle [`Exit`][Kind::Exit]:[`Data`][Name::Data] (and many text things). -fn on_exit_data(context: &mut CompileContext) -> Result<(), String> { +fn on_exit_data(context: &mut CompileContext) -> Result<(), message::Message> { let value = Slice::from_position( context.bytes, &SlicePosition::from_exit_event(context.events, context.index), @@ -1139,7 +1153,7 @@ fn on_exit_drop(context: &mut CompileContext) { } /// Handle [`Exit`][Kind::Exit]:[`Frontmatter`][Name::Frontmatter]. -fn on_exit_frontmatter(context: &mut CompileContext) -> Result<(), String> { +fn on_exit_frontmatter(context: &mut CompileContext) -> Result<(), message::Message> { let value = trim_eol(context.resume().to_string(), true, true); match context.tail_mut() { @@ -1153,7 +1167,7 @@ fn on_exit_frontmatter(context: &mut CompileContext) -> Result<(), String> { } /// Handle [`Exit`][Kind::Exit]:{[`GfmAutolinkLiteralEmail`][Name::GfmAutolinkLiteralEmail],[`GfmAutolinkLiteralMailto`][Name::GfmAutolinkLiteralMailto],[`GfmAutolinkLiteralProtocol`][Name::GfmAutolinkLiteralProtocol],[`GfmAutolinkLiteralWww`][Name::GfmAutolinkLiteralWww],[`GfmAutolinkLiteralXmpp`][Name::GfmAutolinkLiteralXmpp]}. -fn on_exit_gfm_autolink_literal(context: &mut CompileContext) -> Result<(), String> { +fn on_exit_gfm_autolink_literal(context: &mut CompileContext) -> Result<(), message::Message> { on_exit_data(context)?; let value = Slice::from_position( @@ -1183,7 +1197,7 @@ fn on_exit_gfm_autolink_literal(context: &mut CompileContext) -> Result<(), Stri } /// Handle [`Exit`][Kind::Exit]:[`GfmTable`][Name::GfmTable]. -fn on_exit_gfm_table(context: &mut CompileContext) -> Result<(), String> { +fn on_exit_gfm_table(context: &mut CompileContext) -> Result<(), message::Message> { on_exit(context)?; context.gfm_table_inside = false; Ok(()) @@ -1202,7 +1216,7 @@ fn on_exit_gfm_task_list_item_value(context: &mut CompileContext) { } /// Handle [`Exit`][Kind::Exit]:{[`HardBreakEscape`][Name::HardBreakEscape],[`HardBreakTrailing`][Name::HardBreakTrailing]}. -fn on_exit_hard_break(context: &mut CompileContext) -> Result<(), String> { +fn on_exit_hard_break(context: &mut CompileContext) -> Result<(), message::Message> { on_exit(context)?; context.hard_break_after = true; Ok(()) @@ -1227,7 +1241,7 @@ fn on_exit_heading_atx_sequence(context: &mut CompileContext) { } /// Handle [`Exit`][Kind::Exit]:[`HeadingSetext`][Name::HeadingSetext]. -fn on_exit_heading_setext(context: &mut CompileContext) -> Result<(), String> { +fn on_exit_heading_setext(context: &mut CompileContext) -> Result<(), message::Message> { context.heading_setext_text_after = false; on_exit(context)?; Ok(()) @@ -1278,13 +1292,13 @@ fn on_exit_label_text(context: &mut CompileContext) { } /// Handle [`Exit`][Kind::Exit]:[`LineEnding`][Name::LineEnding]. -fn on_exit_line_ending(context: &mut CompileContext) -> Result<(), String> { +fn on_exit_line_ending(context: &mut CompileContext) -> Result<(), message::Message> { if context.heading_setext_text_after { // Ignore. } // Line ending position after hard break is part of it. else if context.hard_break_after { - let end = point_from_event(&context.events[context.index]); + let end = context.events[context.index].point.to_unist(); let node = context.tail_mut(); let tail = node .children_mut() @@ -1313,7 +1327,7 @@ fn on_exit_line_ending(context: &mut CompileContext) -> Result<(), String> { } /// Handle [`Exit`][Kind::Exit]:{[`HtmlFlow`][Name::HtmlFlow],[`HtmlText`][Name::HtmlText]}. -fn on_exit_html(context: &mut CompileContext) -> Result<(), String> { +fn on_exit_html(context: &mut CompileContext) -> Result<(), message::Message> { let value = context.resume().to_string(); match context.tail_mut() { @@ -1326,7 +1340,7 @@ fn on_exit_html(context: &mut CompileContext) -> Result<(), String> { } /// Handle [`Exit`][Kind::Exit]:{[`GfmFootnoteCall`][Name::GfmFootnoteCall],[`Image`][Name::Image],[`Link`][Name::Link]}. -fn on_exit_media(context: &mut CompileContext) -> Result<(), String> { +fn on_exit_media(context: &mut CompileContext) -> Result<(), message::Message> { let reference = context .media_reference_stack .pop() @@ -1379,7 +1393,7 @@ fn on_exit_media(context: &mut CompileContext) -> Result<(), String> { } /// Handle [`Exit`][Kind::Exit]:[`ListItem`][Name::ListItem]. -fn on_exit_list_item(context: &mut CompileContext) -> Result<(), String> { +fn on_exit_list_item(context: &mut CompileContext) -> Result<(), message::Message> { if let Node::ListItem(item) = context.tail_mut() { if item.checked.is_some() { if let Some(Node::Paragraph(paragraph)) = item.children.first_mut() { @@ -1444,13 +1458,13 @@ fn on_exit_list_item_value(context: &mut CompileContext) { } /// Handle [`Exit`][Kind::Exit]:{[`MdxJsxFlowTag`][Name::MdxJsxFlowTag],[`MdxJsxTextTag`][Name::MdxJsxTextTag]}. -fn on_exit_mdx_jsx_tag(context: &mut CompileContext) -> Result<(), String> { +fn on_exit_mdx_jsx_tag(context: &mut CompileContext) -> Result<(), message::Message> { let mut tag = context.jsx_tag.as_ref().expect("expected tag").clone(); // End of a tag, so drop the buffer. context.resume(); // Set end point. - tag.end = point_from_event(&context.events[context.index]); + tag.end = context.events[context.index].point.to_unist(); let stack = &context.jsx_tag_stack; let tail = stack.last(); @@ -1460,15 +1474,24 @@ fn on_exit_mdx_jsx_tag(context: &mut CompileContext) -> Result<(), String> { let tail = tail.unwrap(); if tail.name != tag.name { - return Err(format!( - "{}:{}: Unexpected closing tag `{}`, expected corresponding closing tag for `{}` ({}:{}) (mdx-jsx:end-tag-mismatch)", - tag.start.line, - tag.start.column, - serialize_abbreviated_tag(&tag), - serialize_abbreviated_tag(tail), - tail.start.line, - tail.start.column, - )); + let label = serialize_abbreviated_tag(&tag); + return Err( + message::Message { + place: Some(Box::new(message::Place::Position(Position { + start: tag.start, + end: tag.end, + }))), + reason: format!( + "Unexpected closing tag `{}`, expected corresponding closing tag for `{}` ({}:{})", + label, + serialize_abbreviated_tag(tail), + tail.start.line, + tail.start.column, + ), + rule_id: Box::new("end-tag-mismatch".into()), + source: Box::new("markdown-rs".into()), + }, + ); } // Remove from our custom stack. @@ -1559,7 +1582,7 @@ fn on_exit_mdx_jsx_tag_name_local(context: &mut CompileContext) { } /// Handle [`Exit`][Kind::Exit]:{[`MdxEsm`][Name::MdxEsm],[`MdxFlowExpression`][Name::MdxFlowExpression],[`MdxTextExpression`][Name::MdxTextExpression]}. -fn on_exit_mdx_esm_or_expression(context: &mut CompileContext) -> Result<(), String> { +fn on_exit_mdx_esm_or_expression(context: &mut CompileContext) -> Result<(), message::Message> { on_exit_drop(context); context.tail_pop()?; Ok(()) @@ -1669,16 +1692,6 @@ fn on_exit_resource_title_string(context: &mut CompileContext) { } } -/// Create a point from an event. -fn point_from_event_point(point: &EventPoint) -> Point { - Point::new(point.line, point.column, point.index) -} - -/// Create a point from an event. -fn point_from_event(event: &Event) -> Point { - point_from_event_point(&event.point) -} - /// Create a position from an event. fn position_from_event(event: &Event) -> Position { let end = Point::new(event.point.line, event.point.column, event.point.index); @@ -1741,7 +1754,7 @@ fn on_mismatch_error( context: &mut CompileContext, left: Option<&Event>, right: &Event, -) -> Result<(), String> { +) -> Result<(), message::Message> { if right.name == Name::MdxJsxFlowTag || right.name == Name::MdxJsxTextTag { let stack = &context.jsx_tag_stack; let tag = stack.last().unwrap(); @@ -1751,34 +1764,42 @@ fn on_mismatch_error( &context.events[context.events.len() - 1].point }; - return Err(format!( - "{}:{}: Expected a closing tag for `{}` ({}:{}){} (mdx-jsx:end-tag-mismatch)", - point.line, - point.column, - serialize_abbreviated_tag(tag), - tag.start.line, - tag.start.column, - if let Some(left) = left { - format!(" before the end of `{:?}`", left.name) - } else { - String::new() - } - )); + return Err(message::Message { + place: Some(Box::new(message::Place::Point(point.to_unist()))), + reason: format!( + "Expected a closing tag for `{}` ({}:{}){}", + serialize_abbreviated_tag(tag), + tag.start.line, + tag.start.column, + if let Some(left) = left { + format!(" before the end of `{:?}`", left.name) + } else { + String::new() + } + ), + rule_id: Box::new("end-tag-mismatch".into()), + source: Box::new("markdown-rs".into()), + }); } if let Some(left) = left { if left.name == Name::MdxJsxFlowTag || left.name == Name::MdxJsxTextTag { let tag = context.jsx_tag.as_ref().unwrap(); - return Err(format!( - "{}:{}: Expected the closing tag `{}` either before the start of `{:?}` ({}:{}), or another opening tag after that start (mdx-jsx:end-tag-mismatch)", - tag.start.line, - tag.start.column, - serialize_abbreviated_tag(tag), - &right.name, - &right.point.line, - &right.point.column, - )); + return Err( + message::Message { + place: Some(Box::new(message::Place::Point(tag.start.clone()))), + reason: format!( + "Expected the closing tag `{}` either before the start of `{:?}` ({}:{}), or another opening tag after that start", + serialize_abbreviated_tag(tag), + &right.name, + &right.point.line, + &right.point.column, + ), + rule_id: Box::new("end-tag-mismatch".into()), + source: Box::new("markdown-rs".into()), + } + ); } unreachable!("mismatched (non-jsx): {:?} / {:?}", left.name, right.name); } else { diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 67ac411a..ef95c9a2 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -9,6 +9,7 @@ //! [`attempt`]: Tokenizer::attempt use crate::event::{Content, Event, Kind, Link, Name, Point, VOID_EVENTS}; +use crate::message; use crate::parser::ParseState; use crate::resolve::{call as call_resolve, Name as ResolveName}; use crate::state::{call, State}; @@ -232,7 +233,7 @@ pub struct TokenizeState<'a> { pub gfm_footnote_definitions: Vec, // Last error message provided at an EOF of an expression. - pub mdx_last_parse_error: Option, + pub mdx_last_parse_error: Option<(String, String, String)>, /// Whether to connect events. pub connect: bool, @@ -625,7 +626,7 @@ impl<'a> Tokenizer<'a> { } /// Flush. - pub fn flush(&mut self, state: State, resolve: bool) -> Result { + pub fn flush(&mut self, state: State, resolve: bool) -> Result { let to = (self.point.index, self.point.vs); let state = push_impl(self, to, to, state, true); diff --git a/src/util/location.rs b/src/util/location.rs index 87d2ad8e..bc97a2fd 100644 --- a/src/util/location.rs +++ b/src/util/location.rs @@ -71,11 +71,7 @@ impl Location { } else { 0 }; - return Some(Point { - line: index + 1, - column: offset + 1 - previous, - offset, - }); + return Some(Point::new(index + 1, offset + 1 - previous, offset)); } } diff --git a/src/util/mdx.rs b/src/util/mdx.rs index ce5c9d49..0ed6a713 100644 --- a/src/util/mdx.rs +++ b/src/util/mdx.rs @@ -1,4 +1,4 @@ -use alloc::string::String; +use alloc::{boxed::Box, string::String}; /// Signal used as feedback when parsing MDX ESM/expressions. #[derive(Clone, Debug)] @@ -14,7 +14,7 @@ pub enum Signal { /// ```rust ignore /// Signal::Error("Unexpected `\"`, expected identifier".into(), 1) /// ``` - Error(String, usize), + Error(String, usize, Box, Box), /// An error at the end of the (partial?) expression. /// /// `markdown-rs` will either crash with error message `String` if it @@ -26,7 +26,7 @@ pub enum Signal { /// ```rust ignore /// Signal::Eof("Unexpected end of file in string literal".into()) /// ``` - Eof(String), + Eof(String, Box, Box), /// Done, successfully. /// /// `markdown-rs` knows that this is the end of a valid expression/esm and diff --git a/tests/attention.rs b/tests/attention.rs index 61d038a4..5f071e56 100644 --- a/tests/attention.rs +++ b/tests/attention.rs @@ -1,13 +1,13 @@ use markdown::{ mdast::{Emphasis, Node, Paragraph, Root, Strong, Text}, - to_html, to_html_with_options, to_mdast, + message, to_html, to_html_with_options, to_mdast, unist::Position, CompileOptions, Constructs, Options, ParseOptions, }; use pretty_assertions::assert_eq; #[test] -fn attention() -> Result<(), String> { +fn attention() -> Result<(), message::Message> { let danger = Options { compile: CompileOptions { allow_dangerous_html: true, diff --git a/tests/autolink.rs b/tests/autolink.rs index 1f4fab5a..e06d9b0c 100644 --- a/tests/autolink.rs +++ b/tests/autolink.rs @@ -1,13 +1,13 @@ use markdown::{ mdast::{Link, Node, Paragraph, Root, Text}, - to_html, to_html_with_options, to_mdast, + message, to_html, to_html_with_options, to_mdast, unist::Position, CompileOptions, Constructs, Options, ParseOptions, }; use pretty_assertions::assert_eq; #[test] -fn autolink() -> Result<(), String> { +fn autolink() -> Result<(), message::Message> { let danger = Options { compile: CompileOptions { allow_dangerous_html: true, diff --git a/tests/block_quote.rs b/tests/block_quote.rs index b6fcf6a8..800cbfb2 100644 --- a/tests/block_quote.rs +++ b/tests/block_quote.rs @@ -1,13 +1,13 @@ use markdown::{ mdast::{BlockQuote, Node, Paragraph, Root, Text}, - to_html, to_html_with_options, to_mdast, + message, to_html, to_html_with_options, to_mdast, unist::Position, Constructs, Options, ParseOptions, }; use pretty_assertions::assert_eq; #[test] -fn block_quote() -> Result<(), String> { +fn block_quote() -> Result<(), message::Message> { assert_eq!( to_html("> # a\n> b\n> c"), "
\n

a

\n

b\nc

\n
", diff --git a/tests/character_escape.rs b/tests/character_escape.rs index e062f28b..522aa524 100644 --- a/tests/character_escape.rs +++ b/tests/character_escape.rs @@ -1,13 +1,13 @@ use markdown::{ mdast::{Node, Paragraph, Root, Text}, - to_html, to_html_with_options, to_mdast, + message, to_html, to_html_with_options, to_mdast, unist::Position, CompileOptions, Constructs, Options, ParseOptions, }; use pretty_assertions::assert_eq; #[test] -fn character_escape() -> Result<(), String> { +fn character_escape() -> Result<(), message::Message> { let danger = Options { compile: CompileOptions { allow_dangerous_html: true, diff --git a/tests/character_reference.rs b/tests/character_reference.rs index 03282a1c..41b67e3a 100644 --- a/tests/character_reference.rs +++ b/tests/character_reference.rs @@ -1,13 +1,13 @@ use markdown::{ mdast::{Node, Paragraph, Root, Text}, - to_html, to_html_with_options, to_mdast, + message, to_html, to_html_with_options, to_mdast, unist::Position, CompileOptions, Constructs, Options, ParseOptions, }; use pretty_assertions::assert_eq; #[test] -fn character_reference() -> Result<(), String> { +fn character_reference() -> Result<(), message::Message> { assert_eq!( to_html( "  & © Æ Ď\n¾ ℋ ⅆ\n∲ ≧̸" diff --git a/tests/code_fenced.rs b/tests/code_fenced.rs index 403a8932..86574ca0 100644 --- a/tests/code_fenced.rs +++ b/tests/code_fenced.rs @@ -1,13 +1,13 @@ use markdown::{ mdast::{Code, Node, Root}, - to_html, to_html_with_options, to_mdast, + message, to_html, to_html_with_options, to_mdast, unist::Position, Constructs, Options, ParseOptions, }; use pretty_assertions::assert_eq; #[test] -fn code_fenced() -> Result<(), String> { +fn code_fenced() -> Result<(), message::Message> { assert_eq!( to_html("```\n<\n >\n```"), "
<\n >\n
", diff --git a/tests/code_indented.rs b/tests/code_indented.rs index 1bd5726d..b393deec 100644 --- a/tests/code_indented.rs +++ b/tests/code_indented.rs @@ -1,13 +1,13 @@ use markdown::{ mdast::{Code, Node, Root}, - to_html, to_html_with_options, to_mdast, + message, to_html, to_html_with_options, to_mdast, unist::Position, CompileOptions, Constructs, Options, ParseOptions, }; use pretty_assertions::assert_eq; #[test] -fn code_indented() -> Result<(), String> { +fn code_indented() -> Result<(), message::Message> { assert_eq!( to_html(" a simple\n indented code block"), "
a simple\n  indented code block\n
", diff --git a/tests/code_text.rs b/tests/code_text.rs index 47d8b197..d4a0e70b 100644 --- a/tests/code_text.rs +++ b/tests/code_text.rs @@ -1,13 +1,13 @@ use markdown::{ mdast::{InlineCode, Node, Paragraph, Root, Text}, - to_html, to_html_with_options, to_mdast, + message, to_html, to_html_with_options, to_mdast, unist::Position, CompileOptions, Constructs, Options, ParseOptions, }; use pretty_assertions::assert_eq; #[test] -fn code_text() -> Result<(), String> { +fn code_text() -> Result<(), message::Message> { let danger = Options { compile: CompileOptions { allow_dangerous_html: true, diff --git a/tests/commonmark.rs b/tests/commonmark.rs index 532cb373..7d4f5746 100644 --- a/tests/commonmark.rs +++ b/tests/commonmark.rs @@ -3,12 +3,12 @@ // > 👉 **Important**: this module is generated by `generate/src/main.rs`. // > It is generate from the latest CommonMark website. -use markdown::{to_html_with_options, CompileOptions, Options}; +use markdown::{message, to_html_with_options, CompileOptions, Options}; use pretty_assertions::assert_eq; #[rustfmt::skip] #[test] -fn commonmark() -> Result<(), String> { +fn commonmark() -> Result<(), message::Message> { let danger = Options { compile: CompileOptions { allow_dangerous_html: true, diff --git a/tests/definition.rs b/tests/definition.rs index 0279d4d1..271beaaf 100644 --- a/tests/definition.rs +++ b/tests/definition.rs @@ -1,13 +1,13 @@ use markdown::{ mdast::{Definition, Node, Root}, - to_html, to_html_with_options, to_mdast, + message, to_html, to_html_with_options, to_mdast, unist::Position, CompileOptions, Constructs, Options, ParseOptions, }; use pretty_assertions::assert_eq; #[test] -fn definition() -> Result<(), String> { +fn definition() -> Result<(), message::Message> { let danger = Options { compile: CompileOptions { allow_dangerous_html: true, diff --git a/tests/frontmatter.rs b/tests/frontmatter.rs index 82a928a2..68efd485 100644 --- a/tests/frontmatter.rs +++ b/tests/frontmatter.rs @@ -1,13 +1,13 @@ use markdown::{ mdast::{Node, Root, Toml, Yaml}, - to_html, to_html_with_options, to_mdast, + message, to_html, to_html_with_options, to_mdast, unist::Position, Constructs, Options, ParseOptions, }; use pretty_assertions::assert_eq; #[test] -fn frontmatter() -> Result<(), String> { +fn frontmatter() -> Result<(), message::Message> { let frontmatter = Options { parse: ParseOptions { constructs: Constructs { diff --git a/tests/fuzz.rs b/tests/fuzz.rs index 4778cbb8..98a3f219 100644 --- a/tests/fuzz.rs +++ b/tests/fuzz.rs @@ -1,8 +1,8 @@ -use markdown::{mdast, to_html, to_html_with_options, to_mdast, Options}; +use markdown::{mdast, message, to_html, to_html_with_options, to_mdast, Options}; use pretty_assertions::assert_eq; #[test] -fn fuzz() -> Result<(), String> { +fn fuzz() -> Result<(), message::Message> { assert_eq!( to_html("[\n~\na\n-\n\n"), "

[\n~\na

\n", diff --git a/tests/gfm_autolink_literal.rs b/tests/gfm_autolink_literal.rs index 3470d6dc..29869f8d 100644 --- a/tests/gfm_autolink_literal.rs +++ b/tests/gfm_autolink_literal.rs @@ -5,14 +5,14 @@ use markdown::{ mdast::{Link, Node, Paragraph, Root, Text}, - to_html, to_html_with_options, to_mdast, + message, to_html, to_html_with_options, to_mdast, unist::Position, Options, ParseOptions, }; use pretty_assertions::assert_eq; #[test] -fn gfm_autolink_literal() -> Result<(), String> { +fn gfm_autolink_literal() -> Result<(), message::Message> { assert_eq!( to_html("https://example.com"), "

https://example.com

", diff --git a/tests/gfm_footnote.rs b/tests/gfm_footnote.rs index 92562aa8..7c16d92c 100644 --- a/tests/gfm_footnote.rs +++ b/tests/gfm_footnote.rs @@ -1,13 +1,13 @@ use markdown::{ mdast::{FootnoteDefinition, FootnoteReference, Node, Paragraph, Root, Text}, - to_html, to_html_with_options, to_mdast, + message, to_html, to_html_with_options, to_mdast, unist::Position, CompileOptions, Options, ParseOptions, }; use pretty_assertions::assert_eq; #[test] -fn gfm_footnote() -> Result<(), String> { +fn gfm_footnote() -> Result<(), message::Message> { assert_eq!( to_html("A call.[^a]\n\n[^a]: whatevs"), "

A call.^a

\n", diff --git a/tests/gfm_strikethrough.rs b/tests/gfm_strikethrough.rs index 120f6c1f..55b31e4a 100644 --- a/tests/gfm_strikethrough.rs +++ b/tests/gfm_strikethrough.rs @@ -5,14 +5,14 @@ use markdown::{ mdast::{Delete, Node, Paragraph, Root, Text}, - to_html, to_html_with_options, to_mdast, + message, to_html, to_html_with_options, to_mdast, unist::Position, Options, ParseOptions, }; use pretty_assertions::assert_eq; #[test] -fn gfm_strikethrough() -> Result<(), String> { +fn gfm_strikethrough() -> Result<(), message::Message> { assert_eq!( to_html("a ~b~ c"), "

a ~b~ c

", diff --git a/tests/gfm_table.rs b/tests/gfm_table.rs index 887f3d06..ea46cbef 100644 --- a/tests/gfm_table.rs +++ b/tests/gfm_table.rs @@ -1,13 +1,13 @@ use markdown::{ mdast::{AlignKind, InlineCode, Node, Root, Table, TableCell, TableRow, Text}, - to_html, to_html_with_options, to_mdast, + message, to_html, to_html_with_options, to_mdast, unist::Position, CompileOptions, Constructs, Options, ParseOptions, }; use pretty_assertions::assert_eq; #[test] -fn gfm_table() -> Result<(), String> { +fn gfm_table() -> Result<(), message::Message> { assert_eq!( to_html("| a |\n| - |\n| b |"), "

| a |\n| - |\n| b |

", diff --git a/tests/gfm_tagfilter.rs b/tests/gfm_tagfilter.rs index 70e8e2f9..430c4400 100644 --- a/tests/gfm_tagfilter.rs +++ b/tests/gfm_tagfilter.rs @@ -3,11 +3,11 @@ // To do: clippy introduced this in 1.72 but breaks when it fixes it. // Remove when solved. -use markdown::{to_html_with_options, CompileOptions, Options}; +use markdown::{message, to_html_with_options, CompileOptions, Options}; use pretty_assertions::assert_eq; #[test] -fn gfm_tagfilter() -> Result<(), String> { +fn gfm_tagfilter() -> Result<(), message::Message> { assert_eq!( to_html_with_options( "