Skip to content

Commit

Permalink
Merge pull request #827 from Tpt/escape
Browse files Browse the repository at this point in the history
Make `escape` and it variants take a `impl Into<Cow<str>>` argument and implement `From<(&'a str, Cow<'a, str>)>` on `Attribute`
  • Loading branch information
Mingun authored Oct 20, 2024
2 parents b96b876 + 786bee1 commit 3197e64
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 15 deletions.
5 changes: 4 additions & 1 deletion Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,16 @@
- [#820]: Classify output of the `Serializer` by returning an enumeration with kind of written data
- [#823]: Do not allow serialization of consequent primitives, for example `Vec<usize>` or
`Vec<String>` in `$value` fields. They cannot be deserialized back with the same result
- [#827]: Make `escape` and it variants take a `impl Into<Cow<str>>` argument and implement
`From<(&'a str, Cow<'a, str>)>` on `Attribute`

[#227]: https://github.com/tafia/quick-xml/issues/227
[#655]: https://github.com/tafia/quick-xml/issues/655
[#810]: https://github.com/tafia/quick-xml/pull/810
[#811]: https://github.com/tafia/quick-xml/pull/811
[#820]: https://github.com/tafia/quick-xml/pull/820
[#823]: https://github.com/tafia/quick-xml/pull/823
[#827]: https://github.com/tafia/quick-xml/pull/827


## 0.36.2 -- 2024-09-20
Expand Down Expand Up @@ -979,7 +982,7 @@ serde >= 1.0.181

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

## 0.15.0
Expand Down
14 changes: 9 additions & 5 deletions src/escape.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ impl std::error::Error for EscapeError {
/// | `&` | `&amp;`
/// | `'` | `&apos;`
/// | `"` | `&quot;`
pub fn escape(raw: &str) -> Cow<str> {
pub fn escape<'a>(raw: impl Into<Cow<'a, str>>) -> Cow<'a, str> {
_escape(raw, |ch| matches!(ch, b'<' | b'>' | b'&' | b'\'' | b'\"'))
}

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

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

/// Escapes an `&str` and replaces a subset of xml special characters (`<`, `>`,
/// `&`, `'`, `"`) with their corresponding xml escaped value.
pub(crate) fn _escape<F: Fn(u8) -> bool>(raw: &str, escape_chars: F) -> Cow<str> {
pub(crate) fn _escape<'a, F: Fn(u8) -> bool>(
raw: impl Into<Cow<'a, str>>,
escape_chars: F,
) -> Cow<'a, str> {
let raw = raw.into();
let bytes = raw.as_bytes();
let mut escaped = None;
let mut iter = bytes.iter();
Expand Down Expand Up @@ -192,7 +196,7 @@ pub(crate) fn _escape<F: Fn(u8) -> bool>(raw: &str, escape_chars: F) -> Cow<str>
// if unsafe code will be allowed
Cow::Owned(String::from_utf8(escaped).unwrap())
} else {
Cow::Borrowed(raw)
raw
}
}

Expand Down
25 changes: 25 additions & 0 deletions src/events/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,31 @@ impl<'a> From<(&'a str, &'a str)> for Attribute<'a> {
}
}

impl<'a> From<(&'a str, Cow<'a, str>)> for Attribute<'a> {
/// Creates new attribute from text representation.
/// Key is stored as-is, but the value will be escaped.
///
/// # Examples
///
/// ```
/// # use std::borrow::Cow;
/// use pretty_assertions::assert_eq;
/// use quick_xml::events::attributes::Attribute;
///
/// let features = Attribute::from(("features", Cow::Borrowed("Bells & whistles")));
/// assert_eq!(features.value, "Bells &amp; whistles".as_bytes());
/// ```
fn from(val: (&'a str, Cow<'a, str>)) -> Attribute<'a> {
Attribute {
key: QName(val.0.as_bytes()),
value: match escape(val.1) {
Cow::Borrowed(s) => Cow::Borrowed(s.as_bytes()),
Cow::Owned(s) => Cow::Owned(s.into_bytes()),
},
}
}
}

impl<'a> From<Attr<&'a [u8]>> for Attribute<'a> {
#[inline]
fn from(attr: Attr<&'a [u8]>) -> Self {
Expand Down
15 changes: 6 additions & 9 deletions src/events/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -746,9 +746,8 @@ impl<'a> BytesCData<'a> {
pub fn escape(self) -> Result<BytesText<'a>, EncodingError> {
let decoded = self.decode()?;
Ok(BytesText::wrap(
match escape(&decoded) {
// Because result is borrowed, no replacements was done and we can use original content
Cow::Borrowed(_) => self.content,
match escape(decoded) {
Cow::Borrowed(escaped) => Cow::Borrowed(escaped.as_bytes()),
Cow::Owned(escaped) => Cow::Owned(escaped.into_bytes()),
},
Decoder::utf8(),
Expand All @@ -771,9 +770,8 @@ impl<'a> BytesCData<'a> {
pub fn partial_escape(self) -> Result<BytesText<'a>, EncodingError> {
let decoded = self.decode()?;
Ok(BytesText::wrap(
match partial_escape(&decoded) {
// Because result is borrowed, no replacements was done and we can use original content
Cow::Borrowed(_) => self.content,
match partial_escape(decoded) {
Cow::Borrowed(escaped) => Cow::Borrowed(escaped.as_bytes()),
Cow::Owned(escaped) => Cow::Owned(escaped.into_bytes()),
},
Decoder::utf8(),
Expand All @@ -795,9 +793,8 @@ impl<'a> BytesCData<'a> {
pub fn minimal_escape(self) -> Result<BytesText<'a>, EncodingError> {
let decoded = self.decode()?;
Ok(BytesText::wrap(
match minimal_escape(&decoded) {
// Because result is borrowed, no replacements was done and we can use original content
Cow::Borrowed(_) => self.content,
match minimal_escape(decoded) {
Cow::Borrowed(escaped) => Cow::Borrowed(escaped.as_bytes()),
Cow::Owned(escaped) => Cow::Owned(escaped.into_bytes()),
},
Decoder::utf8(),
Expand Down

0 comments on commit 3197e64

Please sign in to comment.