Skip to content

Commit

Permalink
Accept Decoder instead of Reader when decoding attributes.
Browse files Browse the repository at this point in the history
This will allow decode them even if Reader does not exists, for example,
when you only write events.
  • Loading branch information
Mingun committed Jun 23, 2024
1 parent 31ca532 commit 2553b62
Show file tree
Hide file tree
Showing 6 changed files with 24 additions and 18 deletions.
5 changes: 5 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@

### Misc Changes

- [#760]: `Attribute::decode_and_unescape_value` and `Attribute::decode_and_unescape_value_with` now
accepts `Decoder` instead of `Reader`. Use `Reader::decoder()` to get it.

[#760]: https://github.com/tafia/quick-xml/pull/760


## 0.33.0 -- 2024-06-21

Expand Down
8 changes: 4 additions & 4 deletions benches/macrobenches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ fn parse_document_from_str(doc: &str) -> XmlResult<()> {
match criterion::black_box(r.read_event()?) {
Event::Start(e) | Event::Empty(e) => {
for attr in e.attributes() {
criterion::black_box(attr?.decode_and_unescape_value(&r)?);
criterion::black_box(attr?.decode_and_unescape_value(r.decoder())?);
}
}
Event::Text(e) => {
Expand All @@ -75,7 +75,7 @@ fn parse_document_from_bytes(doc: &[u8]) -> XmlResult<()> {
match criterion::black_box(r.read_event_into(&mut buf)?) {
Event::Start(e) | Event::Empty(e) => {
for attr in e.attributes() {
criterion::black_box(attr?.decode_and_unescape_value(&r)?);
criterion::black_box(attr?.decode_and_unescape_value(r.decoder())?);
}
}
Event::Text(e) => {
Expand All @@ -101,7 +101,7 @@ fn parse_document_from_str_with_namespaces(doc: &str) -> XmlResult<()> {
(resolved_ns, Event::Start(e) | Event::Empty(e)) => {
criterion::black_box(resolved_ns);
for attr in e.attributes() {
criterion::black_box(attr?.decode_and_unescape_value(&r)?);
criterion::black_box(attr?.decode_and_unescape_value(r.decoder())?);
}
}
(resolved_ns, Event::Text(e)) => {
Expand Down Expand Up @@ -129,7 +129,7 @@ fn parse_document_from_bytes_with_namespaces(doc: &[u8]) -> XmlResult<()> {
(resolved_ns, Event::Start(e) | Event::Empty(e)) => {
criterion::black_box(resolved_ns);
for attr in e.attributes() {
criterion::black_box(attr?.decode_and_unescape_value(&r)?);
criterion::black_box(attr?.decode_and_unescape_value(r.decoder())?);
}
}
(resolved_ns, Event::Text(e)) => {
Expand Down
2 changes: 1 addition & 1 deletion examples/custom_entities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
.attributes()
.map(|a| {
a.unwrap()
.decode_and_unescape_value_with(&reader, |ent| {
.decode_and_unescape_value_with(reader.decoder(), |ent| {
custom_entities.get(ent).map(|s| s.as_str())
})
.unwrap()
Expand Down
6 changes: 3 additions & 3 deletions examples/read_nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ impl Translation {
for attr_result in element.attributes() {
let a = attr_result?;
match a.key.as_ref() {
b"Language" => lang = a.decode_and_unescape_value(reader)?,
b"Tag" => tag = a.decode_and_unescape_value(reader)?,
b"Language" => lang = a.decode_and_unescape_value(reader.decoder())?,
b"Tag" => tag = a.decode_and_unescape_value(reader.decoder())?,
_ => (),
}
}
Expand Down Expand Up @@ -138,7 +138,7 @@ fn main() -> Result<(), AppError> {
Ok::<Cow<'_, str>, Infallible>(std::borrow::Cow::from(""))
})
.unwrap().to_string();
let value = a.decode_and_unescape_value(&reader).or_else(|err| {
let value = a.decode_and_unescape_value(reader.decoder()).or_else(|err| {
dbg!("unable to read key in DefaultSettings attribute {:?}, utf8 error {:?}", &a, err);
Ok::<Cow<'_, str>, Infallible>(std::borrow::Cow::from(""))
}).unwrap().to_string();
Expand Down
15 changes: 8 additions & 7 deletions src/events/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
//!
//! Provides an iterator over attributes key/value pairs
use crate::encoding::Decoder;
use crate::errors::Result as XmlResult;
use crate::escape::{escape, resolve_predefined_entity, unescape_with};
use crate::name::QName;
use crate::reader::Reader;
use crate::utils::{is_whitespace, write_byte_string, write_cow_string, Bytes};

use std::fmt::{self, Debug, Display, Formatter};
use std::iter::FusedIterator;
use std::{borrow::Cow, ops::Range};
Expand Down Expand Up @@ -84,23 +85,23 @@ impl<'a> Attribute<'a> {
///
/// This will allocate if the value contains any escape sequences or in
/// non-UTF-8 encoding.
pub fn decode_and_unescape_value<B>(&self, reader: &Reader<B>) -> XmlResult<Cow<'a, str>> {
self.decode_and_unescape_value_with(reader, resolve_predefined_entity)
pub fn decode_and_unescape_value(&self, decoder: Decoder) -> XmlResult<Cow<'a, str>> {
self.decode_and_unescape_value_with(decoder, resolve_predefined_entity)
}

/// Decodes then unescapes the value with custom entities.
///
/// This will allocate if the value contains any escape sequences or in
/// non-UTF-8 encoding.
pub fn decode_and_unescape_value_with<'entity, B>(
pub fn decode_and_unescape_value_with<'entity>(
&self,
reader: &Reader<B>,
decoder: Decoder,
resolve_entity: impl FnMut(&str) -> Option<&'entity str>,
) -> XmlResult<Cow<'a, str>> {
let decoded = match &self.value {
Cow::Borrowed(bytes) => reader.decoder().decode(bytes)?,
Cow::Borrowed(bytes) => decoder.decode(bytes)?,
// Convert to owned, because otherwise Cow will be bound with wrong lifetime
Cow::Owned(bytes) => reader.decoder().decode(bytes)?.into_owned().into(),
Cow::Owned(bytes) => decoder.decode(bytes)?.into_owned().into(),
};

match unescape_with(&decoded, resolve_entity)? {
Expand Down
6 changes: 3 additions & 3 deletions tests/fuzzing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ fn fuzz_101() {
match reader.read_event_into(&mut buf) {
Ok(Event::Start(e)) | Ok(Event::Empty(e)) => {
for a in e.attributes() {
if a.ok()
.map_or(true, |a| a.decode_and_unescape_value(&reader).is_err())
{
if a.ok().map_or(true, |a| {
a.decode_and_unescape_value(reader.decoder()).is_err()
}) {
break;
}
}
Expand Down

0 comments on commit 2553b62

Please sign in to comment.