about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSimon Sapin <simon.sapin@exyr.org>2019-02-01 13:31:24 +0100
committerSimon Sapin <simon.sapin@exyr.org>2019-02-12 09:55:20 +0100
commit7a077804a3a58c93f690e382c86ccd4f1a8850af (patch)
tree01e31a8271689a67d4935060d833d0a75b69a8bf
parent92dcae47425a9084b97680bc0327ac88353b8a68 (diff)
downloadrust-7a077804a3a58c93f690e382c86ccd4f1a8850af.tar.gz
rust-7a077804a3a58c93f690e382c86ccd4f1a8850af.zip
New return types for str::escape_* that impl Display and Iterator<char>
As FCP’ed in the tracking issue: https://github.com/rust-lang/rust/issues/27791#issuecomment-376864727
-rw-r--r--src/liballoc/lib.rs3
-rw-r--r--src/liballoc/str.rs108
-rw-r--r--src/liballoc/tests/str.rs61
-rw-r--r--src/libgraphviz/lib.rs2
-rw-r--r--src/libsyntax/attr/mod.rs2
-rw-r--r--src/libsyntax/print/pprust.rs2
6 files changed, 132 insertions, 46 deletions
diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs
index 189ba84eeed..d4ee428a3b5 100644
--- a/src/liballoc/lib.rs
+++ b/src/liballoc/lib.rs
@@ -68,7 +68,6 @@
 #![warn(intra_doc_link_resolution_failure)]
 #![warn(missing_debug_implementations)]
 
-#![cfg_attr(not(test), feature(fn_traits))]
 #![cfg_attr(not(test), feature(generator_trait))]
 #![cfg_attr(test, feature(test))]
 
@@ -86,6 +85,7 @@
 #![feature(dropck_eyepatch)]
 #![feature(exact_size_is_empty)]
 #![feature(fmt_internals)]
+#![feature(fn_traits)]
 #![feature(fundamental)]
 #![feature(futures_api)]
 #![feature(lang_items)]
@@ -100,6 +100,7 @@
 #![feature(receiver_trait)]
 #![feature(specialization)]
 #![feature(staged_api)]
+#![feature(std_internals)]
 #![feature(str_internals)]
 #![feature(trusted_len)]
 #![feature(try_reserve)]
diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs
index 1fd4c9978a6..1705c80d5f5 100644
--- a/src/liballoc/str.rs
+++ b/src/liballoc/str.rs
@@ -29,10 +29,13 @@
 #![allow(unused_imports)]
 
 use core::borrow::Borrow;
-use core::fmt;
-use core::str as core_str;
+use core::fmt::{self, Write};
+use core::char;
+use core::iter::{Chain, Flatten, FlatMap};
 use core::str::pattern::{Pattern, Searcher, ReverseSearcher, DoubleEndedSearcher};
 use core::mem;
+use core::ops::Try;
+use core::option;
 use core::ptr;
 use core::iter::FusedIterator;
 use core::unicode::conversions;
@@ -452,14 +455,15 @@ impl str {
     #[unstable(feature = "str_escape",
                reason = "return type may change to be an iterator",
                issue = "27791")]
