Skip to content

Commit 3dc7f1a

Browse files
authored
Add error message struct
Closes GH-108. Closes GH-114. Closes: wooorm/mdxjs-rs#42. Closes: wooorm/mdxjs-rs#43.
1 parent 71361e4 commit 3dc7f1a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+632
-459
lines changed

Diff for: examples/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
fn main() -> Result<(), String> {
1+
fn main() -> Result<(), markdown::message::Message> {
22
// Turn on debugging.
33
// You can show it with `RUST_LOG=debug cargo run --features log --example lib`
44
env_logger::init();

Diff for: generate/src/main.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,12 @@ async fn commonmark() {
7272
// > 👉 **Important**: this module is generated by `generate/src/main.rs`.
7373
// > It is generate from the latest CommonMark website.
7474
75-
use markdown::{{to_html_with_options, CompileOptions, Options}};
75+
use markdown::{{message, to_html_with_options, CompileOptions, Options}};
7676
use pretty_assertions::assert_eq;
7777
7878
#[rustfmt::skip]
7979
#[test]
80-
fn commonmark() -> Result<(), String> {{
80+
fn commonmark() -> Result<(), message::Message> {{
8181
let danger = Options {{
8282
compile: CompileOptions {{
8383
allow_dangerous_html: true,

Diff for: readme.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ Yields:
107107
Extensions (in this case GFM):
108108

109109
```rs
110-
fn main() -> Result<(), String> {
110+
fn main() -> Result<(), markdown::message::Message> {
111111
println!(
112112
"{}",
113113
markdown::to_html_with_options(
@@ -135,7 +135,7 @@ Yields:
135135
Syntax tree ([mdast][]):
136136

137137
```rs
138-
fn main() -> Result<(), String> {
138+
fn main() -> Result<(), markdown::message::Message> {
139139
println!(
140140
"{:?}",
141141
markdown::to_mdast("# Hey, *you*!", &markdown::ParseOptions::default())?
@@ -294,8 +294,8 @@ user-provided markdown opens you up to XSS attacks.
294294
An aspect related to XSS for security is syntax errors: markdown itself has no
295295
syntax errors.
296296
Some syntax extensions (specifically, only MDX) do include syntax errors.
297-
For that reason, `to_html_with_options` returns `Result<String, String>`, of
298-
which the error is a simple string indicating where the problem happened, what
297+
For that reason, `to_html_with_options` returns `Result<String, Message>`, of
298+
which the error is a struct indicating where the problem happened, what
299299
occurred, and what was expected instead.
300300
Make sure to handle your errors when using MDX.
301301

Diff for: src/configuration.rs

+13-13
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,7 @@ pub struct CompileOptions {
482482
///
483483
/// ```
484484
/// use markdown::{to_html, to_html_with_options, CompileOptions, Options};
485-
/// # fn main() -> Result<(), String> {
485+
/// # fn main() -> Result<(), markdown::message::Message> {
486486
///
487487
/// // `markdown-rs` is safe by default:
488488
/// assert_eq!(
@@ -526,7 +526,7 @@ pub struct CompileOptions {
526526
///
527527
/// ```
528528
/// use markdown::{to_html, to_html_with_options, CompileOptions, Options};
529-
/// # fn main() -> Result<(), String> {
529+
/// # fn main() -> Result<(), markdown::message::Message> {
530530
///
531531
/// // `markdown-rs` is safe by default:
532532
/// assert_eq!(
@@ -570,7 +570,7 @@ pub struct CompileOptions {
570570
///
571571
/// ```
572572
/// use markdown::{to_html, to_html_with_options, CompileOptions, LineEnding, Options};
573-
/// # fn main() -> Result<(), String> {
573+
/// # fn main() -> Result<(), markdown::message::Message> {
574574
///
575575
/// // `markdown-rs` uses `\n` by default:
576576
/// assert_eq!(
@@ -612,7 +612,7 @@ pub struct CompileOptions {
612612
///
613613
/// ```
614614
/// use markdown::{to_html_with_options, CompileOptions, Options, ParseOptions};
615-
/// # fn main() -> Result<(), String> {
615+
/// # fn main() -> Result<(), markdown::message::Message> {
616616
///
617617
/// // `"Footnotes"` is used by default:
618618
/// assert_eq!(
@@ -657,7 +657,7 @@ pub struct CompileOptions {
657657
///
658658
/// ```
659659
/// use markdown::{to_html_with_options, CompileOptions, Options, ParseOptions};
660-
/// # fn main() -> Result<(), String> {
660+
/// # fn main() -> Result<(), markdown::message::Message> {
661661
///
662662
/// // `"h2"` is used by default:
663663
/// assert_eq!(
@@ -705,7 +705,7 @@ pub struct CompileOptions {
705705
///
706706
/// ```
707707
/// use markdown::{to_html_with_options, CompileOptions, Options, ParseOptions};
708-
/// # fn main() -> Result<(), String> {
708+
/// # fn main() -> Result<(), markdown::message::Message> {
709709
///
710710
/// // `"class=\"sr-only\""` is used by default:
711711
/// assert_eq!(
@@ -748,7 +748,7 @@ pub struct CompileOptions {
748748
///
749749
/// ```
750750
/// use markdown::{to_html_with_options, CompileOptions, Options, ParseOptions};
751-
/// # fn main() -> Result<(), String> {
751+
/// # fn main() -> Result<(), markdown::message::Message> {
752752
///
753753
/// // `"Back to content"` is used by default:
754754
/// assert_eq!(
@@ -804,7 +804,7 @@ pub struct CompileOptions {
804804
///
805805
/// ```
806806
/// use markdown::{to_html_with_options, CompileOptions, Options, ParseOptions};
807-
/// # fn main() -> Result<(), String> {
807+
/// # fn main() -> Result<(), markdown::message::Message> {
808808
///
809809
/// // `"user-content-"` is used by default:
810810
/// assert_eq!(
@@ -843,7 +843,7 @@ pub struct CompileOptions {
843843
///
844844
/// ```
845845
/// use markdown::{to_html_with_options, CompileOptions, Options, ParseOptions};
846-
/// # fn main() -> Result<(), String> {
846+
/// # fn main() -> Result<(), markdown::message::Message> {
847847
///
848848
/// // With `gfm_task_list_item_checkable`, generated `<input type="checkbox" />`
849849
/// // tags do not contain the attribute `disabled=""` and are thus toggleable by
@@ -880,7 +880,7 @@ pub struct CompileOptions {
880880
///
881881
/// ```
882882
/// use markdown::{to_html_with_options, CompileOptions, Options, ParseOptions};
883-
/// # fn main() -> Result<(), String> {
883+
/// # fn main() -> Result<(), markdown::message::Message> {
884884
///
885885
/// // With `allow_dangerous_html`, `markdown-rs` passes HTML through untouched:
886886
/// assert_eq!(
@@ -975,7 +975,7 @@ pub struct ParseOptions {
975975
///
976976
/// ```
977977
/// use markdown::{to_html, to_html_with_options, Constructs, Options, ParseOptions};
978-
/// # fn main() -> Result<(), String> {
978+
/// # fn main() -> Result<(), markdown::message::Message> {
979979
///
980980
/// // `markdown-rs` follows CommonMark by default:
981981
/// assert_eq!(
@@ -1020,7 +1020,7 @@ pub struct ParseOptions {
10201020
///
10211021
/// ```
10221022
/// use markdown::{to_html_with_options, Constructs, Options, ParseOptions};
1023-
/// # fn main() -> Result<(), String> {
1023+
/// # fn main() -> Result<(), markdown::message::Message> {
10241024
///
10251025
/// // `markdown-rs` supports single tildes by default:
10261026
/// assert_eq!(
@@ -1075,7 +1075,7 @@ pub struct ParseOptions {
10751075
///
10761076
/// ```
10771077
/// use markdown::{to_html_with_options, Constructs, Options, ParseOptions};
1078-
/// # fn main() -> Result<(), String> {
1078+
/// # fn main() -> Result<(), markdown::message::Message> {
10791079
///
10801080
/// // `markdown-rs` supports single dollars by default:
10811081
/// assert_eq!(

Diff for: src/construct/content.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,12 @@
2424
//! [paragraph]: crate::construct::paragraph
2525
2626
use crate::event::{Content, Kind, Link, Name};
27+
use crate::message;
2728
use crate::resolve::Name as ResolveName;
2829
use crate::state::{Name as StateName, State};
2930
use crate::subtokenize::{subtokenize, Subresult};
3031
use crate::tokenizer::Tokenizer;
31-
use alloc::{string::String, vec};
32+
use alloc::vec;
3233

3334
/// Before a content chunk.
3435
///
@@ -110,7 +111,7 @@ pub fn definition_after(tokenizer: &mut Tokenizer) -> State {
110111

111112
/// Merge `Content` chunks, which currently span a single line, into actual
112113
/// `Content`s that span multiple lines.
113-
pub fn resolve(tokenizer: &mut Tokenizer) -> Result<Option<Subresult>, String> {
114+
pub fn resolve(tokenizer: &mut Tokenizer) -> Result<Option<Subresult>, message::Message> {
114115
let mut index = 0;
115116

116117
while index < tokenizer.events.len() {

Diff for: src/construct/document.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@
1010
//! * [GFM: Footnote definition][crate::construct::gfm_footnote_definition]
1111
1212
use crate::event::{Content, Event, Kind, Link, Name};
13+
use crate::message;
1314
use crate::state::{Name as StateName, State};
1415
use crate::subtokenize::divide_events;
1516
use crate::tokenizer::{Container, ContainerState, Tokenizer};
1617
use crate::util::skip;
17-
use alloc::{boxed::Box, string::String, vec::Vec};
18+
use alloc::{boxed::Box, vec::Vec};
1819

1920
/// Phases where we can exit containers.
2021
#[derive(Debug, PartialEq)]
@@ -470,7 +471,7 @@ pub fn flow_end(tokenizer: &mut Tokenizer) -> State {
470471
}
471472

472473
/// Close containers (and flow if needed).
473-
fn exit_containers(tokenizer: &mut Tokenizer, phase: &Phase) -> Result<(), String> {
474+
fn exit_containers(tokenizer: &mut Tokenizer, phase: &Phase) -> Result<(), message::Message> {
474475
let mut stack_close = tokenizer
475476
.tokenize_state
476477
.document_container_stack

Diff for: src/construct/mdx_esm.rs

+17-9
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,12 @@
3030
//! [parse_options]: crate::ParseOptions
3131
3232
use crate::event::Name;
33+
use crate::message;
3334
use crate::state::{Name as StateName, State};
3435
use crate::tokenizer::Tokenizer;
3536
use crate::util::{mdx_collect::collect, slice::Slice};
3637
use crate::MdxSignal;
37-
use alloc::format;
38+
use alloc::boxed::Box;
3839

3940
/// Start of MDX ESM.
4041
///
@@ -205,24 +206,31 @@ fn parse_esm(tokenizer: &mut Tokenizer) -> State {
205206
// Parse and handle what was signaled back.
206207
match parse(&result.value) {
207208
MdxSignal::Ok => State::Ok,
208-
MdxSignal::Error(message, relative) => {
209+
MdxSignal::Error(message, relative, source, rule_id) => {
209210
let point = tokenizer
210211
.parse_state
211212
.location
212213
.as_ref()
213214
.expect("expected location index if aware mdx is on")
214215
.relative_to_point(&result.stops, relative)
215216
.expect("expected non-empty string");
216-
State::Error(format!("{}:{}: {}", point.line, point.column, message))
217+
State::Error(message::Message {
218+
place: Some(Box::new(message::Place::Point(point))),
219+
reason: message,
220+
source,
221+
rule_id,
222+
})
217223
}
218-
MdxSignal::Eof(message) => {
224+
MdxSignal::Eof(message, source, rule_id) => {
219225
if tokenizer.current.is_none() {
220-
State::Error(format!(
221-
"{}:{}: {}",
222-
tokenizer.point.line, tokenizer.point.column, message
223-
))
226+
State::Error(message::Message {
227+
place: Some(Box::new(message::Place::Point(tokenizer.point.to_unist()))),
228+
reason: message,
229+
source,
230+
rule_id,
231+
})
224232
} else {
225-
tokenizer.tokenize_state.mdx_last_parse_error = Some(message);
233+
tokenizer.tokenize_state.mdx_last_parse_error = Some((message, *source, *rule_id));
226234
State::Retry(StateName::MdxEsmContinuationStart)
227235
}
228236
}

Diff for: src/construct/partial_mdx_expression.rs

+29-18
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,12 @@
5858
5959
use crate::construct::partial_space_or_tab::space_or_tab_min_max;
6060
use crate::event::Name;
61+
use crate::message;
6162
use crate::state::{Name as StateName, State};
6263
use crate::tokenizer::Tokenizer;
6364
use crate::util::{constant::TAB_SIZE, mdx_collect::collect};
6465
use crate::{MdxExpressionKind, MdxExpressionParse, MdxSignal};
65-
use alloc::format;
66+
use alloc::boxed::Box;
6667

6768
/// Start of an MDX expression.
6869
///
@@ -89,12 +90,15 @@ pub fn start(tokenizer: &mut Tokenizer) -> State {
8990
pub fn before(tokenizer: &mut Tokenizer) -> State {
9091
match tokenizer.current {
9192
None => {
92-
State::Error(format!(
93-
"{}:{}: {}",
94-
tokenizer.point.line, tokenizer.point.column,
95-
tokenizer.tokenize_state.mdx_last_parse_error.take()
96-
.unwrap_or_else(|| "Unexpected end of file in expression, expected a corresponding closing brace for `{`".into())
97-
))
93+
let problem = tokenizer.tokenize_state.mdx_last_parse_error.take()
94+
.unwrap_or_else(|| ("Unexpected end of file in expression, expected a corresponding closing brace for `{`".into(), "markdown-rs".into(), "unexpected-eof".into()));
95+
96+
State::Error(message::Message {
97+
place: Some(Box::new(message::Place::Point(tokenizer.point.to_unist()))),
98+
reason: problem.0,
99+
rule_id: Box::new(problem.2),
100+
source: Box::new(problem.1),
101+
})
98102
}
99103
Some(b'\n') => {
100104
tokenizer.enter(Name::LineEnding);
@@ -167,10 +171,14 @@ pub fn eol_after(tokenizer: &mut Tokenizer) -> State {
167171
|| tokenizer.tokenize_state.token_2 == Name::MdxJsxFlowTag)
168172
&& tokenizer.lazy
169173
{
170-
State::Error(format!(
171-
"{}:{}: Unexpected lazy line in expression in container, expected line to be prefixed with `>` when in a block quote, whitespace when in a list, etc",
172-
tokenizer.point.line, tokenizer.point.column
173-
))
174+
State::Error(
175+
message::Message {
176+
place: Some(Box::new(message::Place::Point(tokenizer.point.to_unist()))),
177+
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(),
178+
source: Box::new("markdown-rs".into()),
179+
rule_id: Box::new("unexpected-lazy".into()),
180+
}
181+
)
174182
} else if matches!(tokenizer.current, Some(b'\t' | b' ')) {
175183
tokenizer.attempt(State::Next(StateName::MdxExpressionBefore), State::Nok);
176184
// Idea: investigate if we’d need to use more complex stripping.
@@ -220,21 +228,24 @@ fn parse_expression(tokenizer: &mut Tokenizer, parse: &MdxExpressionParse) -> St
220228
// Parse and handle what was signaled back.
221229
match parse(&result.value, &kind) {
222230
MdxSignal::Ok => State::Ok,
223-
MdxSignal::Error(message, relative) => {
231+
MdxSignal::Error(reason, relative, source, rule_id) => {
224232
let point = tokenizer
225233
.parse_state
226234
.location
227235
.as_ref()
228236
.expect("expected location index if aware mdx is on")
229237
.relative_to_point(&result.stops, relative)
230-
.map_or((tokenizer.point.line, tokenizer.point.column), |d| {
231-
(d.line, d.column)
232-
});
238+
.unwrap_or_else(|| tokenizer.point.to_unist());
233239

234-
State::Error(format!("{}:{}: {}", point.0, point.1, message))
240+
State::Error(message::Message {
241+
place: Some(Box::new(message::Place::Point(point))),
242+
reason,
243+
rule_id,
244+
source,
245+
})
235246
}
236-
MdxSignal::Eof(message) => {
237-
tokenizer.tokenize_state.mdx_last_parse_error = Some(message);
247+
MdxSignal::Eof(reason, source, rule_id) => {
248+
tokenizer.tokenize_state.mdx_last_parse_error = Some((reason, *source, *rule_id));
238249
tokenizer.enter(Name::MdxExpressionData);
239250
tokenizer.consume();
240251
State::Next(StateName::MdxExpressionInside)

0 commit comments

Comments
 (0)