From aecf8c2a624d45d1f881caa53c960708cc0f655a Mon Sep 17 00:00:00 2001 From: varkor Date: Thu, 21 Jun 2018 14:04:53 +0100 Subject: [PATCH 1/4] Add trim_start, trim_end, trim_start_matches and trim_end_matches --- src/liballoc/tests/lib.rs | 1 + src/liballoc/tests/str.rs | 58 +++++++++++++ src/libcore/str/mod.rs | 172 +++++++++++++++++++++++++++++++++++--- 3 files changed, 218 insertions(+), 13 deletions(-) diff --git a/src/liballoc/tests/lib.rs b/src/liballoc/tests/lib.rs index 618aff963f22d..0dcbcf50fb401 100644 --- a/src/liballoc/tests/lib.rs +++ b/src/liballoc/tests/lib.rs @@ -25,6 +25,7 @@ #![feature(unboxed_closures)] #![feature(exact_chunks)] #![feature(repeat_generic_slice)] +#![feature(trim_direction)] extern crate alloc_system; extern crate core; diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs index 6275c7bb11206..a1940c894b872 100644 --- a/src/liballoc/tests/str.rs +++ b/src/liballoc/tests/str.rs @@ -726,6 +726,36 @@ fn test_is_char_boundary() { } } +#[test] +fn test_trim_start_matches() { + let v: &[char] = &[]; + assert_eq!(" *** foo *** ".trim_start_matches(v), " *** foo *** "); + let chars: &[char] = &['*', ' ']; + assert_eq!(" *** foo *** ".trim_start_matches(chars), "foo *** "); + assert_eq!(" *** *** ".trim_start_matches(chars), ""); + assert_eq!("foo *** ".trim_start_matches(chars), "foo *** "); + + assert_eq!("11foo1bar11".trim_start_matches('1'), "foo1bar11"); + let chars: &[char] = &['1', '2']; + assert_eq!("12foo1bar12".trim_start_matches(chars), "foo1bar12"); + assert_eq!("123foo1bar123".trim_start_matches(|c: char| c.is_numeric()), "foo1bar123"); +} + +#[test] +fn test_trim_end_matches() { + let v: &[char] = &[]; + assert_eq!(" *** foo *** ".trim_end_matches(v), " *** foo *** "); + let chars: &[char] = &['*', ' ']; + assert_eq!(" *** foo *** ".trim_end_matches(chars), " *** foo"); + assert_eq!(" *** *** ".trim_end_matches(chars), ""); + assert_eq!(" *** foo".trim_end_matches(chars), " *** foo"); + + assert_eq!("11foo1bar11".trim_end_matches('1'), "11foo1bar"); + let chars: &[char] = &['1', '2']; + assert_eq!("12foo1bar12".trim_end_matches(chars), "12foo1bar"); + assert_eq!("123foo1bar123".trim_end_matches(|c: char| c.is_numeric()), "123foo1bar"); +} + #[test] fn test_trim_left_matches() { let v: &[char] = &[]; @@ -771,6 +801,26 @@ fn test_trim_matches() { assert_eq!("123foo1bar123".trim_matches(|c: char| c.is_numeric()), "foo1bar"); } +#[test] +fn test_trim_start() { + assert_eq!("".trim_start(), ""); + assert_eq!("a".trim_start(), "a"); + assert_eq!(" ".trim_start(), ""); + assert_eq!(" blah".trim_start(), "blah"); + assert_eq!(" \u{3000} wut".trim_start(), "wut"); + assert_eq!("hey ".trim_start(), "hey "); +} + +#[test] +fn test_trim_end() { + assert_eq!("".trim_end(), ""); + assert_eq!("a".trim_end(), "a"); + assert_eq!(" ".trim_end(), ""); + assert_eq!("blah ".trim_end(), "blah"); + assert_eq!("wut \u{3000} ".trim_end(), "wut"); + assert_eq!(" hey".trim_end(), " hey"); +} + #[test] fn test_trim_left() { assert_eq!("".trim_left(), ""); @@ -1518,12 +1568,20 @@ fn trim_ws() { "a \t "); assert_eq!(" \t a \t ".trim_right_matches(|c: char| c.is_whitespace()), " \t a"); + assert_eq!(" \t a \t ".trim_start_matches(|c: char| c.is_whitespace()), + "a \t "); + assert_eq!(" \t a \t ".trim_end_matches(|c: char| c.is_whitespace()), + " \t a"); assert_eq!(" \t a \t ".trim_matches(|c: char| c.is_whitespace()), "a"); assert_eq!(" \t \t ".trim_left_matches(|c: char| c.is_whitespace()), ""); assert_eq!(" \t \t ".trim_right_matches(|c: char| c.is_whitespace()), ""); + assert_eq!(" \t \t ".trim_start_matches(|c: char| c.is_whitespace()), + ""); + assert_eq!(" \t \t ".trim_end_matches(|c: char| c.is_whitespace()), + ""); assert_eq!(" \t \t ".trim_matches(|c: char| c.is_whitespace()), ""); } diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 86b8349fa3c89..eda3551963610 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -3587,6 +3587,78 @@ impl str { self.trim_matches(|c: char| c.is_whitespace()) } + /// Returns a string slice with leading whitespace removed. + /// + /// 'Whitespace' is defined according to the terms of the Unicode Derived + /// Core Property `White_Space`. + /// + /// # Text directionality + /// + /// A string is a sequence of bytes. `start` in this context means the first + /// position of that byte string; for a left-to-right language like English or + /// Russian, this will be left side; and for right-to-left languages like + /// like Arabic or Hebrew, this will be the right side. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s = " Hello\tworld\t"; + /// + /// assert_eq!("Hello\tworld\t", s.trim_start()); + /// ``` + /// + /// Directionality: + /// + /// ``` + /// let s = " English"; + /// assert!(Some('E') == s.trim_start().chars().next()); + /// + /// let s = " עברית"; + /// assert!(Some('ע') == s.trim_start().chars().next()); + /// ``` + #[unstable(feature = "trim_direction", issue = "30459")] + pub fn trim_start(&self) -> &str { + self.trim_start_matches(|c: char| c.is_whitespace()) + } + + /// Returns a string slice with trailing whitespace removed. + /// + /// 'Whitespace' is defined according to the terms of the Unicode Derived + /// Core Property `White_Space`. + /// + /// # Text directionality + /// + /// A string is a sequence of bytes. `end` in this context means the last + /// position of that byte string; for a left-to-right language like English or + /// Russian, this will be right side; and for right-to-left languages like + /// like Arabic or Hebrew, this will be the left side. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s = " Hello\tworld\t"; + /// + /// assert_eq!(" Hello\tworld", s.trim_end()); + /// ``` + /// + /// Directionality: + /// + /// ``` + /// let s = "English "; + /// assert!(Some('h') == s.trim_end().chars().rev().next()); + /// + /// let s = "עברית "; + /// assert!(Some('ת') == s.trim_end().chars().rev().next()); + /// ``` + #[unstable(feature = "trim_direction", issue = "30459")] + pub fn trim_end(&self) -> &str { + self.trim_end_matches(|c: char| c.is_whitespace()) + } + /// Returns a string slice with leading whitespace removed. /// /// 'Whitespace' is defined according to the terms of the Unicode Derived @@ -3619,8 +3691,9 @@ impl str { /// assert!(Some('ע') == s.trim_left().chars().next()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_deprecated(reason = "superseded by `trim_start`", since = "1.33.0")] pub fn trim_left(&self) -> &str { - self.trim_left_matches(|c: char| c.is_whitespace()) + self.trim_start() } /// Returns a string slice with trailing whitespace removed. @@ -3655,8 +3728,9 @@ impl str { /// assert!(Some('ת') == s.trim_right().chars().rev().next()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_deprecated(reason = "superseded by `trim_end`", since = "1.33.0")] pub fn trim_right(&self) -> &str { - self.trim_right_matches(|c: char| c.is_whitespace()) + self.trim_end() } /// Returns a string slice with all prefixes and suffixes that match a @@ -3725,14 +3799,14 @@ impl str { /// Basic usage: /// /// ``` - /// assert_eq!("11foo1bar11".trim_left_matches('1'), "foo1bar11"); - /// assert_eq!("123foo1bar123".trim_left_matches(char::is_numeric), "foo1bar123"); + /// assert_eq!("11foo1bar11".trim_start_matches('1'), "foo1bar11"); + /// assert_eq!("123foo1bar123".trim_start_matches(char::is_numeric), "foo1bar123"); /// /// let x: &[_] = &['1', '2']; - /// assert_eq!("12foo1bar12".trim_left_matches(x), "foo1bar12"); + /// assert_eq!("12foo1bar12".trim_start_matches(x), "foo1bar12"); /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn trim_left_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str { + #[unstable(feature = "trim_direction", issue = "30459")] + pub fn trim_start_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str { let mut i = self.len(); let mut matcher = pat.into_searcher(self); if let Some((a, _)) = matcher.next_reject() { @@ -3764,20 +3838,20 @@ impl str { /// Simple patterns: /// /// ``` - /// assert_eq!("11foo1bar11".trim_right_matches('1'), "11foo1bar"); - /// assert_eq!("123foo1bar123".trim_right_matches(char::is_numeric), "123foo1bar"); + /// assert_eq!("11foo1bar11".trim_end_matches('1'), "11foo1bar"); + /// assert_eq!("123foo1bar123".trim_end_matches(char::is_numeric), "123foo1bar"); /// /// let x: &[_] = &['1', '2']; - /// assert_eq!("12foo1bar12".trim_right_matches(x), "12foo1bar"); + /// assert_eq!("12foo1bar12".trim_end_matches(x), "12foo1bar"); /// ``` /// /// A more complex pattern, using a closure: /// /// ``` - /// assert_eq!("1fooX".trim_right_matches(|c| c == '1' || c == 'X'), "1foo"); + /// assert_eq!("1fooX".trim_end_matches(|c| c == '1' || c == 'X'), "1foo"); /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn trim_right_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str + #[unstable(feature = "trim_direction", issue = "30459")] + pub fn trim_end_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str where P::Searcher: ReverseSearcher<'a> { let mut j = 0; @@ -3791,6 +3865,78 @@ impl str { } } + /// Returns a string slice with all prefixes that match a pattern + /// repeatedly removed. + /// + /// The pattern can be a `&str`, [`char`], or a closure that determines if + /// a character matches. + /// + /// [`char`]: primitive.char.html + /// + /// # Text directionality + /// + /// A string is a sequence of bytes. 'Left' in this context means the first + /// position of that byte string; for a language like Arabic or Hebrew + /// which are 'right to left' rather than 'left to right', this will be + /// the _right_ side, not the left. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// assert_eq!("11foo1bar11".trim_left_matches('1'), "foo1bar11"); + /// assert_eq!("123foo1bar123".trim_left_matches(char::is_numeric), "foo1bar123"); + /// + /// let x: &[_] = &['1', '2']; + /// assert_eq!("12foo1bar12".trim_left_matches(x), "foo1bar12"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_deprecated(reason = "superseded by `trim_start_matches`", since = "1.33.0")] + pub fn trim_left_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str { + self.trim_start_matches(pat) + } + + /// Returns a string slice with all suffixes that match a pattern + /// repeatedly removed. + /// + /// The pattern can be a `&str`, [`char`], or a closure that + /// determines if a character matches. + /// + /// [`char`]: primitive.char.html + /// + /// # Text directionality + /// + /// A string is a sequence of bytes. 'Right' in this context means the last + /// position of that byte string; for a language like Arabic or Hebrew + /// which are 'right to left' rather than 'left to right', this will be + /// the _left_ side, not the right. + /// + /// # Examples + /// + /// Simple patterns: + /// + /// ``` + /// assert_eq!("11foo1bar11".trim_right_matches('1'), "11foo1bar"); + /// assert_eq!("123foo1bar123".trim_right_matches(char::is_numeric), "123foo1bar"); + /// + /// let x: &[_] = &['1', '2']; + /// assert_eq!("12foo1bar12".trim_right_matches(x), "12foo1bar"); + /// ``` + /// + /// A more complex pattern, using a closure: + /// + /// ``` + /// assert_eq!("1fooX".trim_right_matches(|c| c == '1' || c == 'X'), "1foo"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_deprecated(reason = "superseded by `trim_end_matches`", since = "1.33.0")] + pub fn trim_right_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str + where P::Searcher: ReverseSearcher<'a> + { + self.trim_end_matches(pat) + } + /// Parses this string slice into another type. /// /// Because `parse` is so general, it can cause problems with type From 33067ad2011ce97e80a247c5a1a9bdb15fc4f376 Mon Sep 17 00:00:00 2001 From: varkor Date: Thu, 2 Aug 2018 22:00:13 +0100 Subject: [PATCH 2/4] Add #![feature(trim_direction)] to doc comments --- src/libcore/str/mod.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index eda3551963610..00e296be4f2d8 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -3604,14 +3604,17 @@ impl str { /// Basic usage: /// /// ``` - /// let s = " Hello\tworld\t"; + /// #![feature(trim_direction)] /// + /// let s = " Hello\tworld\t"; /// assert_eq!("Hello\tworld\t", s.trim_start()); /// ``` /// /// Directionality: /// /// ``` + /// #![feature(trim_direction)] + /// /// let s = " English"; /// assert!(Some('E') == s.trim_start().chars().next()); /// @@ -3640,14 +3643,17 @@ impl str { /// Basic usage: /// /// ``` - /// let s = " Hello\tworld\t"; + /// #![feature(trim_direction)] /// + /// let s = " Hello\tworld\t"; /// assert_eq!(" Hello\tworld", s.trim_end()); /// ``` /// /// Directionality: /// /// ``` + /// #![feature(trim_direction)] + /// /// let s = "English "; /// assert!(Some('h') == s.trim_end().chars().rev().next()); /// @@ -3799,6 +3805,8 @@ impl str { /// Basic usage: /// /// ``` + /// #![feature(trim_direction)] + /// /// assert_eq!("11foo1bar11".trim_start_matches('1'), "foo1bar11"); /// assert_eq!("123foo1bar123".trim_start_matches(char::is_numeric), "foo1bar123"); /// @@ -3838,6 +3846,8 @@ impl str { /// Simple patterns: /// /// ``` + /// #![feature(trim_direction)] + /// /// assert_eq!("11foo1bar11".trim_end_matches('1'), "11foo1bar"); /// assert_eq!("123foo1bar123".trim_end_matches(char::is_numeric), "123foo1bar"); /// @@ -3848,6 +3858,8 @@ impl str { /// A more complex pattern, using a closure: /// /// ``` + /// #![feature(trim_direction)] + /// /// assert_eq!("1fooX".trim_end_matches(|c| c == '1' || c == 'X'), "1foo"); /// ``` #[unstable(feature = "trim_direction", issue = "30459")] From 3e10ffcb81e8f887289904e5aaf272e872a9cb2c Mon Sep 17 00:00:00 2001 From: varkor Date: Sun, 5 Aug 2018 21:51:40 +0100 Subject: [PATCH 3/4] Make features stable and clarify examples --- src/liballoc/tests/lib.rs | 1 - src/libcore/str/mod.rs | 30 ++++++++---------------------- 2 files changed, 8 insertions(+), 23 deletions(-) diff --git a/src/liballoc/tests/lib.rs b/src/liballoc/tests/lib.rs index 0dcbcf50fb401..618aff963f22d 100644 --- a/src/liballoc/tests/lib.rs +++ b/src/liballoc/tests/lib.rs @@ -25,7 +25,6 @@ #![feature(unboxed_closures)] #![feature(exact_chunks)] #![feature(repeat_generic_slice)] -#![feature(trim_direction)] extern crate alloc_system; extern crate core; diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 00e296be4f2d8..0caab089a3bae 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -3604,8 +3604,6 @@ impl str { /// Basic usage: /// /// ``` - /// #![feature(trim_direction)] - /// /// let s = " Hello\tworld\t"; /// assert_eq!("Hello\tworld\t", s.trim_start()); /// ``` @@ -3613,15 +3611,13 @@ impl str { /// Directionality: /// /// ``` - /// #![feature(trim_direction)] - /// - /// let s = " English"; + /// let s = " English "; /// assert!(Some('E') == s.trim_start().chars().next()); /// - /// let s = " עברית"; + /// let s = " עברית "; /// assert!(Some('ע') == s.trim_start().chars().next()); /// ``` - #[unstable(feature = "trim_direction", issue = "30459")] + #[stable(feature = "trim_direction", since = "1.30.0")] pub fn trim_start(&self) -> &str { self.trim_start_matches(|c: char| c.is_whitespace()) } @@ -3643,8 +3639,6 @@ impl str { /// Basic usage: /// /// ``` - /// #![feature(trim_direction)] - /// /// let s = " Hello\tworld\t"; /// assert_eq!(" Hello\tworld", s.trim_end()); /// ``` @@ -3652,15 +3646,13 @@ impl str { /// Directionality: /// /// ``` - /// #![feature(trim_direction)] - /// - /// let s = "English "; + /// let s = " English "; /// assert!(Some('h') == s.trim_end().chars().rev().next()); /// - /// let s = "עברית "; + /// let s = " עברית "; /// assert!(Some('ת') == s.trim_end().chars().rev().next()); /// ``` - #[unstable(feature = "trim_direction", issue = "30459")] + #[stable(feature = "trim_direction", since = "1.30.0")] pub fn trim_end(&self) -> &str { self.trim_end_matches(|c: char| c.is_whitespace()) } @@ -3805,15 +3797,13 @@ impl str { /// Basic usage: /// /// ``` - /// #![feature(trim_direction)] - /// /// assert_eq!("11foo1bar11".trim_start_matches('1'), "foo1bar11"); /// assert_eq!("123foo1bar123".trim_start_matches(char::is_numeric), "foo1bar123"); /// /// let x: &[_] = &['1', '2']; /// assert_eq!("12foo1bar12".trim_start_matches(x), "foo1bar12"); /// ``` - #[unstable(feature = "trim_direction", issue = "30459")] + #[stable(feature = "trim_direction", since = "1.30.0")] pub fn trim_start_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str { let mut i = self.len(); let mut matcher = pat.into_searcher(self); @@ -3846,8 +3836,6 @@ impl str { /// Simple patterns: /// /// ``` - /// #![feature(trim_direction)] - /// /// assert_eq!("11foo1bar11".trim_end_matches('1'), "11foo1bar"); /// assert_eq!("123foo1bar123".trim_end_matches(char::is_numeric), "123foo1bar"); /// @@ -3858,11 +3846,9 @@ impl str { /// A more complex pattern, using a closure: /// /// ``` - /// #![feature(trim_direction)] - /// /// assert_eq!("1fooX".trim_end_matches(|c| c == '1' || c == 'X'), "1foo"); /// ``` - #[unstable(feature = "trim_direction", issue = "30459")] + #[stable(feature = "trim_direction", since = "1.30.0")] pub fn trim_end_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str where P::Searcher: ReverseSearcher<'a> { From ff7967056916f68f2df5214e47e9e72414b44fc3 Mon Sep 17 00:00:00 2001 From: varkor Date: Sun, 19 Aug 2018 22:36:59 +0100 Subject: [PATCH 4/4] Remove old tests --- src/liballoc/tests/str.rs | 50 --------------------------------------- 1 file changed, 50 deletions(-) diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs index a1940c894b872..a5fa7f0c4d938 100644 --- a/src/liballoc/tests/str.rs +++ b/src/liballoc/tests/str.rs @@ -756,36 +756,6 @@ fn test_trim_end_matches() { assert_eq!("123foo1bar123".trim_end_matches(|c: char| c.is_numeric()), "123foo1bar"); } -#[test] -fn test_trim_left_matches() { - let v: &[char] = &[]; - assert_eq!(" *** foo *** ".trim_left_matches(v), " *** foo *** "); - let chars: &[char] = &['*', ' ']; - assert_eq!(" *** foo *** ".trim_left_matches(chars), "foo *** "); - assert_eq!(" *** *** ".trim_left_matches(chars), ""); - assert_eq!("foo *** ".trim_left_matches(chars), "foo *** "); - - assert_eq!("11foo1bar11".trim_left_matches('1'), "foo1bar11"); - let chars: &[char] = &['1', '2']; - assert_eq!("12foo1bar12".trim_left_matches(chars), "foo1bar12"); - assert_eq!("123foo1bar123".trim_left_matches(|c: char| c.is_numeric()), "foo1bar123"); -} - -#[test] -fn test_trim_right_matches() { - let v: &[char] = &[]; - assert_eq!(" *** foo *** ".trim_right_matches(v), " *** foo *** "); - let chars: &[char] = &['*', ' ']; - assert_eq!(" *** foo *** ".trim_right_matches(chars), " *** foo"); - assert_eq!(" *** *** ".trim_right_matches(chars), ""); - assert_eq!(" *** foo".trim_right_matches(chars), " *** foo"); - - assert_eq!("11foo1bar11".trim_right_matches('1'), "11foo1bar"); - let chars: &[char] = &['1', '2']; - assert_eq!("12foo1bar12".trim_right_matches(chars), "12foo1bar"); - assert_eq!("123foo1bar123".trim_right_matches(|c: char| c.is_numeric()), "123foo1bar"); -} - #[test] fn test_trim_matches() { let v: &[char] = &[]; @@ -821,26 +791,6 @@ fn test_trim_end() { assert_eq!(" hey".trim_end(), " hey"); } -#[test] -fn test_trim_left() { - assert_eq!("".trim_left(), ""); - assert_eq!("a".trim_left(), "a"); - assert_eq!(" ".trim_left(), ""); - assert_eq!(" blah".trim_left(), "blah"); - assert_eq!(" \u{3000} wut".trim_left(), "wut"); - assert_eq!("hey ".trim_left(), "hey "); -} - -#[test] -fn test_trim_right() { - assert_eq!("".trim_right(), ""); - assert_eq!("a".trim_right(), "a"); - assert_eq!(" ".trim_right(), ""); - assert_eq!("blah ".trim_right(), "blah"); - assert_eq!("wut \u{3000} ".trim_right(), "wut"); - assert_eq!(" hey".trim_right(), " hey"); -} - #[test] fn test_trim() { assert_eq!("".trim(), "");