Skip to content

Commit 118a52b

Browse files
authored
Rollup merge of rust-lang#83130 - clarfonthey:escape, r=m-ou-se
escape_ascii take 2 The previous PR, rust-lang#73111 was closed for inactivity; since I've had trouble in the past reopening closed PRs, I'm just making a new one. I'm still running the tests locally but figured I'd open the PR in the meantime. Will fix whatever errors show up so we don't have to wait again for this. r? `@m-ou-se`
2 parents c1dc467 + 77a5fe9 commit 118a52b

File tree

3 files changed

+122
-0
lines changed

3 files changed

+122
-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;
@@ -661,6 +662,31 @@ impl u8 {
661662
pub const fn is_ascii_control(&self) -> bool {
662663
matches!(*self, b'\0'..=b'\x1F' | b'\x7F')
663664
}
665+
666+
/// Returns an iterator that produces an escaped version of a `u8`,
667+
/// treating it as an ASCII character.
668+
///
669+
/// The behavior is identical to [`ascii::escape_default`].
670+
///
671+
/// # Examples
672+
///
673+
/// ```
674+
/// #![feature(inherent_ascii_escape)]
675+
///
676+
/// assert_eq!("0", b'0'.escape_ascii().to_string());
677+
/// assert_eq!("\\t", b'\t'.escape_ascii().to_string());
678+
/// assert_eq!("\\r", b'\r'.escape_ascii().to_string());
679+
/// assert_eq!("\\n", b'\n'.escape_ascii().to_string());
680+
/// assert_eq!("\\'", b'\''.escape_ascii().to_string());
681+
/// assert_eq!("\\\"", b'"'.escape_ascii().to_string());
682+
/// assert_eq!("\\\\", b'\\'.escape_ascii().to_string());
683+
/// assert_eq!("\\x9d", b'\x9d'.escape_ascii().to_string());
684+
/// ```
685+
#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
686+
#[inline]
687+
pub fn escape_ascii(&self) -> ascii::EscapeDefault {
688+
ascii::escape_default(*self)
689+
}
664690
}
665691

666692
#[lang = "u16"]

library/core/src/slice/ascii.rs

+93
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,95 @@ 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 [`slice::escape_ascii`] method. See its
91+
/// documentation for more information.
92+
#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
93+
#[derive(Clone)]
94+
pub struct EscapeAscii<'a> {
95+
inner: iter::FlatMap<super::Iter<'a, u8>, ascii::EscapeDefault, EscapeByte>,
96+
}
97+
98+
#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
99+
impl<'a> iter::Iterator for EscapeAscii<'a> {
100+
type Item = u8;
101+
#[inline]
102+
fn next(&mut self) -> Option<u8> {
103+
self.inner.next()
104+
}
105+
#[inline]
106+
fn size_hint(&self) -> (usize, Option<usize>) {
107+
self.inner.size_hint()
108+
}
109+
#[inline]
110+
fn try_fold<Acc, Fold, R>(&mut self, init: Acc, fold: Fold) -> R
111+
where
112+
Fold: FnMut(Acc, Self::Item) -> R,
113+
R: ops::Try<Ok = Acc>,
114+
{
115+
self.inner.try_fold(init, fold)
116+
}
117+
#[inline]
118+
fn fold<Acc, Fold>(self, init: Acc, fold: Fold) -> Acc
119+
where
120+
Fold: FnMut(Acc, Self::Item) -> Acc,
121+
{
122+
self.inner.fold(init, fold)
123+
}
124+
#[inline]
125+
fn last(mut self) -> Option<u8> {
126+
self.next_back()
127+
}
128+
}
129+
130+
#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
131+
impl<'a> iter::DoubleEndedIterator for EscapeAscii<'a> {
132+
fn next_back(&mut self) -> Option<u8> {
133+
self.inner.next_back()
134+
}
135+
}
136+
#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
137+
impl<'a> iter::ExactSizeIterator for EscapeAscii<'a> {}
138+
#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
139+
impl<'a> iter::FusedIterator for EscapeAscii<'a> {}
140+
#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
141+
impl<'a> fmt::Display for EscapeAscii<'a> {
142+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143+
self.clone().try_for_each(|b| f.write_char(b as char))
144+
}
145+
}
146+
#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
147+
impl<'a> fmt::Debug for EscapeAscii<'a> {
148+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149+
f.pad("EscapeAscii { .. }")
150+
}
58151
}
59152

60153
/// 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
@@ -81,6 +81,9 @@ pub use index::SliceIndex;
8181
#[unstable(feature = "slice_range", issue = "76393")]
8282
pub use index::range;
8383

84+
#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
85+
pub use ascii::EscapeAscii;
86+
8487
#[lang = "slice"]
8588
#[cfg(not(test))]
8689
impl<T> [T] {

0 commit comments

Comments
 (0)