Skip to content

Commit 3197e64

Browse files
authored
Merge pull request #827 from Tpt/escape
Make `escape` and it variants take a `impl Into<Cow<str>>` argument and implement `From<(&'a str, Cow<'a, str>)>` on `Attribute`
2 parents b96b876 + 786bee1 commit 3197e64

File tree

4 files changed

+44
-15
lines changed

4 files changed

+44
-15
lines changed

Changelog.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,16 @@
3232
- [#820]: Classify output of the `Serializer` by returning an enumeration with kind of written data
3333
- [#823]: Do not allow serialization of consequent primitives, for example `Vec<usize>` or
3434
`Vec<String>` in `$value` fields. They cannot be deserialized back with the same result
35+
- [#827]: Make `escape` and it variants take a `impl Into<Cow<str>>` argument and implement
36+
`From<(&'a str, Cow<'a, str>)>` on `Attribute`
3537

3638
[#227]: https://github.com/tafia/quick-xml/issues/227
3739
[#655]: https://github.com/tafia/quick-xml/issues/655
3840
[#810]: https://github.com/tafia/quick-xml/pull/810
3941
[#811]: https://github.com/tafia/quick-xml/pull/811
4042
[#820]: https://github.com/tafia/quick-xml/pull/820
4143
[#823]: https://github.com/tafia/quick-xml/pull/823
44+
[#827]: https://github.com/tafia/quick-xml/pull/827
4245

4346

4447
## 0.36.2 -- 2024-09-20
@@ -979,7 +982,7 @@ serde >= 1.0.181
979982

980983
## 0.16.0
981984
- feat: (breaking change) set failure and encoding_rs crates as optional.
982-
You should now use respectively `use-failure` and `encoding` features to get the old behavior
985+
You should now use respectively `use-failure` and `encoding` features to get the old behavior
983986
- perf: improve perf using memchr3 iterator. Reading is 18% better on benches
984987

985988
## 0.15.0

src/escape.rs

+9-5
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ impl std::error::Error for EscapeError {
101101
/// | `&` | `&amp;`
102102
/// | `'` | `&apos;`
103103
/// | `"` | `&quot;`
104-
pub fn escape(raw: &str) -> Cow<str> {
104+
pub fn escape<'a>(raw: impl Into<Cow<'a, str>>) -> Cow<'a, str> {
105105
_escape(raw, |ch| matches!(ch, b'<' | b'>' | b'&' | b'\'' | b'\"'))
106106
}
107107

@@ -126,7 +126,7 @@ pub fn escape(raw: &str) -> Cow<str> {
126126
/// | `<` | `&lt;`
127127
/// | `>` | `&gt;`
128128
/// | `&` | `&amp;`
129-
pub fn partial_escape(raw: &str) -> Cow<str> {
129+
pub fn partial_escape<'a>(raw: impl Into<Cow<'a, str>>) -> Cow<'a, str> {
130130
_escape(raw, |ch| matches!(ch, b'<' | b'>' | b'&'))
131131
}
132132

@@ -143,13 +143,17 @@ pub fn partial_escape(raw: &str) -> Cow<str> {
143143
/// | `&` | `&amp;`
144144
///
145145
/// [requires]: https://www.w3.org/TR/xml11/#syntax
146-
pub fn minimal_escape(raw: &str) -> Cow<str> {
146+
pub fn minimal_escape<'a>(raw: impl Into<Cow<'a, str>>) -> Cow<'a, str> {
147147
_escape(raw, |ch| matches!(ch, b'<' | b'&'))
148148
}
149149

150150
/// Escapes an `&str` and replaces a subset of xml special characters (`<`, `>`,
151151
/// `&`, `'`, `"`) with their corresponding xml escaped value.
152-
pub(crate) fn _escape<F: Fn(u8) -> bool>(raw: &str, escape_chars: F) -> Cow<str> {
152+
pub(crate) fn _escape<'a, F: Fn(u8) -> bool>(
153+
raw: impl Into<Cow<'a, str>>,
154+
escape_chars: F,
155+
) -> Cow<'a, str> {
156+
let raw = raw.into();
153157
let bytes = raw.as_bytes();
154158
let mut escaped = None;
155159
let mut iter = bytes.iter();
@@ -192,7 +196,7 @@ pub(crate) fn _escape<F: Fn(u8) -> bool>(raw: &str, escape_chars: F) -> Cow<str>
192196
// if unsafe code will be allowed
193197
Cow::Owned(String::from_utf8(escaped).unwrap())
194198
} else {
195-
Cow::Borrowed(raw)
199+
raw
196200
}
197201
}
198202

src/events/attributes.rs

+25
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,31 @@ impl<'a> From<(&'a str, &'a str)> for Attribute<'a> {
153153
}
154154
}
155155

156+
impl<'a> From<(&'a str, Cow<'a, str>)> for Attribute<'a> {
157+
/// Creates new attribute from text representation.
158+
/// Key is stored as-is, but the value will be escaped.
159+
///
160+
/// # Examples
161+
///
162+
/// ```
163+
/// # use std::borrow::Cow;
164+
/// use pretty_assertions::assert_eq;
165+
/// use quick_xml::events::attributes::Attribute;
166+
///
167+
/// let features = Attribute::from(("features", Cow::Borrowed("Bells & whistles")));
168+
/// assert_eq!(features.value, "Bells &amp; whistles".as_bytes());
169+
/// ```
170+
fn from(val: (&'a str, Cow<'a, str>)) -> Attribute<'a> {
171+
Attribute {
172+
key: QName(val.0.as_bytes()),
173+
value: match escape(val.1) {
174+
Cow::Borrowed(s) => Cow::Borrowed(s.as_bytes()),
175+
Cow::Owned(s) => Cow::Owned(s.into_bytes()),
176+
},
177+
}
178+
}
179+
}
180+
156181
impl<'a> From<Attr<&'a [u8]>> for Attribute<'a> {
157182
#[inline]
158183
fn from(attr: Attr<&'a [u8]>) -> Self {

src/events/mod.rs

+6-9
Original file line numberDiff line numberDiff line change
@@ -746,9 +746,8 @@ impl<'a> BytesCData<'a> {
746746
pub fn escape(self) -> Result<BytesText<'a>, EncodingError> {
747747
let decoded = self.decode()?;
748748
Ok(BytesText::wrap(
749-
match escape(&decoded) {
750-
// Because result is borrowed, no replacements was done and we can use original content
751-
Cow::Borrowed(_) => self.content,
749+
match escape(decoded) {
750+
Cow::Borrowed(escaped) => Cow::Borrowed(escaped.as_bytes()),
752751
Cow::Owned(escaped) => Cow::Owned(escaped.into_bytes()),
753752
},
754753
Decoder::utf8(),
@@ -771,9 +770,8 @@ impl<'a> BytesCData<'a> {
771770
pub fn partial_escape(self) -> Result<BytesText<'a>, EncodingError> {
772771
let decoded = self.decode()?;
773772
Ok(BytesText::wrap(
774-
match partial_escape(&decoded) {
775-
// Because result is borrowed, no replacements was done and we can use original content
776-
Cow::Borrowed(_) => self.content,
773+
match partial_escape(decoded) {
774+
Cow::Borrowed(escaped) => Cow::Borrowed(escaped.as_bytes()),
777775
Cow::Owned(escaped) => Cow::Owned(escaped.into_bytes()),
778776
},
779777
Decoder::utf8(),
@@ -795,9 +793,8 @@ impl<'a> BytesCData<'a> {
795793
pub fn minimal_escape(self) -> Result<BytesText<'a>, EncodingError> {
796794
let decoded = self.decode()?;
797795
Ok(BytesText::wrap(
798-
match minimal_escape(&decoded) {
799-
// Because result is borrowed, no replacements was done and we can use original content
800-
Cow::Borrowed(_) => self.content,
796+
match minimal_escape(decoded) {
797+
Cow::Borrowed(escaped) => Cow::Borrowed(escaped.as_bytes()),
801798
Cow::Owned(escaped) => Cow::Owned(escaped.into_bytes()),
802799
},
803800
Decoder::utf8(),

0 commit comments

Comments
 (0)