about summary refs log tree commit diff
diff options
context:
space:
mode:
authorltdk <usr@ltdk.xyz>2020-10-24 00:45:41 -0400
committerltdk <usr@ltdk.xyz>2021-03-28 17:38:25 -0400
commitc20ba9cdae257c70c934e2c9dc1a77c1798d8113 (patch)
tree5bfe69b72db26981d752207762c5b820328afc0e
parent4a20eb6a9da36c88ee929826c4f1eb8d7ea393b2 (diff)
downloadrust-c20ba9cdae257c70c934e2c9dc1a77c1798d8113.tar.gz
rust-c20ba9cdae257c70c934e2c9dc1a77c1798d8113.zip
Add escape_default method to u8 and [u8]
-rw-r--r--library/core/src/num/mod.rs26
-rw-r--r--library/core/src/slice/ascii.rs92
-rw-r--r--library/core/src/slice/mod.rs3
3 files changed, 121 insertions, 0 deletions
diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs
index f0bd976ba83..6032dc9a2d3 100644
--- a/library/core/src/num/mod.rs
+++ b/library/core/src/num/mod.rs
@@ -2,6 +2,7 @@
 
 #![stable(feature = "rust1", since = "1.0.0")]
 
+use crate::ascii;
 use crate::intrinsics;
 use crate::mem;
 use crate::str::FromStr;
@@ -661,6 +662,31 @@ impl u8 {
     pub const fn is_ascii_control(&self) -> bool {
         matches!(*self, b'\0'..=b'\x1F' | b'\x7F')
     }
+
+    /// Returns an iterator that produces an escaped version of a `u8`,
+    /// treating it as an ASCII character.
+    ///
+    /// The behavior is identical to [`ascii::escape_default`].
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(inherent_ascii_escape)]
+    ///
+    /// assert_eq!("0", b'0'.escape_ascii().to_string());
+    /// assert_eq!("\\t", b'\t'.escape_ascii().to_string());
+    /// assert_eq!("\\r", b'\r'.escape_ascii().to_string());
+    /// assert_eq!("\\n", b'\n'.escape_ascii().to_string());
+    /// assert_eq!("\\'", b'\''.escape_ascii().to_string());
+    /// assert_eq!("\\\"", b'"'.escape_ascii().to_string());
+    /// assert_eq!("\\\\", b'\\'.escape_ascii().to_string());
+    /// assert_eq!("\\x9d", b'\x9d'.escape_ascii().to_string());
+    /// ```
+    #[unstable(feature = "inherent_ascii_escape", issue = "77174")]
+    #[inline]
+    pub fn escape_ascii(&self) -> ascii::EscapeDefault {
+        ascii::escape_default(*self)
+    }
 }
 
 #[lang = "u16"]
diff --git a/library/core/src/slice/ascii.rs b/library/core/src/slice/ascii.rs
index 009ef9e0a9c..22fa08b9795 100644
--- a/library/core/src/slice/ascii.rs
+++ b/library/core/src/slice/ascii.rs
@@ -1,7 +1,10 @@
 //! Operations on ASCII `[u8]`.
 
+use crate::ascii;
+use crate::fmt::{self, Write};
 use crate::iter;
 use crate::mem;
+use crate::ops;
 
 #[lang = "slice_u8"]
 #[cfg(not(test))]
@@ -56,6 +59,95 @@ impl [u8] {
             byte.make_ascii_lowercase();
         }
     }
+
+    /// Returns an iterator that produces an escaped version of this slice,
+    /// treating it as an ASCII string.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(inherent_ascii_escape)]
+    ///
+    /// let s = b"0\t\r\n'\"\\\x9d";
+    /// let escaped = s.escape_ascii().to_string();
+    /// assert_eq!(escaped, "0\\t\\r\\n\\'\\\"\\\\\\x9d");
+    /// ```
+    #[unstable(feature = "inherent_ascii_escape", issue = "77174")]
+    pub fn escape_ascii(&self) -> EscapeAscii<'_> {
+        EscapeAscii { inner: self.iter().flat_map(EscapeByte) }
+    }
+}
+
+impl_fn_for_zst! {
+    #[derive(Clone)]
+    struct EscapeByte impl Fn = |byte: &u8| -> ascii::EscapeDefault {
+        ascii::escape_default(*byte)
+    };
+}
+
+/// An iterator over the escaped version of a byte slice.
+///
+/// This `struct` is created by the [`slice::escape_ascii`] method. See its
+/// documentation for more information.
+#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
+#[derive(Clone)]
+pub struct EscapeAscii<'a> {
+    inner: iter::FlatMap<super::Iter<'a, u8>, ascii::EscapeDefault, EscapeByte>,
+}
+
+#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
+impl<'a> iter::Iterator for EscapeAscii<'a> {
+    type Item = u8;
+    #[inline]
+    fn next(&mut self) -> Option<u8> {
+        self.inner.next()
+    }
+    #[inline]
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        self.inner.size_hint()
+    }
+    #[inline]
+    fn try_fold<Acc, Fold, R>(&mut self, init: Acc, fold: Fold) -> R
+    where
+        Fold: FnMut(Acc, Self::Item) -> R,
+        R: ops::Try<Ok = Acc>,
+    {
+        self.inner.try_fold(init, fold)
+    }
+    #[inline]
+    fn fold<Acc, Fold>(self, init: Acc, fold: Fold) -> Acc
+    where
+        Fold: FnMut(Acc, Self::Item) -> Acc,
+    {
+        self.inner.fold(init, fold)
+    }
+    #[inline]
+    fn last(mut self) -> Option<u8> {
+        self.next_back()
+    }
+}
+
+#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
+impl<'a> iter::DoubleEndedIterator for EscapeAscii<'a> {
+    fn next_back(&mut self) -> Option<u8> {
+        self.inner.next_back()
+    }
+}
+#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
+impl<'a> iter::ExactSizeIterator for EscapeAscii<'a> {}
+#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
+impl<'a> iter::FusedIterator for EscapeAscii<'a> {}
+#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
+impl<'a> fmt::Display for EscapeAscii<'a> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.clone().try_for_each(|b| f.write_char(b as char))
+    }
+}
+#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
+impl<'a> fmt::Debug for EscapeAscii<'a> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.pad("EscapeAscii { .. }")
+    }
 }
 
 /// Returns `true` if any byte in the word `v` is nonascii (>= 128). Snarfed
diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs
index d7a28c8d08f..59fad8c813c 100644
--- a/library/core/src/slice/mod.rs
+++ b/library/core/src/slice/mod.rs
@@ -81,6 +81,9 @@ pub use index::SliceIndex;
 #[unstable(feature = "slice_range", issue = "76393")]
 pub use index::range;
 
+#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
+pub use ascii::EscapeAscii;
+
 #[lang = "slice"]
 #[cfg(not(test))]
 impl<T> [T] {