Skip to content

Commit 4674244

Browse files
authored
Merge pull request #760 from Mingun/in-mind-of-write
Remove requirement to have Reader when decoding attributes and write anything converted to Event
2 parents 649f3d8 + 55f7aa1 commit 4674244

File tree

12 files changed

+59
-53
lines changed

12 files changed

+59
-53
lines changed

Changelog.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@
1919

2020
### Misc Changes
2121

22+
- [#760]: `Attribute::decode_and_unescape_value` and `Attribute::decode_and_unescape_value_with` now
23+
accepts `Decoder` instead of `Reader`. Use `Reader::decoder()` to get it.
24+
- [#760]: `Writer::write_event` now consumes event. Use `Event::borrow()` if you want to keep ownership.
25+
26+
[#760]: https://github.com/tafia/quick-xml/pull/760
27+
2228

2329
## 0.33.0 -- 2024-06-21
2430

benches/macrobenches.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ fn parse_document_from_str(doc: &str) -> XmlResult<()> {
5050
match criterion::black_box(r.read_event()?) {
5151
Event::Start(e) | Event::Empty(e) => {
5252
for attr in e.attributes() {
53-
criterion::black_box(attr?.decode_and_unescape_value(&r)?);
53+
criterion::black_box(attr?.decode_and_unescape_value(r.decoder())?);
5454
}
5555
}
5656
Event::Text(e) => {
@@ -75,7 +75,7 @@ fn parse_document_from_bytes(doc: &[u8]) -> XmlResult<()> {
7575
match criterion::black_box(r.read_event_into(&mut buf)?) {
7676
Event::Start(e) | Event::Empty(e) => {
7777
for attr in e.attributes() {
78-
criterion::black_box(attr?.decode_and_unescape_value(&r)?);
78+
criterion::black_box(attr?.decode_and_unescape_value(r.decoder())?);
7979
}
8080
}
8181
Event::Text(e) => {
@@ -101,7 +101,7 @@ fn parse_document_from_str_with_namespaces(doc: &str) -> XmlResult<()> {
101101
(resolved_ns, Event::Start(e) | Event::Empty(e)) => {
102102
criterion::black_box(resolved_ns);
103103
for attr in e.attributes() {
104-
criterion::black_box(attr?.decode_and_unescape_value(&r)?);
104+
criterion::black_box(attr?.decode_and_unescape_value(r.decoder())?);
105105
}
106106
}
107107
(resolved_ns, Event::Text(e)) => {
@@ -129,7 +129,7 @@ fn parse_document_from_bytes_with_namespaces(doc: &[u8]) -> XmlResult<()> {
129129
(resolved_ns, Event::Start(e) | Event::Empty(e)) => {
130130
criterion::black_box(resolved_ns);
131131
for attr in e.attributes() {
132-
criterion::black_box(attr?.decode_and_unescape_value(&r)?);
132+
criterion::black_box(attr?.decode_and_unescape_value(r.decoder())?);
133133
}
134134
}
135135
(resolved_ns, Event::Text(e)) => {

examples/custom_entities.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
4747
.attributes()
4848
.map(|a| {
4949
a.unwrap()
50-
.decode_and_unescape_value_with(&reader, |ent| {
50+
.decode_and_unescape_value_with(reader.decoder(), |ent| {
5151
custom_entities.get(ent).map(|s| s.as_str())
5252
})
5353
.unwrap()

examples/read_nodes.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ impl Translation {
7070
for attr_result in element.attributes() {
7171
let a = attr_result?;
7272
match a.key.as_ref() {
73-
b"Language" => lang = a.decode_and_unescape_value(reader)?,
74-
b"Tag" => tag = a.decode_and_unescape_value(reader)?,
73+
b"Language" => lang = a.decode_and_unescape_value(reader.decoder())?,
74+
b"Tag" => tag = a.decode_and_unescape_value(reader.decoder())?,
7575
_ => (),
7676
}
7777
}
@@ -138,7 +138,7 @@ fn main() -> Result<(), AppError> {
138138
Ok::<Cow<'_, str>, Infallible>(std::borrow::Cow::from(""))
139139
})
140140
.unwrap().to_string();
141-
let value = a.decode_and_unescape_value(&reader).or_else(|err| {
141+
let value = a.decode_and_unescape_value(reader.decoder()).or_else(|err| {
142142
dbg!("unable to read key in DefaultSettings attribute {:?}, utf8 error {:?}", &a, err);
143143
Ok::<Cow<'_, str>, Infallible>(std::borrow::Cow::from(""))
144144
}).unwrap().to_string();

fuzz/fuzz_targets/fuzz_target_1.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ where
2626
let _event = black_box(event.borrow());
2727
let _event = black_box(event.as_ref());
2828
debug_format!(event);
29-
debug_format!(writer.write_event(event));
29+
debug_format!(writer.write_event(event.borrow()));
3030
}
3131
match event_result {
3232
Ok(Event::Start(ref e)) | Ok(Event::Empty(ref e)) => {

fuzz/fuzz_targets/structured_roundtrip.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ fn fuzz_round_trip(driver: Driver) -> quick_xml::Result<()> {
5151
// TODO: Handle error cases.
5252
use WriterFunc::*;
5353
match writer_func {
54-
WriteEvent(event) => writer.write_event(event)?,
54+
WriteEvent(event) => writer.write_event(event.borrow())?,
5555
WriteBom => writer.write_bom()?,
5656
WriteIndent => writer.write_indent()?,
5757
CreateElement {

src/events/attributes.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22
//!
33
//! Provides an iterator over attributes key/value pairs
44
5+
use crate::encoding::Decoder;
56
use crate::errors::Result as XmlResult;
67
use crate::escape::{escape, resolve_predefined_entity, unescape_with};
78
use crate::name::QName;
8-
use crate::reader::Reader;
99
use crate::utils::{is_whitespace, write_byte_string, write_cow_string, Bytes};
10+
1011
use std::fmt::{self, Debug, Display, Formatter};
1112
use std::iter::FusedIterator;
1213
use std::{borrow::Cow, ops::Range};
@@ -84,23 +85,23 @@ impl<'a> Attribute<'a> {
8485
///
8586
/// This will allocate if the value contains any escape sequences or in
8687
/// non-UTF-8 encoding.
87-
pub fn decode_and_unescape_value<B>(&self, reader: &Reader<B>) -> XmlResult<Cow<'a, str>> {
88-
self.decode_and_unescape_value_with(reader, resolve_predefined_entity)
88+
pub fn decode_and_unescape_value(&self, decoder: Decoder) -> XmlResult<Cow<'a, str>> {
89+
self.decode_and_unescape_value_with(decoder, resolve_predefined_entity)
8990
}
9091

9192
/// Decodes then unescapes the value with custom entities.
9293
///
9394
/// This will allocate if the value contains any escape sequences or in
9495
/// non-UTF-8 encoding.
95-
pub fn decode_and_unescape_value_with<'entity, B>(
96+
pub fn decode_and_unescape_value_with<'entity>(
9697
&self,
97-
reader: &Reader<B>,
98+
decoder: Decoder,
9899
resolve_entity: impl FnMut(&str) -> Option<&'entity str>,
99100
) -> XmlResult<Cow<'a, str>> {
100101
let decoded = match &self.value {
101-
Cow::Borrowed(bytes) => reader.decoder().decode(bytes)?,
102+
Cow::Borrowed(bytes) => decoder.decode(bytes)?,
102103
// Convert to owned, because otherwise Cow will be bound with wrong lifetime
103-
Cow::Owned(bytes) => reader.decoder().decode(bytes)?.into_owned().into(),
104+
Cow::Owned(bytes) => decoder.decode(bytes)?.into_owned().into(),
104105
};
105106

106107
match unescape_with(&decoded, resolve_entity)? {

src/reader/ns_reader.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,6 @@ impl<R> NsReader<R> {
337337
/// ```
338338
/// # use pretty_assertions::assert_eq;
339339
/// use quick_xml::events::Event;
340-
/// use quick_xml::events::attributes::Attribute;
341340
/// use quick_xml::name::{Namespace, QName, ResolveResult::*};
342341
/// use quick_xml::reader::NsReader;
343342
///

src/writer.rs

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ use {crate::de::DeError, serde::Serialize};
5151
/// },
5252
/// Ok(Event::Eof) => break,
5353
/// // we can either move or borrow the event to write, depending on your use-case
54-
/// Ok(e) => assert!(writer.write_event(e).is_ok()),
54+
/// Ok(e) => assert!(writer.write_event(e.borrow()).is_ok()),
5555
/// Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e),
5656
/// }
5757
/// }
@@ -192,37 +192,37 @@ impl<W: Write> Writer<W> {
192192
}
193193

194194
/// Writes the given event to the underlying writer.
195-
pub fn write_event<'a, E: AsRef<Event<'a>>>(&mut self, event: E) -> Result<()> {
195+
pub fn write_event<'a, E: Into<Event<'a>>>(&mut self, event: E) -> Result<()> {
196196
let mut next_should_line_break = true;
197-
let result = match *event.as_ref() {
198-
Event::Start(ref e) => {
199-
let result = self.write_wrapped(b"<", e, b">");
197+
let result = match event.into() {
198+
Event::Start(e) => {
199+
let result = self.write_wrapped(b"<", &e, b">");
200200
if let Some(i) = self.indent.as_mut() {
201201
i.grow();
202202
}
203203
result
204204
}
205-
Event::End(ref e) => {
205+
Event::End(e) => {
206206
if let Some(i) = self.indent.as_mut() {
207207
i.shrink();
208208
}
209-
self.write_wrapped(b"</", e, b">")
209+
self.write_wrapped(b"</", &e, b">")
210210
}
211-
Event::Empty(ref e) => self.write_wrapped(b"<", e, b"/>"),
212-
Event::Text(ref e) => {
211+
Event::Empty(e) => self.write_wrapped(b"<", &e, b"/>"),
212+
Event::Text(e) => {
213213
next_should_line_break = false;
214-
self.write(e)
214+
self.write(&e)
215215
}
216-
Event::Comment(ref e) => self.write_wrapped(b"<!--", e, b"-->"),
217-
Event::CData(ref e) => {
216+
Event::Comment(e) => self.write_wrapped(b"<!--", &e, b"-->"),
217+
Event::CData(e) => {
218218
next_should_line_break = false;
219219
self.write(b"<![CDATA[")?;
220-
self.write(e)?;
220+
self.write(&e)?;
221221
self.write(b"]]>")
222222
}
223-
Event::Decl(ref e) => self.write_wrapped(b"<?", e, b"?>"),
224-
Event::PI(ref e) => self.write_wrapped(b"<?", e, b"?>"),
225-
Event::DocType(ref e) => self.write_wrapped(b"<!DOCTYPE ", e, b">"),
223+
Event::Decl(e) => self.write_wrapped(b"<?", &e, b"?>"),
224+
Event::PI(e) => self.write_wrapped(b"<?", &e, b"?>"),
225+
Event::DocType(e) => self.write_wrapped(b"<!DOCTYPE ", &e, b">"),
226226
Event::Eof => Ok(()),
227227
};
228228
if let Some(i) = self.indent.as_mut() {

src/writer/async_tokio.rs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,37 +9,37 @@ use crate::{ElementWriter, Writer};
99

1010
impl<W: AsyncWrite + Unpin> Writer<W> {
1111
/// Writes the given event to the underlying writer. Async version of [`Writer::write_event`].
12-
pub async fn write_event_async<'a, E: AsRef<Event<'a>>>(&mut self, event: E) -> Result<()> {
12+
pub async fn write_event_async<'a, E: Into<Event<'a>>>(&mut self, event: E) -> Result<()> {
1313
let mut next_should_line_break = true;
14-
let result = match *event.as_ref() {
15-
Event::Start(ref e) => {
16-
let result = self.write_wrapped_async(b"<", e, b">").await;
14+
let result = match event.into() {
15+
Event::Start(e) => {
16+
let result = self.write_wrapped_async(b"<", &e, b">").await;
1717
if let Some(i) = self.indent.as_mut() {
1818
i.grow();
1919
}
2020
result
2121
}
22-
Event::End(ref e) => {
22+
Event::End(e) => {
2323
if let Some(i) = self.indent.as_mut() {
2424
i.shrink();
2525
}
26-
self.write_wrapped_async(b"</", e, b">").await
26+
self.write_wrapped_async(b"</", &e, b">").await
2727
}
28-
Event::Empty(ref e) => self.write_wrapped_async(b"<", e, b"/>").await,
29-
Event::Text(ref e) => {
28+
Event::Empty(e) => self.write_wrapped_async(b"<", &e, b"/>").await,
29+
Event::Text(e) => {
3030
next_should_line_break = false;
31-
self.write_async(e).await
31+
self.write_async(&e).await
3232
}
33-
Event::Comment(ref e) => self.write_wrapped_async(b"<!--", e, b"-->").await,
34-
Event::CData(ref e) => {
33+
Event::Comment(e) => self.write_wrapped_async(b"<!--", &e, b"-->").await,
34+
Event::CData(e) => {
3535
next_should_line_break = false;
3636
self.write_async(b"<![CDATA[").await?;
37-
self.write_async(e).await?;
37+
self.write_async(&e).await?;
3838
self.write_async(b"]]>").await
3939
}
40-
Event::Decl(ref e) => self.write_wrapped_async(b"<?", e, b"?>").await,
41-
Event::PI(ref e) => self.write_wrapped_async(b"<?", e, b"?>").await,
42-
Event::DocType(ref e) => self.write_wrapped_async(b"<!DOCTYPE ", e, b">").await,
40+
Event::Decl(e) => self.write_wrapped_async(b"<?", &e, b"?>").await,
41+
Event::PI(e) => self.write_wrapped_async(b"<?", &e, b"?>").await,
42+
Event::DocType(e) => self.write_wrapped_async(b"<!DOCTYPE ", &e, b">").await,
4343
Event::Eof => Ok(()),
4444
};
4545
if let Some(i) = self.indent.as_mut() {

tests/fuzzing.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ fn fuzz_101() {
3030
match reader.read_event_into(&mut buf) {
3131
Ok(Event::Start(e)) | Ok(Event::Empty(e)) => {
3232
for a in e.attributes() {
33-
if a.ok()
34-
.map_or(true, |a| a.decode_and_unescape_value(&reader).is_err())
35-
{
33+
if a.ok().map_or(true, |a| {
34+
a.decode_and_unescape_value(reader.decoder()).is_err()
35+
}) {
3636
break;
3737
}
3838
}

tests/roundtrip.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ fn with_trim_ref() {
180180
loop {
181181
match reader.read_event().unwrap() {
182182
Eof => break,
183-
e => assert!(writer.write_event(&e).is_ok()), // either `e` or `&e`
183+
e => assert!(writer.write_event(e.borrow()).is_ok()), // either `e` or `&e`
184184
}
185185
}
186186

0 commit comments

Comments
 (0)