Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make escape and it variants take a impl Into<Cow<str>> argument and implement From<(&'a str, Cow<'a, str>)> on Attribute #827

Merged
merged 1 commit into from
Oct 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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