Skip to content
Permalink

Comparing changes

This is a direct comparison between two commits made in this repository or its related repositories. View the default comparison for this range or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: tafia/quick-xml
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: def940df04e600094fa97c871318f54a26cebf56
Choose a base ref
..
head repository: tafia/quick-xml
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 5817baff9539f44d4ae4f35e0f1cf3fef9d55704
Choose a head ref
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@ include = ["src/*", "LICENSE-MIT.md", "README.md"]
[dependencies]
document-features = { version = "0.2", optional = true }
encoding_rs = { version = "0.8", optional = true }
serde = { version = ">=1.0.100", optional = true }
serde = { version = ">=1.0.139", optional = true }
tokio = { version = "1.10", optional = true, default-features = false, features = ["io-util"] }
memchr = "2.1"
arbitrary = { version = "1", features = ["derive"], optional = true }
@@ -85,7 +85,7 @@ async-tokio = ["tokio"]
## # }
## let xml = to_utf16le_with_bom(r#"<?xml encoding='UTF-16'><element/>"#);
## let mut reader = Reader::from_reader(xml.as_ref());
## reader.trim_text(true);
## reader.config_mut().trim_text(true);
##
## let mut buf = Vec::new();
## let mut unsupported = false;
24 changes: 22 additions & 2 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -10,8 +10,16 @@

## Unreleased

The way to configure parser is changed. Now all configuration is contained in the
`Config` struct and can be applied at once. When `serde-types` feature is enabled,
configuration is serializable.

### New Features

