Skip to content

Commit 35ea315

Browse files
committed
Add escape_default method to u8 and [u8]
1 parent 2e46cb3 commit 35ea315

File tree

3 files changed

+124
-0
lines changed

3 files changed

+124
-0
lines changed

library/core/src/num/mod.rs

+26
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
#![stable(feature = "rust1", since = "1.0.0")]
44

5+
use crate::ascii;
56
use crate::intrinsics;
67
use crate::mem;
78
use crate::str::FromStr;
@@ -649,6 +650,31 @@ impl u8 {
649650
pub const fn is_ascii_control(&self) -> bool {
650651
matches!(*self, b'\0'..=b'\x1F' | b'\x7F')
651652
}
653+
654+
/// Returns an iterator that produces an escaped version of a `u8`,
655+
/// treating it as an ASCII character.
656+
///
657+
/// The behavior is identical to [`ascii::escape_default`].
658+
///
659+
/// # Examples
660+
///
661+
/// ```
662+
/// # #![feature(inherent_ascii_escape)]
663+
///
664+
/// assert_eq!("0", b'0'.escape_ascii().to_string());
665+
/// assert_eq!("\\t", b'\t'.escape_ascii().to_string());
666+
/// assert_eq!("\\r", b'\r'.escape_ascii().to_string());
667+
/// assert_eq!("\\n", b'\n'.escape_ascii().to_string());
668+
/// assert_eq!("\\'", b'\''.escape_ascii().to_string());
669+
/// assert_eq!("\\\"", b'"'.escape_ascii().to_string());
670+
/// assert_eq!("\\\\", b'\\'.escape_ascii().to_string());
671+
/// assert_eq!("\\x9d", b'\x9d'.escape_ascii().to_string());
672+
/// ```
673+
#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
674+
#[inline]
675+
pub fn escape_ascii(&self) -> ascii::EscapeDefault {
676+
ascii::escape_default(*self)
677+
}
652678
}
653679

654680
#[lang = "u16"]

library/core/src/slice/ascii.rs

+95
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
//! Operations on ASCII `[u8]`.
22
3+
use crate::ascii;
4+
use crate::fmt::{self, Write};
5+
use crate::iter;
36
use crate::mem;
7+
use crate::ops;
48

59
#[lang = "slice_u8"]
610
#[cfg(not(test))]
@@ -55,6 +59,97 @@ impl [u8] {
5559
byte.make_ascii_lowercase();
5660
}
5761
}
62+
63+
/// Returns an iterator that produces an escaped version of this slice,
64+
/// treating it as an ASCII string.
65+
///
66+
/// # Examples
67+
///
68+
/// ```
69+
/// #![feature(inherent_ascii_escape)]
70+
///
71+
/// let s = b"0\t\r\n'\"\\\x9d";
72+
/// let escaped = s.escape_ascii().to_string();
73+
/// assert_eq!(escaped, "0\\t\\r\\n\\'\\\"\\\\\\x9d");
74+
/// ```
75+
#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
76+
pub fn escape_ascii(&self) -> EscapeAscii<'_> {
77+
EscapeAscii { inner: self.iter().flat_map(EscapeByte) }
78+
}
79+
}
80+
81+
impl_fn_for_zst! {
82+
#[derive(Clone)]
83+
struct EscapeByte impl Fn = |byte: &u8| -> ascii::EscapeDefault {
84+
ascii::escape_default(*byte)
85+
};
86+
}
87+
88+
/// An iterator over the escaped version of a byte slice.
89+
///
90+
/// This `struct` is created by the [`[u8]::escape_ascii`] method. See its
91+
/// documentation for more.
92+
///
93+
/// [`[u8]::escape_ascii`]: ../../std/primitive.slice.html#method.escape_ascii
94+
#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
95+
#[derive(Clone)]
96+
pub struct EscapeAscii<'a> {
97+
inner: iter::FlatMap<super::Iter<'a, u8>, ascii::EscapeDefault, EscapeByte>,
98+
}
99+
100+
#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
101+
impl<'a> iter::Iterator for EscapeAscii<'a> {
102+
type Item = u8;
103+
#[inline]
104+
fn next(&mut self) -> Option<u8> {
105+
self.inner.next()
106+
}
107+
#[inline]
108+
fn size_hint(&self) -> (usize, Option<usize>) {
109+
self.inner.size_hint()
110+
}
111+
#[inline]
112+
fn try_fold<Acc, Fold, R>(&mut self, init: Acc, fold: Fold) -> R
113+
where
114+
Fold: FnMut(Acc, Self::Item) -> R,
115+
R: ops::Try<Ok = Acc>,
116+
{
117+
self.inner.try_fold(init, fold)
118+
}
119+
#[inline]
120+
fn fold<Acc, Fold>(self, init: Acc, fold: Fold) -> Acc
121+
where
122+
Fold: FnMut(Acc, Self::Item) -> Acc,
123+
{
124+
self.inner.fold(init, fold)
125+
}
126+
#[inline]
127+
fn last(mut self) -> Option<u8> {
128+
self.next_back()
129+
}
130+
}
131+
132+
#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
133+
impl<'a> iter::DoubleEndedIterator for EscapeAscii<'a> {
134+
fn next_back(&mut self) -> Option<u8> {
135+
self.inner.next_back()
136+
}
137+
}
138+
#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
139+
impl<'a> iter::ExactSizeIterator for EscapeAscii<'a> {}
140+
#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
141+
impl<'a> iter::FusedIterator for EscapeAscii<'a> {}
142+
#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
143+
impl<'a> fmt::Display for EscapeAscii<'a> {
144+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145+
self.clone().try_for_each(|b| f.write_char(b as char))
146+
}
147+
}
148+
#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
149+
impl<'a> fmt::Debug for EscapeAscii<'a> {
150+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151+
f.pad("EscapeAscii { .. }")
152+
}
58153
}
59154

60155
/// Returns `true` if any byte in the word `v` is nonascii (>= 128). Snarfed

library/core/src/slice/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ pub use sort::heapsort;
7676
#[stable(feature = "slice_get_slice", since = "1.28.0")]
7777
pub use index::SliceIndex;
7878

79+
#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
80+
pub use ascii::EscapeAscii;
81+
7982
#[lang = "slice"]
8083
#[cfg(not(test))]
8184
impl<T> [T] {

0 commit comments

Comments
 (0)