Skip to content

Commit e9b7716

Browse files
committed
Add allow_unmatched_ends reader config option
1 parent 7f86e57 commit e9b7716

File tree

3 files changed

+49
-6
lines changed

3 files changed

+49
-6
lines changed

src/reader/mod.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,25 @@ pub struct Config {
7676
/// [`expand_empty_elements`]: Self::expand_empty_elements
7777
pub check_end_names: bool,
7878

79+
/// Whether unmatched closing tag names should be allowed. Unless enabled,
80+
/// in case of a dangling end tag, the [`Error::IllFormed(UnmatchedEndTag)`]
81+
/// is returned from read methods.
82+
///
83+
/// When set to `true`, it won't check if a closing tag has a corresponding
84+
/// opening tag at all. For example, `<a></a></b>` will be permitted.
85+
///
86+
/// Note that the emitted [`End`] event will not be modified if this is enabled,
87+
/// ie. it will contain the data of the unmatched end tag.
88+
///
89+
/// Note, that setting this to `true` will lead to additional allocates that
90+
/// needed to store tag name for an [`End`] event.
91+
///
92+
/// Default: `false`
93+
///
94+
/// [`Error::IllFormed(UnmatchedEndTag)`]: crate::errors::IllFormedError::UnmatchedEndTag
95+
/// [`End`]: crate::events::Event::End
96+
pub allow_unmatched_ends: bool,
97+
7998
/// Whether empty elements should be split into an `Open` and a `Close` event.
8099
///
81100
/// When set to `true`, all [`Empty`] events produced by a self-closing tag
@@ -192,6 +211,7 @@ impl Default for Config {
192211
Self {
193212
check_comments: false,
194213
check_end_names: true,
214+
allow_unmatched_ends: false,
195215
expand_empty_elements: false,
196216
trim_markup_names_in_closing_tags: true,
197217
trim_text_start: false,

src/reader/state.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -212,12 +212,14 @@ impl ReaderState {
212212
self.opened_buffer.truncate(start);
213213
}
214214
None => {
215-
// Report error at start of the end tag at `<` character
216-
// -2 for `<` and `>`
217-
self.last_error_offset = self.offset - buf.len() as u64 - 2;
218-
return Err(Error::IllFormed(IllFormedError::UnmatchedEndTag(
219-
decoder.decode(name).unwrap_or_default().into_owned(),
220-
)));
215+
if !self.config.allow_unmatched_ends {
216+
// Report error at start of the end tag at `<` character
217+
// -2 for `<` and `>`
218+
self.last_error_offset = self.offset - buf.len() as u64 - 2;
219+
return Err(Error::IllFormed(IllFormedError::UnmatchedEndTag(
220+
decoder.decode(name).unwrap_or_default().into_owned(),
221+
)));
222+
}
221223
}
222224
}
223225

tests/reader-config.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,27 @@ mod check_end_names {
344344
);
345345
assert_eq!(reader.read_event().unwrap(), Event::Eof);
346346
}
347+
348+
#[test]
349+
fn unmatched_end_tags() {
350+
let mut reader = Reader::from_str("<tag></tag></unmatched>");
351+
reader.config_mut().allow_unmatched_ends = true;
352+
353+
assert_eq!(
354+
reader.read_event().unwrap(),
355+
Event::Start(BytesStart::new("tag"))
356+
);
357+
assert_eq!(
358+
reader.read_event().unwrap(),
359+
Event::End(BytesEnd::new("tag"))
360+
);
361+
// #770: We want to allow this
362+
assert_eq!(
363+
reader.read_event().unwrap(),
364+
Event::End(BytesEnd::new("unmatched"))
365+
);
366+
assert_eq!(reader.read_event().unwrap(), Event::Eof);
367+
}
347368
}
348369
}
349370

0 commit comments

Comments
 (0)