splitty-1.0.2/.cargo_vcs_info.json0000644000000001360000000000100125440ustar { "git": { "sha1": "005dafd17141838fdbea72ec4c5823b66e320e3e" }, "path_in_vcs": "" }splitty-1.0.2/.gitignore000064400000000000000000000000231046102023000133170ustar 00000000000000/target Cargo.lock splitty-1.0.2/Cargo.toml0000644000000017340000000000100105470ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "splitty" version = "1.0.2" authors = ["dystroy "] build = false autobins = false autoexamples = false autotests = false autobenches = false description = "a string splitter taking quotes into account" readme = "README.md" keywords = [ "string", "split", "quote", "no-std", ] categories = [ "parsing", "no-std", ] license = "MIT" repository = "https://github.com/Canop/splitty" [lib] name = "splitty" path = "src/lib.rs" [dependencies] splitty-1.0.2/Cargo.toml.orig000064400000000000000000000005201046102023000142200ustar 00000000000000[package] name = "splitty" version = "1.0.2" authors = ["dystroy "] repository = "https://github.com/Canop/splitty" description = "a string splitter taking quotes into account" edition = "2018" keywords = ["string", "split", "quote", "no-std"] license = "MIT" categories = ["parsing", "no-std"] [dependencies] splitty-1.0.2/LICENSE000064400000000000000000000020461046102023000123430ustar 00000000000000MIT License Copyright (c) 2020 Canop Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. splitty-1.0.2/README.md000064400000000000000000000015251046102023000126160ustar 00000000000000[![MIT][s2]][l2] [![Latest Version][s1]][l1] [![docs][s3]][l3] [![Chat on Miaou][s4]][l4] [s1]: https://img.shields.io/crates/v/splitty.svg [l1]: https://crates.io/crates/splitty [s2]: https://img.shields.io/badge/license-MIT-blue.svg [l2]: LICENSE [s3]: https://docs.rs/splitty/badge.svg [l3]: https://docs.rs/splitty/ [s4]: https://miaou.dystroy.org/static/shields/room.svg [l4]: https://miaou.dystroy.org/3 # splitty A no-std string splitter for which spaces between quotes aren't separators. Quotes not starting or ending a substring are handled as ordinary characters. ```rust use splitty::*; let cmd = "xterm -e \"vi /some/path\""; let mut token = split_unquoted_char(cmd, ' ') .unwrap_quotes(true); assert_eq!(token.next(), Some("xterm")); assert_eq!(token.next(), Some("-e")); assert_eq!(token.next(), Some("vi /some/path")); ``` splitty-1.0.2/src/lib.rs000064400000000000000000000020461046102023000132410ustar 00000000000000//! A no-std string splitter for which spaces between //! quotes aren't separators. //! //! //! ``` //! use splitty::*; //! //! let cmd = "xterm -e \"vi /some/path\""; //! //! let mut token = split_unquoted_char(cmd, ' ') //! .unwrap_quotes(true); //! //! assert_eq!(token.next(), Some("xterm")); //! assert_eq!(token.next(), Some("-e")); //! assert_eq!(token.next(), Some("vi /some/path")); //! assert_eq!(token.next(), None); //! ``` //! //! Quotes not starting or ending a substring are handled as ordinary characters. //! //! Splitty has a limited set of features but is tested for corner-cases: //! //! ``` //! use splitty::*; //! //! let cmd = r#" a "2 * 试" x"x "z "#; //! //! let mut token = split_unquoted_whitespace(cmd) //! .unwrap_quotes(true); //! //! assert_eq!(token.next(), Some("a")); //! assert_eq!(token.next(), Some("2 * 试")); //! assert_eq!(token.next(), Some("x\"x")); //! assert_eq!(token.next(), Some("\"z ")); //! assert_eq!(token.next(), None); //! ``` #![no_std] mod split_unquoted_char; pub use split_unquoted_char::*; splitty-1.0.2/src/split_unquoted_char.rs000064400000000000000000000122471046102023000165530ustar 00000000000000/// A string splitter splitting on a specific char unless it's /// part of a substring starting and ending with a quote (`"`). /// Quotes around a substring are removed if required. pub struct SplitUnquotedChar<'s> { src: &'s str, unwrap_quotes: bool, delimitor: char, } impl<'s> SplitUnquotedChar<'s> { /// Create a new token iterator /// /// ``` /// let cmd = r#"Type "rhit -p blog" or "rhit --path blog""#; /// /// let mut split = splitty::SplitUnquotedChar::new(cmd, ' '); /// assert_eq!(split.next(), Some("Type")); /// assert_eq!(split.next(), Some("\"rhit -p blog\"")); /// assert_eq!(split.next(), Some("or")); /// assert_eq!(split.next(), Some("\"rhit --path blog\"")); /// assert_eq!(split.next(), None); /// /// let mut split = splitty::SplitUnquotedChar::new(cmd, ' ') /// .unwrap_quotes(true); /// assert_eq!(split.next(), Some("Type")); /// assert_eq!(split.next(), Some("rhit -p blog")); /// assert_eq!(split.next(), Some("or")); /// assert_eq!(split.next(), Some("rhit --path blog")); /// assert_eq!(split.next(), None); /// ``` pub fn new(src: &'s str, delimitor: char) -> Self { Self { src, unwrap_quotes: false, delimitor, } } /// Set whether token starting and ending with a quote /// should have them removed pub fn unwrap_quotes(&self, b: bool) -> Self { Self { src: self.src, unwrap_quotes: b, delimitor: self.delimitor, } } } impl<'s> Iterator for SplitUnquotedChar<'s> { type Item = &'s str; fn next(&mut self) -> Option<&'s str> { // we ignore delimitors at the start self.src = self.src.trim_start_matches(self.delimitor); let mut char_indices = self.src.char_indices(); if let Some((_, c0)) = char_indices.next() { let mut previous = c0; for (bi, c) in self.src.char_indices() { if c == self.delimitor { if c0 == '"' { if bi == 1 || previous != '"' { previous = c; continue; } // the first and last quotes aren't part of the // returned token let token = if self.unwrap_quotes { &self.src[1..bi - 1] } else { &self.src[..bi] }; self.src = &self.src[bi..]; return Some(token); } let token = &self.src[..bi]; self.src = &self.src[bi..]; return Some(token); } previous = c; } let unwrap = self.unwrap_quotes && c0 == '"' && previous == '"' && self.src.len() > 1; let token = if unwrap { &self.src[1..self.src.len() - 1] } else { self.src }; self.src = &self.src[0..0]; Some(token) } else { None } } } /// Return a new iterator of the the whitespace separated tokens /// of the given string, taking quotes into account pub fn split_unquoted_whitespace(src: & str) -> SplitUnquotedChar { SplitUnquotedChar::new(src, ' ') } /// Return a new iterator of the the `delimitor` separated tokens /// of the given string, taking quotes into account pub fn split_unquoted_char(src: &str, delimitor: char) -> SplitUnquotedChar { SplitUnquotedChar::new(src, delimitor) } #[cfg(test)] mod split_unquoted_whitespace_test { use super::*; macro_rules! t { ($src:literal -> [$($token:literal),* $(,)?]) => { let mut split = SplitUnquotedChar::new($src, ' ') .unwrap_quotes(true); $( assert_eq!(split.next(), Some($token)); )* assert_eq!(split.next(), None); } } #[test] fn test_split_unquoted_whitespace() { t!("" -> []); t!(" " -> []); t!(" a 试bc d " -> ["a", "试bc", "d"]); t!("e^iπ^ = 1" -> ["e^iπ^", "=", "1"]); t!("1234" -> ["1234"]); t!("1234\"" -> ["1234\""]); t!(r#"""# -> [r#"""#]); t!(r#""a""# -> [r#"a"#]); t!(r#" " "# -> [r#"" "#]); t!(r#"a "deux mots" b"# -> ["a", "deux mots", "b"]); t!(r#" " ""# -> [" "]); t!(r#" a "2 * 试" x"x "z "# -> ["a", "2 * 试", "x\"x", "\"z "]); t!(r#"""""# -> ["\""]); t!(r#""""""# -> ["\"\""]); } } #[test] fn test_issue_1(){ let s = r#"1,2,3,4"#; let mut it = split_unquoted_char(s, ','); assert_eq!(it.next(), Some("1")); assert_eq!(it.next(), Some("2")); assert_eq!(it.next(), Some("3")); assert_eq!(it.next(), Some("4")); assert_eq!(it.next(), None); assert_eq!(it.next(), None); } #[test] fn test_issue_2() { let mut token = split_unquoted_whitespace("\"one\" two").unwrap_quotes(false); assert_eq!(token.next(), Some("\"one\"")); assert_eq!(token.next(), Some("two")); assert_eq!(token.next(), None); }