-    pub fn escape_debug(&self) -> String {
-        let mut string = String::with_capacity(self.len());
+    pub fn escape_debug(&self) -> EscapeDebug {
         let mut chars = self.chars();
-        if let Some(first) = chars.next() {
-            string.extend(first.escape_debug_ext(true))
+        EscapeDebug {
+            inner: chars.next()
+                .map(|first| first.escape_debug_ext(true))
+                .into_iter()
+                .flatten()
+                .chain(chars.flat_map(CharEscapeDebugContinue))
         }
-        string.extend(chars.flat_map(|c| c.escape_debug_ext(false)));
-        string
     }
 
     /// Escapes each char in `s` with [`char::escape_default`].
@@ -468,8 +472,8 @@ impl str {
     #[unstable(feature = "str_escape",
                reason = "return type may change to be an iterator",
                issue = "27791")]
-    pub fn escape_default(&self) -> String {
-        self.chars().flat_map(|c| c.escape_default()).collect()
+    pub fn escape_default(&self) -> EscapeDefault {
+        EscapeDefault { inner: self.chars().flat_map(CharEscapeDefault) }
     }
 
     /// Escapes each char in `s` with [`char::escape_unicode`].
@@ -478,8 +482,8 @@ impl str {
     #[unstable(feature = "str_escape",
                reason = "return type may change to be an iterator",
                issue = "27791")]
-    pub fn escape_unicode(&self) -> String {
-        self.chars().flat_map(|c| c.escape_unicode()).collect()
+    pub fn escape_unicode(&self) -> EscapeUnicode {
+        EscapeUnicode { inner: self.chars().flat_map(CharEscapeUnicode) }
     }
 
     /// Converts a [`Box<str>`] into a [`String`] without copying or allocating.
@@ -612,3 +616,83 @@ impl str {
 pub unsafe fn from_boxed_utf8_unchecked(v: Box<[u8]>) -> Box<str> {
     Box::from_raw(Box::into_raw(v) as *mut str)
 }
+
+impl_fn_for_zst! {
+    #[derive(Clone)]
+    struct CharEscapeDebugContinue impl Fn = |c: char| -> char::EscapeDebug {
+        c.escape_debug_ext(false)
+    };
+
+    #[derive(Clone)]
+    struct CharEscapeUnicode impl Fn = |c: char| -> char::EscapeUnicode {
+        c.escape_unicode()
+    };
+    #[derive(Clone)]
+    struct CharEscapeDefault impl Fn = |c: char| -> char::EscapeDefault {
+        c.escape_default()
+    };
+}
+
+macro_rules! escape_types {
+    ($(
+        struct $Name: ident<'a> {
+            inner: $Inner: ty,
+        }
+    )+) => {$(
+        #[unstable(feature = "str_escape", issue = "27791")]
+        #[derive(Clone, Debug)]
+        pub struct $Name<'a> {
+            inner: $Inner,
+        }
+
+        #[unstable(feature = "str_escape", issue = "27791")]
+        impl<'a> fmt::Display for $Name<'a> {
+            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+                self.clone().try_for_each(|c| f.write_char(c))
+            }
+        }
+
+        #[unstable(feature = "str_escape", issue = "27791")]
+        impl<'a> Iterator for $Name<'a> {
+            type Item = char;
+
+            #[inline]
+            fn next(&mut self) -> Option<char> { 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
+                Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: 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)
+            }
+        }
+
+        #[unstable(feature = "str_escape", issue = "27791")]
+        impl<'a> FusedIterator for $Name<'a> {}
+    )+}
+}
+
+escape_types! {
+    struct EscapeDebug<'a> {
+        inner: Chain<
+            Flatten<option::IntoIter<char::EscapeDebug>>,
+            FlatMap<Chars<'a>, char::EscapeDebug, CharEscapeDebugContinue>
+        >,
+    }
+    struct EscapeUnicode<'a> {
+        inner: FlatMap<Chars<'a>, char::EscapeUnicode, CharEscapeUnicode>,
+    }
+    struct EscapeDefault<'a> {
+        inner: FlatMap<Chars<'a>, char::EscapeDefault, CharEscapeDefault>,
+    }
+}
diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs
index f4e6bc9ea31..a1dc763f6d8 100644
--- a/src/liballoc/tests/str.rs
+++ b/src/liballoc/tests/str.rs
@@ -990,15 +990,15 @@ fn test_split_at_boundscheck() {
 
 #[test]
 fn test_escape_unicode() {
-    assert_eq!("abc".escape_unicode(), "\\u{61}\\u{62}\\u{63}");
-    assert_eq!("a c".escape_unicode(), "\\u{61}\\u{20}\\u{63}");
-    assert_eq!("\r\n\t".escape_unicode(), "\\u{d}\\u{a}\\u{9}");
-    assert_eq!("'\"\\".escape_unicode(), "\\u{27}\\u{22}\\u{5c}");
-    assert_eq!("\x00\x01\u{fe}\u{ff}".escape_unicode(), "\\u{0}\\u{1}\\u{fe}\\u{ff}");
-    assert_eq!("\u{100}\u{ffff}".escape_unicode(), "\\u{100}\\u{ffff}");
-    assert_eq!("\u{10000}\u{10ffff}".escape_unicode(), "\\u{10000}\\u{10ffff}");
-    assert_eq!("ab\u{fb00}".escape_unicode(), "\\u{61}\\u{62}\\u{fb00}");
-    assert_eq!("\u{1d4ea}\r".escape_unicode(), "\\u{1d4ea}\\u{d}");
+    assert_eq!("abc".escape_unicode().to_string(), "\\u{61}\\u{62}\\u{63}");
+    assert_eq!("a c".escape_unicode().to_string(), "\\u{61}\\u{20}\\u{63}");
+    assert_eq!("\r\n\t".escape_unicode().to_string(), "\\u{d}\\u{a}\\u{9}");
+    assert_eq!("'\"\\".escape_unicode().to_string(), "\\u{27}\\u{22}\\u{5c}");
+    assert_eq!("\x00\x01\u{fe}\u{ff}".escape_unicode().to_string(), "\\u{0}\\u{1}\\u{fe}\\u{ff}");
+    assert_eq!("\u{100}\u{ffff}".escape_unicode().to_string(), "\\u{100}\\u{ffff}");
+    assert_eq!("\u{10000}\u{10ffff}".escape_unicode().to_string(), "\\u{10000}\\u{10ffff}");
+    assert_eq!("ab\u{fb00}".escape_unicode().to_string(), "\\u{61}\\u{62}\\u{fb00}");
+    assert_eq!("\u{1d4ea}\r".escape_unicode().to_string(), "\\u{1d4ea}\\u{d}");
 }
 
 #[test]
@@ -1009,31 +1009,32 @@ fn test_escape_debug() {
     // they are escaped. However, when the character is unescaped (e.g., for
     // printable characters), only a single backslash appears (as the character
     // itself appears in the debug string).
-    assert_eq!("abc".escape_debug(), "abc");
-    assert_eq!("a c".escape_debug(), "a c");
-    assert_eq!("éèê".escape_debug(), "éèê");
-    assert_eq!("\r\n\t".escape_debug(), "\\r\\n\\t");
-    assert_eq!("'\"\\".escape_debug(), "\\'\\\"\\\\");
-    assert_eq!("\u{7f}\u{ff}".escape_debug(), "\\u{7f}\u{ff}");
-    assert_eq!("\u{100}\u{ffff}".escape_debug(), "\u{100}\\u{ffff}");
-    assert_eq!("\u{10000}\u{10ffff}".escape_debug(), "\u{10000}\\u{10ffff}");
-    assert_eq!("ab\u{200b}".escape_debug(), "ab\\u{200b}");
-    assert_eq!("\u{10d4ea}\r".escape_debug(), "\\u{10d4ea}\\r");
-    assert_eq!("\u{301}a\u{301}bé\u{e000}".escape_debug(), "\\u{301}a\u{301}bé\\u{e000}");
+    assert_eq!("abc".escape_debug().to_string(), "abc");
+    assert_eq!("a c".escape_debug().to_string(), "a c");
+    assert_eq!("éèê".escape_debug().to_string(), "éèê");
+    assert_eq!("\r\n\t".escape_debug().to_string(), "\\r\\n\\t");
+    assert_eq!("'\"\\".escape_debug().to_string(), "\\'\\\"\\\\");
+    assert_eq!("\u{7f}\u{ff}".escape_debug().to_string(), "\\u{7f}\u{ff}");
+    assert_eq!("\u{100}\u{ffff}".escape_debug().to_string(), "\u{100}\\u{ffff}");
+    assert_eq!("\u{10000}\u{10ffff}".escape_debug().to_string(), "\u{10000}\\u{10ffff}");
+    assert_eq!("ab\u{200b}".escape_debug().to_string(), "ab\\u{200b}");
+    assert_eq!("\u{10d4ea}\r".escape_debug().to_string(), "\\u{10d4ea}\\r");
+    assert_eq!("\u{301}a\u{301}bé\u{e000}".escape_debug().to_string(),
+               "\\u{301}a\u{301}bé\\u{e000}");
 }
 
 #[test]
 fn test_escape_default() {
-    assert_eq!("abc".escape_default(), "abc");
-    assert_eq!("a c".escape_default(), "a c");
-    assert_eq!("éèê".escape_default(), "\\u{e9}\\u{e8}\\u{ea}");
-    assert_eq!("\r\n\t".escape_default(), "\\r\\n\\t");
-    assert_eq!("'\"\\".escape_default(), "\\'\\\"\\\\");
-    assert_eq!("\u{7f}\u{ff}".escape_default(), "\\u{7f}\\u{ff}");
-    assert_eq!("\u{100}\u{ffff}".escape_default(), "\\u{100}\\u{ffff}");
-    assert_eq!("\u{10000}\u{10ffff}".escape_default(), "\\u{10000}\\u{10ffff}");
-    assert_eq!("ab\u{200b}".escape_default(), "ab\\u{200b}");
-    assert_eq!("\u{10d4ea}\r".escape_default(), "\\u{10d4ea}\\r");
+    assert_eq!("abc".escape_default().to_string(), "abc");
+    assert_eq!("a c".escape_default().to_string(), "a c");
+    assert_eq!("éèê".escape_default().to_string(), "\\u{e9}\\u{e8}\\u{ea}");
+    assert_eq!("\r\n\t".escape_default().to_string(), "\\r\\n\\t");
+    assert_eq!("'\"\\".escape_default().to_string(), "\\'\\\"\\\\");
+    assert_eq!("\u{7f}\u{ff}".escape_default().to_string(), "\\u{7f}\\u{ff}");
+    assert_eq!("\u{100}\u{ffff}".escape_default().to_string(), "\\u{100}\\u{ffff}");
+    assert_eq!("\u{10000}\u{10ffff}".escape_default().to_string(), "\\u{10000}\\u{10ffff}");
+    assert_eq!("ab\u{200b}".escape_default().to_string(), "ab\\u{200b}");
+    assert_eq!("\u{10d4ea}\r".escape_default().to_string(), "\\u{10d4ea}\\r");
 }
 
 #[test]
diff --git a/src/libgraphviz/lib.rs b/src/libgraphviz/lib.rs
index fadcfaec4b2..6ee151a6484 100644
--- a/src/libgraphviz/lib.rs
+++ b/src/libgraphviz/lib.rs
@@ -538,7 +538,7 @@ impl<'a> LabelText<'a> {
             EscStr(s) => s,
             LabelStr(s) => {
                 if s.contains('\\') {
-                    (&*s).escape_default().into()
+                    (&*s).escape_default().to_string().into()
                 } else {
                     s
                 }
diff --git a/src/libsyntax/attr/mod.rs b/src/libsyntax/attr/mod.rs
index 0c3aedae715..6c3f6c173ed 100644
--- a/src/libsyntax/attr/mod.rs
+++ b/src/libsyntax/attr/mod.rs
@@ -634,7 +634,7 @@ impl LitKind {
 
         match *self {
             LitKind::Str(string, ast::StrStyle::Cooked) => {
-                let escaped = string.as_str().escape_default();
+                let escaped = string.as_str().escape_default().to_string();
                 Token::Literal(token::Lit::Str_(Symbol::intern(&escaped)), None)
             }
             LitKind::Str(string, ast::StrStyle::Raw(n)) => {
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index c670f47b597..cdf805176a2 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -606,7 +606,7 @@ pub trait PrintState<'a> {
         match lit.node {
             ast::LitKind::Str(st, style) => self.print_string(&st.as_str(), style),
             ast::LitKind::Err(st) => {
-                let st = st.as_str().escape_debug();
+                let st = st.as_str().escape_debug().to_string();
                 let mut res = String::with_capacity(st.len() + 2);
                 res.push('\'');
                 res.push_str(&st);