- [#513]: Allow to continue parsing after getting new `Error::IllFormed`.
- [#677]: Added methods `config()` and `config_mut()` to inspect and change the parser
configuration. Previous builder methods on `Reader` / `NsReader` was replaced by
direct access to fields of config using `reader.config_mut().<...>`.
- [#379]: Improved compliance with the XML attribute value normalization process by
adding `Attribute::normalized_value()` and `Attribute::normalized_value_with()`,
which ought to be used in place of `Attribute::unescape_value()` and
@@ -21,9 +29,21 @@

### Misc Changes

- [#675]: Minimum supported version of serde raised to 1.0.139
- [#675]: Rework the `quick_xml::Error` type to provide more accurate information:
- `Error::EndEventMismatch` replaced by `IllFormedError::MismatchedEnd` in some cases
- `Error::EndEventMismatch` replaced by `IllFormedError::UnmatchedEnd` in some cases
- `Error::TextNotFound` was removed because not used
- `Error::UnexpectedBang` replaced by `SyntaxError`
- `Error::UnexpectedEof` replaced by `SyntaxError` in some cases
- `Error::UnexpectedEof` replaced by `IllFormedError` in some cases
- `Error::UnexpectedToken` replaced by `IllFormedError::DoubleHyphenInComment`
- [#379]: Added tests for attribute value normalization

[#379]: https://github.com/tafia/quick-xml/pull/379
[#513]: https://github.com/tafia/quick-xml/issues/513
[#675]: https://github.com/tafia/quick-xml/pull/675
[#677]: https://github.com/tafia/quick-xml/pull/677

## 0.31.0 -- 2023-10-22

@@ -91,14 +111,14 @@ serde >= 1.0.181

### Bug Fixes

- [#618]: Avoid crashing on wrong comments like `<!-->` when using `read_event_into*` functions.
- [#604]: Avoid crashing on wrong comments like `<!-->` when using `read_event_into*` functions.

### Misc Changes

[#604]: https://github.com/tafia/quick-xml/issue/604
[#609]: https://github.com/tafia/quick-xml/pull/609
[#615]: https://github.com/tafia/quick-xml/pull/615
[#617]: https://github.com/tafia/quick-xml/pull/617
[#618]: https://github.com/tafia/quick-xml/pull/618


## 0.29.0 -- 2023-06-13
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -28,7 +28,7 @@ let xml = r#"<tag1 att1 = "test">
<tag2>Test 2</tag2>
</tag1>"#;
let mut reader = Reader::from_str(xml);
reader.trim_text(true);
reader.config_mut().trim_text(true);

let mut count = 0;
let mut txt = Vec::new();
@@ -73,7 +73,7 @@ use std::io::Cursor;

let xml = r#"<this_tag k1="v1" k2="v2"><child>text</child></this_tag>"#;
let mut reader = Reader::from_str(xml);
reader.trim_text(true);
reader.config_mut().trim_text(true);
let mut writer = Writer::new(Cursor::new(Vec::new()));
loop {
match reader.read_event() {
30 changes: 20 additions & 10 deletions benches/microbenches.rs
Original file line number Diff line number Diff line change
@@ -33,7 +33,7 @@ fn read_event(c: &mut Criterion) {
group.bench_function("trim_text = false", |b| {
b.iter(|| {
let mut r = Reader::from_str(SAMPLE);
r.check_end_names(false);
r.config_mut().check_end_names = false;
let mut count = criterion::black_box(0);
loop {
match r.read_event() {
@@ -52,7 +52,9 @@ fn read_event(c: &mut Criterion) {
group.bench_function("trim_text = true", |b| {
b.iter(|| {
let mut r = Reader::from_str(SAMPLE);
r.trim_text(true).check_end_names(false);
let config = r.config_mut();
config.trim_text(true);
config.check_end_names = false;
let mut count = criterion::black_box(0);
loop {
match r.read_event() {
@@ -77,7 +79,7 @@ fn read_resolved_event_into(c: &mut Criterion) {
group.bench_function("trim_text = false", |b| {
b.iter(|| {
let mut r = NsReader::from_str(SAMPLE);
r.check_end_names(false);
r.config_mut().check_end_names = false;
let mut count = criterion::black_box(0);
loop {
match r.read_resolved_event() {
@@ -96,7 +98,9 @@ fn read_resolved_event_into(c: &mut Criterion) {
group.bench_function("trim_text = true", |b| {
b.iter(|| {
let mut r = NsReader::from_str(SAMPLE);
r.trim_text(true).check_end_names(false);
let config = r.config_mut();
config.trim_text(true);
config.check_end_names = false;
let mut count = criterion::black_box(0);
loop {
match r.read_resolved_event() {
@@ -123,7 +127,9 @@ fn one_event(c: &mut Criterion) {
b.iter(|| {
let mut r = Reader::from_str(&src);
let mut nbtxt = criterion::black_box(0);
r.trim_text(true).check_end_names(false);
let config = r.config_mut();
config.trim_text(true);
config.check_end_names = false;
match r.read_event() {
Ok(Event::Start(ref e)) => nbtxt += e.len(),
something_else => panic!("Did not expect {:?}", something_else),
@@ -138,7 +144,9 @@ fn one_event(c: &mut Criterion) {
b.iter(|| {
let mut r = Reader::from_str(&src);
let mut nbtxt = criterion::black_box(0);
r.trim_text(true).check_end_names(false);
let config = r.config_mut();
config.trim_text(true);
config.check_end_names = false;
match r.read_event() {
Ok(Event::Comment(e)) => nbtxt += e.unescape().unwrap().len(),
something_else => panic!("Did not expect {:?}", something_else),
@@ -153,7 +161,9 @@ fn one_event(c: &mut Criterion) {
b.iter(|| {
let mut r = Reader::from_str(&src);
let mut nbtxt = criterion::black_box(0);
r.trim_text(true).check_end_names(false);
let config = r.config_mut();
config.trim_text(true);
config.check_end_names = false;
match r.read_event() {
Ok(Event::CData(ref e)) => nbtxt += e.len(),
something_else => panic!("Did not expect {:?}", something_else),
@@ -171,7 +181,7 @@ fn attributes(c: &mut Criterion) {
group.bench_function("with_checks = true", |b| {
b.iter(|| {
let mut r = Reader::from_str(PLAYERS);
r.check_end_names(false);
r.config_mut().check_end_names = false;
let mut count = criterion::black_box(0);
loop {
match r.read_event() {
@@ -192,7 +202,7 @@ fn attributes(c: &mut Criterion) {
group.bench_function("with_checks = false", |b| {
b.iter(|| {
let mut r = Reader::from_str(PLAYERS);
r.check_end_names(false);
r.config_mut().check_end_names = false;
let mut count = criterion::black_box(0);
loop {
match r.read_event() {
@@ -213,7 +223,7 @@ fn attributes(c: &mut Criterion) {
group.bench_function("try_get_attribute", |b| {
b.iter(|| {
let mut r = Reader::from_str(PLAYERS);
r.check_end_names(false);
r.config_mut().check_end_names = false;
let mut count = criterion::black_box(0);
loop {
match r.read_event() {
2 changes: 1 addition & 1 deletion compare/benches/bench.rs
Original file line number Diff line number Diff line change
@@ -59,7 +59,7 @@ fn low_level_comparison(c: &mut Criterion) {
|b, input| {
b.iter(|| {
let mut r = Reader::from_reader(input.as_bytes());
r.check_end_names(false);
r.config_mut().check_end_names = false;
let mut count = criterion::black_box(0);
let mut buf = Vec::new();
loop {
2 changes: 1 addition & 1 deletion examples/custom_entities.rs
Original file line number Diff line number Diff line change
@@ -25,7 +25,7 @@ const DATA: &str = r#"

fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut reader = Reader::from_str(DATA);
reader.trim_text(true);
reader.config_mut().trim_text(true);

let mut custom_entities: HashMap<String, String> = HashMap::new();
let entity_re = Regex::new(r#"<!ENTITY\s+([^ \t\r\n]+)\s+"([^"]*)"\s*>"#)?;
2 changes: 1 addition & 1 deletion examples/read_buffered.rs
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ fn main() -> Result<(), quick_xml::Error> {
use quick_xml::reader::Reader;

let mut reader = Reader::from_file("tests/documents/document.xml")?;
reader.trim_text(true);
reader.config_mut().trim_text(true);

let mut buf = Vec::new();

35 changes: 29 additions & 6 deletions examples/read_nodes.rs
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@
// Note: for this specific data set using serde feature would simplify
// this simple data is purely to make it easier to understand the code

use quick_xml::events::attributes::AttrError;
use quick_xml::events::{BytesStart, Event};
use quick_xml::name::QName;
use quick_xml::reader::Reader;
@@ -29,6 +30,26 @@ const XML: &str = r#"
</Localization>
"#;

#[derive(Debug)]
enum AppError {
/// XML parsing error
Xml(quick_xml::Error),
/// The `Translation/Text` node is missed
NoText(String),
}

impl From<quick_xml::Error> for AppError {
fn from(error: quick_xml::Error) -> Self {
Self::Xml(error)
}
}

impl From<AttrError> for AppError {
fn from(error: AttrError) -> Self {
Self::Xml(quick_xml::Error::InvalidAttr(error))
}
}

#[derive(Debug)]
struct Translation {
tag: String,
@@ -40,7 +61,7 @@ impl Translation {
fn new_from_element(
reader: &mut Reader<&[u8]>,
element: BytesStart,
) -> Result<Translation, quick_xml::Error> {
) -> Result<Translation, AppError> {
let mut tag = Cow::Borrowed("");
let mut lang = Cow::Borrowed("");

@@ -68,30 +89,32 @@ impl Translation {
} else {
dbg!("Expected Event::Start for Text, got: {:?}", &event);
let name_string = reader.decoder().decode(name.as_ref())?;
Err(quick_xml::Error::UnexpectedToken(name_string.into()))
Err(AppError::NoText(name_string.into()))
}
} else {
let event_string = format!("{:?}", event);
Err(quick_xml::Error::UnexpectedToken(event_string))
Err(AppError::NoText(event_string))
}
}
}

fn main() -> Result<(), quick_xml::Error> {
fn main() -> Result<(), AppError> {
// In a real-world use case, Settings would likely be a struct
// HashMap here is just to make the sample code short
let mut settings: HashMap<String, String>;
let mut translations: Vec<Translation> = Vec::new();

let mut reader = Reader::from_str(XML);
reader.trim_text(true);
let config = reader.config_mut();

config.trim_text(true);
// == Handling empty elements ==
// To simply our processing code
// we want the same events for empty elements, like:
// <DefaultSettings Language="es" Greeting="HELLO"/>
// <Text/>
reader.expand_empty_elements(true);
config.expand_empty_elements = true;

let mut buf = Vec::new();

loop {
2 changes: 1 addition & 1 deletion examples/read_texts.rs
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ fn main() {
<tag1>text3</tag1><tag1><tag2>text4</tag2></tag1>";

let mut reader = Reader::from_str(xml);
reader.trim_text(true);
reader.config_mut().trim_text(true);

loop {
match reader.read_event() {
7 changes: 3 additions & 4 deletions fuzz/fuzz_targets/fuzz_target_1.rs
Original file line number Diff line number Diff line change
@@ -17,10 +17,9 @@ where
{
let mut writer = Writer::new(Cursor::new(Vec::new()));
let mut buf = vec![];
let reader = reader
.expand_empty_elements(true)
.trim_text(true)
.trim_text_end(true);
let config = reader.config_mut();
config.expand_empty_elements = true;
config.trim_text(true);
loop {
let event_result = reader.read_event_into(&mut buf);
if let Ok(ref event) = event_result {
19 changes: 4 additions & 15 deletions fuzz/fuzz_targets/structured_roundtrip.rs
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@
use arbitrary::{Arbitrary, Unstructured};
use libfuzzer_sys::fuzz_target;
use quick_xml::events::{BytesCData, BytesText, Event};
use quick_xml::reader::{NsReader, Reader};
use quick_xml::reader::{Config, NsReader, Reader};
use quick_xml::writer::Writer;
use std::{hint::black_box, io::Cursor};

@@ -41,7 +41,7 @@ enum WriterFunc<'a> {
#[derive(Debug, arbitrary::Arbitrary)]
struct Driver<'a> {
writer_funcs: Vec<WriterFunc<'a>>,
reader_config: Vec<bool>,
reader_config: Config,
}

fn fuzz_round_trip(driver: Driver) -> quick_xml::Result<()> {
@@ -83,13 +83,7 @@ fn fuzz_round_trip(driver: Driver) -> quick_xml::Result<()> {
let xml = writer.into_inner().into_inner();
// The str should be valid as we just generated it, unwrapping **should** be safe.
let mut reader = Reader::from_str(std::str::from_utf8(&xml).unwrap());
let mut config_iter = driver.reader_config.iter();
reader.check_comments(*config_iter.next().unwrap_or(&false));
reader.check_end_names(*config_iter.next().unwrap_or(&false));
reader.expand_empty_elements(*config_iter.next().unwrap_or(&false));
reader.trim_markup_names_in_closing_tags(*config_iter.next().unwrap_or(&false));
reader.trim_text(*config_iter.next().unwrap_or(&false));
reader.trim_text_end(*config_iter.next().unwrap_or(&false));
*reader.config_mut() = driver.reader_config.clone();

loop {
let event = black_box(reader.read_event()?);
@@ -99,12 +93,7 @@ fn fuzz_round_trip(driver: Driver) -> quick_xml::Result<()> {
}

let mut reader = NsReader::from_reader(&xml[..]);
reader.check_comments(*config_iter.next().unwrap_or(&false));
reader.check_end_names(*config_iter.next().unwrap_or(&false));
reader.expand_empty_elements(*config_iter.next().unwrap_or(&false));
reader.trim_markup_names_in_closing_tags(*config_iter.next().unwrap_or(&false));
reader.trim_text(*config_iter.next().unwrap_or(&false));
reader.trim_text_end(*config_iter.next().unwrap_or(&false));
*reader.config_mut() = driver.reader_config;

loop {
let event = black_box(reader.read_event()?);
Loading