Skip to content

Commit 380826e

Browse files
committed
Remove StartText
StartText would be out of place once all events are expected to contain UTF-8. Additionally the decoder implementation strips BOM bytes out of the bytestream so there's no good way to access them.
1 parent 90be3ee commit 380826e

File tree

10 files changed

+10
-282
lines changed

10 files changed

+10
-282
lines changed

Changelog.md

-11
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@
2020
- [#180]: Make `Decoder` struct public. You already had access to it via the
2121
`Reader::decoder()` method, but could not name it in the code. Now the preferred
2222
way to access decoding functionality is via this struct
23-
- [#191]: New event variant `StartText` emitted for bytes before the XML declaration
24-
or a start comment or a tag. For streams with BOM this event will contain a BOM
2523
- [#395]: Add support for XML Schema `xs:list`
2624
- [#324]: `Reader::from_str` / `Deserializer::from_str` / `from_str` now ignore
2725
the XML declared encoding and always use UTF-8
@@ -96,15 +94,6 @@
9694
`Decoder::decode()` and `Decoder::decode_with_bom_removal()`.
9795
Use `reader.decoder().decode_*(...)` instead of `reader.decode_*(...)` for now.
9896
`Reader::encoding()` is replaced by `Decoder::encoding()` as well
99-
- [#191]: Remove poorly designed `BytesText::unescape_and_decode_without_bom()` and
100-
`BytesText::unescape_and_decode_without_bom_with_custom_entities()`. Although these methods worked
101-
as expected, this was only due to good luck. They was replaced by the
102-
`BytesStartText::decode_with_bom_removal()`:
103-
- conceptually, you should decode BOM only for the first `Text` event from the
104-
reader (since now `StartText` event is emitted instead for this)
105-
- text before the first tag is not an XML content at all, so it is meaningless
106-
to try to unescape something in it
107-
10897
- [#180]: Eliminated the differences in the decoding API when feature `encoding` enabled and when it is
10998
disabled. Signatures of functions are now the same regardless of whether or not the feature is
11099
enabled, and an error will be returned instead of performing replacements for invalid characters

benches/microbenches.rs

-14
Original file line numberDiff line numberDiff line change
@@ -118,20 +118,6 @@ fn read_resolved_event_into(c: &mut Criterion) {
118118
/// Benchmarks, how fast individual event parsed
119119
fn one_event(c: &mut Criterion) {
120120
let mut group = c.benchmark_group("One event");
121-
group.bench_function("StartText", |b| {
122-
let src = "Hello world!".repeat(512 / 12);
123-
b.iter(|| {
124-
let mut r = Reader::from_str(&src);
125-
let mut nbtxt = criterion::black_box(0);
126-
r.check_end_names(false).check_comments(false);
127-
match r.read_event() {
128-
Ok(Event::StartText(e)) => nbtxt += e.len(),
129-
something_else => panic!("Did not expect {:?}", something_else),
130-
};
131-
132-
assert_eq!(nbtxt, 504);
133-
})
134-
});
135121

136122
group.bench_function("Start", |b| {
137123
let src = format!(r#"<hello target="{}">"#, "world".repeat(512 / 5));

src/de/mod.rs

-8
Original file line numberDiff line numberDiff line change
@@ -929,10 +929,6 @@ impl<'i, R: BufRead> XmlRead<'i> for IoReader<R> {
929929
let event = loop {
930930
let e = self.reader.read_event_into(&mut self.buf)?;
931931
match e {
932-
//TODO: Probably not the best idea treat StartText as usual text
933-
// Usually this event will represent a BOM
934-
// Changing this requires review of the serde-de::top_level::one_element test
935-
Event::StartText(e) => break Ok(DeEvent::Text(e.into_owned().into())),
936932
Event::Start(e) => break Ok(DeEvent::Start(e.into_owned())),
937933
Event::End(e) => break Ok(DeEvent::End(e.into_owned())),
938934
Event::Text(e) => break Ok(DeEvent::Text(e.into_owned())),
@@ -973,10 +969,6 @@ impl<'de> XmlRead<'de> for SliceReader<'de> {
973969
loop {
974970
let e = self.reader.read_event()?;
975971
match e {
976-
//TODO: Probably not the best idea treat StartText as usual text
977-
// Usually this event will represent a BOM
978-
// Changing this requires review of the serde-de::top_level::one_element test
979-
Event::StartText(e) => break Ok(DeEvent::Text(e.into())),
980972
Event::Start(e) => break Ok(DeEvent::Start(e)),
981973
Event::End(e) => break Ok(DeEvent::End(e)),
982974
Event::Text(e) => break Ok(DeEvent::Text(e)),

src/events/mod.rs

-122
Original file line numberDiff line numberDiff line change
@@ -50,69 +50,6 @@ use crate::name::{LocalName, QName};
5050
use crate::utils::write_cow_string;
5151
use attributes::{Attribute, Attributes};
5252

53-
/// Text that appeared before an XML declaration, a start element or a comment.
54-
///
55-
/// In well-formed XML it could contain a Byte-Order-Mark (BOM). If this event
56-
/// contains something else except BOM, the XML should be considered ill-formed.
57-
///
58-
/// This is a reader-only event. If you need to write a text before the first tag,
59-
/// use the [`BytesText`] event.
60-
#[derive(Debug, Clone, Eq, PartialEq)]
61-
pub struct BytesStartText<'a> {
62-
content: BytesText<'a>,
63-
}
64-
65-
impl<'a> BytesStartText<'a> {
66-
/// Converts the event into an owned event.
67-
pub fn into_owned(self) -> BytesStartText<'static> {
68-
BytesStartText {
69-
content: self.content.into_owned(),
70-
}
71-
}
72-
73-
/// Extracts the inner `Cow` from the `BytesStartText` event container.
74-
#[inline]
75-
pub fn into_inner(self) -> Cow<'a, [u8]> {
76-
self.content.into_inner()
77-
}
78-
79-
/// Converts the event into a borrowed event.
80-
#[inline]
81-
pub fn borrow(&self) -> BytesStartText {
82-
BytesStartText {
83-
content: self.content.borrow(),
84-
}
85-
}
86-
87-
/// Decodes bytes of event, stripping byte order mark (BOM) if it is presented
88-
/// in the event.
89-
///
90-
/// This method does not unescapes content, because no escape sequences can
91-
/// appeared in the BOM or in the text before the first tag.
92-
pub fn decode_with_bom_removal(&self) -> Result<String> {
93-
//TODO: Fix lifetime issue - it should be possible to borrow string
94-
let decoded = self.content.decoder.decode_with_bom_removal(&*self)?;
95-
96-
Ok(decoded.to_string())
97-
}
98-
}
99-
100-
impl<'a> Deref for BytesStartText<'a> {
101-
type Target = BytesText<'a>;
102-
103-
fn deref(&self) -> &Self::Target {
104-
&self.content
105-
}
106-
}
107-
108-
impl<'a> From<BytesText<'a>> for BytesStartText<'a> {
109-
fn from(content: BytesText<'a>) -> Self {
110-
Self { content }
111-
}
112-
}
113-
114-
////////////////////////////////////////////////////////////////////////////////////////////////////
115-
11653
/// Opening tag data (`Event::Start`), with optional attributes.
11754
///
11855
/// `<name attr="value">`.
@@ -794,12 +731,6 @@ impl<'a> Deref for BytesText<'a> {
794731
}
795732
}
796733

797-
impl<'a> From<BytesStartText<'a>> for BytesText<'a> {
798-
fn from(content: BytesStartText<'a>) -> Self {
799-
content.content
800-
}
801-
}
802-
803734
////////////////////////////////////////////////////////////////////////////////////////////////////
804735

805736
/// CDATA content contains unescaped data from the reader. If you want to write them as a text,
@@ -938,56 +869,6 @@ impl<'a> Deref for BytesCData<'a> {
938869
/// [`Reader::read_event_into`]: crate::reader::Reader::read_event_into
939870
#[derive(Clone, Debug, Eq, PartialEq)]
940871
pub enum Event<'a> {
941-
/// Text that appeared before the first opening tag or an [XML declaration].
942-
/// [According to the XML standard][std], no text allowed before the XML
943-
/// declaration. However, if there is a BOM in the stream, some data may be
944-
/// present.
945-
///
946-
/// When this event is generated, it is the very first event emitted by the
947-
/// [`Reader`], and there can be the only one such event.
948-
///
949-
/// The [`Writer`] writes content of this event "as is" without encoding or
950-
/// escaping. If you write it, it should be written first and only one time
951-
/// (but writer does not enforce that).
952-
///
953-
/// # Examples
954-
///
955-
/// ```
956-
/// # use pretty_assertions::assert_eq;
957-
/// use std::borrow::Cow;
958-
/// use quick_xml::Reader;
959-
/// use quick_xml::events::Event;
960-
///
961-
/// // XML in UTF-8 with BOM
962-
/// let xml = b"\xEF\xBB\xBF<?xml version='1.0'?>".as_ref();
963-
/// let mut reader = Reader::from_reader(xml);
964-
/// let mut buf = Vec::new();
965-
/// let mut events_processed = 0;
966-
/// loop {
967-
/// match reader.read_event_into(&mut buf) {
968-
/// Ok(Event::StartText(e)) => {
969-
/// assert_eq!(events_processed, 0);
970-
/// // Content contains BOM
971-
/// assert_eq!(e.into_inner(), Cow::Borrowed(b"\xEF\xBB\xBF"));
972-
/// }
973-
/// Ok(Event::Decl(_)) => {
974-
/// assert_eq!(events_processed, 1);
975-
/// }
976-
/// Ok(Event::Eof) => {
977-
/// assert_eq!(events_processed, 2);
978-
/// break;
979-
/// }
980-
/// e => panic!("Unexpected event {:?}", e),
981-
/// }
982-
/// events_processed += 1;
983-
/// }
984-
/// ```
985-
///
986-
/// [XML declaration]: Event::Decl
987-
/// [std]: https://www.w3.org/TR/xml11/#NT-document
988-
/// [`Reader`]: crate::reader::Reader
989-
/// [`Writer`]: crate::writer::Writer
990-
StartText(BytesStartText<'a>),
991872
/// Start tag (with attributes) `<tag attr="value">`.
992873
Start(BytesStart<'a>),
993874
/// End tag `</tag>`.
@@ -1015,7 +896,6 @@ impl<'a> Event<'a> {
1015896
/// buffer used when reading but incurring a new, separate allocation.
1016897
pub fn into_owned(self) -> Event<'static> {
1017898
match self {
1018-
Event::StartText(e) => Event::StartText(e.into_owned()),
1019899
Event::Start(e) => Event::Start(e.into_owned()),
1020900
Event::End(e) => Event::End(e.into_owned()),
1021901
Event::Empty(e) => Event::Empty(e.into_owned()),
@@ -1033,7 +913,6 @@ impl<'a> Event<'a> {
1033913
#[inline]
1034914
pub fn borrow(&self) -> Event {
1035915
match self {
1036-
Event::StartText(e) => Event::StartText(e.borrow()),
1037916
Event::Start(e) => Event::Start(e.borrow()),
1038917
Event::End(e) => Event::End(e.borrow()),
1039918
Event::Empty(e) => Event::Empty(e.borrow()),
@@ -1053,7 +932,6 @@ impl<'a> Deref for Event<'a> {
1053932

1054933
fn deref(&self) -> &[u8] {
1055934
match *self {
1056-
Event::StartText(ref e) => &*e,
1057935
Event::Start(ref e) | Event::Empty(ref e) => &*e,
1058936
Event::End(ref e) => &*e,
1059937
Event::Text(ref e) => &*e,

src/reader/mod.rs

+6-18
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ pub use ns_reader::NsReader;
142142
/// subgraph _
143143
/// direction LR
144144
///
145-
/// Init -- "(no event)"\nStartText --> OpenedTag
145+
/// Init -- "(no event)"\n --> OpenedTag
146146
/// OpenedTag -- Decl, DocType, PI\nComment, CData\nStart, Empty, End --> ClosedTag
147147
/// ClosedTag -- "#lt;false#gt;\n(no event)"\nText --> OpenedTag
148148
/// end
@@ -153,13 +153,13 @@ pub use ns_reader::NsReader;
153153
#[derive(Clone)]
154154
enum ParseState {
155155
/// Initial state in which reader stay after creation. Transition from that
156-
/// state could produce a `StartText`, `Decl`, `Comment` or `Start` event.
157-
/// The next state is always `OpenedTag`. The reader will never return to this
158-
/// state. The event emitted during transition to `OpenedTag` is a `StartEvent`
159-
/// if the first symbol not `<`, otherwise no event are emitted.
156+
/// state could produce a `Text`, `Decl`, `Comment` or `Start` event. The next
157+
/// state is always `OpenedTag`. The reader will never return to this state. The
158+
/// event emitted during transition to `OpenedTag` is a `StartEvent` if the
159+
/// first symbol not `<`, otherwise no event are emitted.
160160
Init,
161161
/// State after seeing the `<` symbol. Depending on the next symbol all other
162-
/// events (except `StartText`) could be generated.
162+
/// events could be generated.
163163
///
164164
/// After generating ane event the reader moves to the `ClosedTag` state.
165165
OpenedTag,
@@ -420,8 +420,6 @@ impl<R> Reader<R> {
420420
}
421421

422422
/// Read until '<' is found and moves reader to an `OpenedTag` state.
423-
///
424-
/// Return a `StartText` event if `first` is `true` and a `Text` event otherwise
425423
fn read_until_open<'i, B>(&mut self, buf: B, first: bool) -> Result<Event<'i>>
426424
where
427425
R: XmlSource<'i, B>,
@@ -1461,16 +1459,6 @@ mod test {
14611459
use crate::reader::Reader;
14621460
use pretty_assertions::assert_eq;
14631461

1464-
#[test]
1465-
fn start_text() {
1466-
let mut reader = Reader::from_str("bom");
1467-
1468-
assert_eq!(
1469-
reader.read_event_impl($buf).unwrap(),
1470-
Event::StartText(BytesText::from_escaped("bom").into())
1471-
);
1472-
}
1473-
14741462
#[test]
14751463
fn declaration() {
14761464
let mut reader = Reader::from_str("<?xml ?>");

src/reader/parser.rs

+2-13
Original file line numberDiff line numberDiff line change
@@ -63,17 +63,11 @@ pub(super) struct Parser {
6363
}
6464

6565
impl Parser {
66-
/// Trims whitespaces from `bytes`, if required, and returns a [`StartText`]
67-
/// or a [`Text`] event. When [`StartText`] is returned, the method can change
68-
/// the encoding of the reader, detecting it from the beginning of the stream.
66+
/// Trims whitespaces from `bytes`, if required, and returns a [`Text`] event.
6967
///
7068
/// # Parameters
7169
/// - `bytes`: data from the start of stream to the first `<` or from `>` to `<`
72-
/// - `first`: if `true`, then this is the first call of that function,
73-
/// i. e. data from the start of stream and [`StartText`] will be returned,
74-
/// otherwise [`Text`] will be returned
7570
///
76-
/// [`StartText`]: Event::StartText
7771
/// [`Text`]: Event::Text
7872
pub fn read_text<'b>(&mut self, bytes: &'b [u8], first: bool) -> Result<Event<'b>> {
7973
#[cfg(feature = "encoding")]
@@ -93,12 +87,7 @@ impl Parser {
9387
} else {
9488
bytes
9589
};
96-
97-
Ok(if first {
98-
Event::StartText(BytesText::wrap(content, self.decoder()).into())
99-
} else {
100-
Event::Text(BytesText::wrap(content, self.decoder()))
101-
})
90+
Ok(Event::Text(BytesText::wrap(content, self.decoder())))
10291
}
10392

10493
/// reads `BytesElement` starting with a `!`,

src/writer.rs

-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@ impl<W: Write> Writer<W> {
8989
pub fn write_event<'a, E: AsRef<Event<'a>>>(&mut self, event: E) -> Result<()> {
9090
let mut next_should_line_break = true;
9191
let result = match *event.as_ref() {
92-
Event::StartText(ref e) => self.write(&e),
9392
Event::Start(ref e) => {
9493
let result = self.write_wrapped(b"<", e, b">");
9594
if let Some(i) = self.indent.as_mut() {

tests/test.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ fn fuzz_101() {
173173
fn test_no_trim() {
174174
let mut reader = Reader::from_str(" <tag> text </tag> ");
175175

176-
assert!(matches!(reader.read_event().unwrap(), StartText(_)));
176+
assert!(matches!(reader.read_event().unwrap(), Text(_)));
177177
assert!(matches!(reader.read_event().unwrap(), Start(_)));
178178
assert!(matches!(reader.read_event().unwrap(), Text(_)));
179179
assert!(matches!(reader.read_event().unwrap(), End(_)));
@@ -185,7 +185,7 @@ fn test_trim_end() {
185185
let mut reader = Reader::from_str(" <tag> text </tag> ");
186186
reader.trim_text_end(true);
187187

188-
assert!(matches!(reader.read_event().unwrap(), StartText(_)));
188+
assert!(matches!(reader.read_event().unwrap(), Text(_)));
189189
assert!(matches!(reader.read_event().unwrap(), Start(_)));
190190
assert!(matches!(reader.read_event().unwrap(), Text(_)));
191191
assert!(matches!(reader.read_event().unwrap(), End(_)));

0 commit comments

Comments
 (0)