about summary refs log tree commit diff
diff options
context:
space:
mode:
authorYuki Okushi <huyuumi.dev@gmail.com>2020-07-29 09:24:17 +0900
committerGitHub <noreply@github.com>2020-07-29 09:24:17 +0900
commit6968b75bd0524915d3fcf6b201b41827d4695603 (patch)
tree28c242417d38f01677cb0d1a66e616d71cb2fdaf
parent157975c6c40b02e56e931617f1d6c8148bc7d2bd (diff)
parent6e9dc7d9ff806eb3a0b99ffd10892dac8e6f9739 (diff)
downloadrust-6968b75bd0524915d3fcf6b201b41827d4695603.tar.gz
rust-6968b75bd0524915d3fcf6b201b41827d4695603.zip
Rollup merge of #74707 - matklad:split_once, r=dtolnay
Add str::[r]split_once

This is useful for quick&dirty parsing of key: value config pairs. Used a bunch in Cargo and rust-analyzer:

* https://github.com/rust-lang/cargo/search?q=splitn%282&unscoped_q=splitn%282
* https://github.com/rust-analyzer/rust-analyzer/search?q=split_delim&unscoped_q=split_delim

In theory, once const-generics are done, this functionality could be achieved without a dedicated method with

```rust
match s.splitn(delimier, 2).collect_array::<2>() {
  Some([prefix, suffix]) => todo!(),
  None => todo!(),
}
```

Even in that world, having a dedicated method seems clearer on the intention.

I am not sure about naming -- this is something I've just came up with yesterday, I don't know off the top of my head analogs in other languages.

If T-libs thinks this is a reasonable API to have, I'll open a tracking issue and add more thorough tests.
-rw-r--r--library/alloc/tests/lib.rs1
-rw-r--r--library/alloc/tests/str.rs24
-rw-r--r--library/core/src/str/mod.rs41
3 files changed, 66 insertions, 0 deletions
diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs
index e2dc816b015..fa20a466715 100644
--- a/library/alloc/tests/lib.rs
+++ b/library/alloc/tests/lib.rs
@@ -6,6 +6,7 @@
 #![feature(map_first_last)]
 #![feature(new_uninit)]
 #![feature(pattern)]
+#![feature(str_split_once)]
 #![feature(trusted_len)]
 #![feature(try_reserve)]
 #![feature(unboxed_closures)]
diff --git a/library/alloc/tests/str.rs b/library/alloc/tests/str.rs
index eee98d45340..b20cf076aca 100644
--- a/library/alloc/tests/str.rs
+++ b/library/alloc/tests/str.rs
@@ -1319,6 +1319,30 @@ fn test_rsplitn() {
 }
 
 #[test]
+fn test_split_once() {
+    assert_eq!("".split_once("->"), None);
+    assert_eq!("-".split_once("->"), None);
+    assert_eq!("->".split_once("->"), Some(("", "")));
+    assert_eq!("a->".split_once("->"), Some(("a", "")));
+    assert_eq!("->b".split_once("->"), Some(("", "b")));
+    assert_eq!("a->b".split_once("->"), Some(("a", "b")));
+    assert_eq!("a->b->c".split_once("->"), Some(("a", "b->c")));
+    assert_eq!("---".split_once("--"), Some(("", "-")));
+}
+
+#[test]
+fn test_rsplit_once() {
+    assert_eq!("".rsplit_once("->"), None);
+    assert_eq!("-".rsplit_once("->"), None);
+    assert_eq!("->".rsplit_once("->"), Some(("", "")));
+    assert_eq!("a->".rsplit_once("->"), Some(("a", "")));
+    assert_eq!("->b".rsplit_once("->"), Some(("", "b")));
+    assert_eq!("a->b".rsplit_once("->"), Some(("a", "b")));
+    assert_eq!("a->b->c".rsplit_once("->"), Some(("a->b", "c")));
+    assert_eq!("---".rsplit_once("--"), Some(("-", "")));
+}
+
+#[test]
 fn test_split_whitespace() {
     let data = "\n \tMäry   häd\tä  little lämb\nLittle lämb\n";
     let words: Vec<&str> = data.split_whitespace().collect();
diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs
index 790ec4bd24f..9d7e38d0e18 100644
--- a/library/core/src/str/mod.rs
+++ b/library/core/src/str/mod.rs
@@ -3610,6 +3610,47 @@ impl str {
         RSplitN(self.splitn(n, pat).0)
     }
 
+    /// Splits the string on the first occurrence of the specified delimiter and
+    /// returns prefix before delimiter and suffix after delimiter.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(str_split_once)]
+    ///
+    /// assert_eq!("cfg".split_once('='), None);
+    /// assert_eq!("cfg=foo".split_once('='), Some(("cfg", "foo")));
+    /// assert_eq!("cfg=foo=bar".split_once('='), Some(("cfg", "foo=bar")));
+    /// ```
+    #[unstable(feature = "str_split_once", reason = "newly added", issue = "74773")]
+    #[inline]
+    pub fn split_once<'a, P: Pattern<'a>>(&'a self, delimiter: P) -> Option<(&'a str, &'a str)> {
+        let (start, end) = delimiter.into_searcher(self).next_match()?;
+        Some((&self[..start], &self[end..]))
+    }
+
+    /// Splits the string on the last occurrence of the specified delimiter and
+    /// returns prefix before delimiter and suffix after delimiter.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(str_split_once)]
+    ///
+    /// assert_eq!("cfg".rsplit_once('='), None);
+    /// assert_eq!("cfg=foo".rsplit_once('='), Some(("cfg", "foo")));
+    /// assert_eq!("cfg=foo=bar".rsplit_once('='), Some(("cfg=foo", "bar")));
+    /// ```
+    #[unstable(feature = "str_split_once", reason = "newly added", issue = "74773")]
+    #[inline]
+    pub fn rsplit_once<'a, P>(&'a self, delimiter: P) -> Option<(&'a str, &'a str)>
+    where
+        P: Pattern<'a, Searcher: ReverseSearcher<'a>>,
+    {
+        let (start, end) = delimiter.into_searcher(self).next_match_back()?;
+        Some((&self[..start], &self[end..]))
+    }
+
     /// An iterator over the disjoint matches of a pattern within the given string
     /// slice.
     ///