├── tests ├── lib.rs └── unit │ ├── escape.rs │ ├── readme.rs │ ├── split.rs │ ├── utils.rs │ ├── index.rs │ ├── count.rs │ ├── case.rs │ ├── strip.rs │ ├── chop.rs │ └── query.rs ├── .gitignore ├── Cargo.toml ├── .travis.yml ├── LICENSE.md ├── src ├── escape.rs ├── index.rs ├── count.rs ├── split.rs ├── strip.rs ├── case.rs ├── lib.rs ├── query.rs └── manipulate.rs └── README.md /tests/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate voca_rs; 2 | 3 | #[cfg(test)] 4 | 5 | mod unit { 6 | mod case; 7 | mod chop; 8 | mod count; 9 | mod escape; 10 | mod index; 11 | mod manipulate; 12 | mod query; 13 | mod readme; 14 | mod split; 15 | mod strip; 16 | mod utils; 17 | } 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | # Ignore documentation 13 | doc/ 14 | 15 | .vscode/ 16 | .idea/ 17 | .dccache -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "voca_rs" 3 | version = "1.15.2" 4 | authors = ["A. Merezhanyi "] 5 | license = "MIT" 6 | description = "Voca_rs is a Rust library for manipulating [unicode] strings" 7 | 8 | documentation = "https://docs.rs/voca_rs/" 9 | repository = "https://github.com/a-merezhanyi/voca_rs" 10 | 11 | keywords = ["string", "case", "snake", "camel", "unicode"] 12 | categories = ["encoding"] 13 | readme = "README.md" 14 | include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE"] 15 | 16 | [badges] 17 | travis-ci = { repository = "a-merezhanyi/voca_rs" } 18 | codecov = { repository = "a-merezhanyi/voca_rs", branch = "master", service = "github" } 19 | 20 | [dependencies] 21 | regex = "1.6.0" 22 | stfu8 = "0.2.5" 23 | unicode-segmentation = "1.10.0" 24 | 25 | [profile.dev] 26 | lto = true 27 | opt-level = 0 28 | 29 | [profile.release] 30 | opt-level = 3 31 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - stable 4 | 5 | cache: cargo 6 | os: 7 | - linux 8 | 9 | 10 | after_success: 11 | # Upload docs 12 | - | 13 | if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_RUST_VERSION" == "stable" && "$TRAVIS_PULL_REQUEST" = "false" && "$TRAVIS_BRANCH" == "master" ]]; then 14 | cargo doc && 15 | echo "" > target/doc/index.html && 16 | git clone https://github.com/davisp/ghp-import.git && 17 | ./ghp-import/ghp_import.py -n -p -f -m "Documentation upload" -r https://"$GH_TOKEN"@github.com/"$TRAVIS_REPO_SLUG.git" target/doc && 18 | echo "Uploaded documentation" 19 | fi 20 | # Coverage report 21 | - | 22 | if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_RUST_VERSION" == "stable" ]]; then 23 | wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz && 24 | tar xzf master.tar.gz && 25 | cd kcov-master && 26 | mkdir build && 27 | cd build && 28 | cmake .. && 29 | make && 30 | sudo make install && 31 | cd ../.. && 32 | rm -rf kcov-master && 33 | for file in target/debug/lib-*[^\.d]; do mkdir -p "target/cov/$(basename $file)"; kcov --exclude-pattern=/.cargo,/usr/lib --verify "target/cov/$(basename $file)" "$file"; done && 34 | for file in target/debug/voca_rs-*[^\.d]; do mkdir -p "target/cov/$(basename $file)"; kcov --exclude-pattern=/.cargo,/usr/lib --verify "target/cov/$(basename $file)" "$file"; done && 35 | bash <(curl -s https://codecov.io/bash) && 36 | echo "Uploaded code coverage" 37 | fi 38 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2022 A. Merezhanyi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | 1. The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | 2. Redistributions of source code must retain the above copyright notice, this 16 | list of conditions and the following disclaimer. 17 | 18 | 3. Redistributions in binary form must reproduce the above copyright notice, 19 | this list of conditions and the following disclaimer in the documentation 20 | and/or other materials provided with the distribution. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | SOFTWARE. 29 | 30 | ## Acknowledgements: 31 | 32 | - Voca.js https://github.com/panzerdp/voca `Copyright (c) 2017 Dmitri Pavlutin` 33 | 34 | - Unidecode https://github.com/chowdhurya/rust-unidecode `Copyright (c) 2015, Amit Chowdhury` 35 | 36 | - heck https://github.com/withoutboats/heck `Copyright (c) 2018, Saoirse Shipwreckt` 37 | 38 | - Inflector https://github.com/whatisinternet/inflector `Copyright (c) 2019, Josh Teeter` 39 | 40 | - Graphite Helpers https://github.com/GrafiteInc/Helpers `Copyright (c) 2020, Matt Lantz` 41 | -------------------------------------------------------------------------------- /tests/unit/escape.rs: -------------------------------------------------------------------------------- 1 | //! voca_rs::escape testing 2 | use voca_rs::Voca; 3 | 4 | #[test] 5 | fn escape_html() { 6 | assert_eq!(voca_rs::escape::escape_html(""), ""); 7 | assert_eq!( 8 | voca_rs::escape::escape_html("<>&\"'`"), 9 | "<>&"'`" 10 | ); 11 | assert_eq!( 12 | voca_rs::escape::escape_html(voca_rs::utils::PUNCTUATION), 13 | "!"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~" 14 | ); 15 | assert_eq!( 16 | voca_rs::escape::escape_html("

wonderful world

"), 17 | "<p>wonderful world</p>" 18 | ); 19 | assert_eq!(voca_rs::escape::escape_html(""), "<span>"); 20 | assert_eq!( 21 | voca_rs::escape::escape_html("

wonderfulworld

"), 22 | "<p>wonderful<span>world<span/></p>" 23 | ); 24 | } 25 | #[test] 26 | fn _escape_html() { 27 | assert_eq!("<>&\"'`"._escape_html(), "<>&"'`"); 28 | } 29 | #[test] 30 | fn escape_regexp() { 31 | assert_eq!(voca_rs::escape::escape_regexp(""), ""); 32 | assert_eq!( 33 | voca_rs::escape::escape_regexp("(hours)[minutes]{seconds}"), 34 | "\\(hours\\)\\[minutes\\]\\{seconds\\}" 35 | ); 36 | assert_eq!( 37 | voca_rs::escape::escape_regexp("-[]/{}()*+?.\\^$|"), 38 | "\\-\\[\\]\\/\\{\\}\\(\\)\\*\\+\\?\\.\\\\\\^\\$\\|" 39 | ); 40 | } 41 | #[test] 42 | fn _escape_regexp() { 43 | assert_eq!( 44 | "(hours)[minutes]{seconds}"._escape_regexp(), 45 | "\\(hours\\)\\[minutes\\]\\{seconds\\}" 46 | ); 47 | } 48 | #[test] 49 | fn unescape_html() { 50 | assert_eq!(voca_rs::escape::escape_html(""), ""); 51 | assert_eq!( 52 | voca_rs::escape::unescape_html("<>&"'`"), 53 | "<>&\"'`" 54 | ); 55 | assert_eq!( 56 | voca_rs::escape::unescape_html( 57 | "!"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~" 58 | ), 59 | voca_rs::utils::PUNCTUATION 60 | ); 61 | assert_eq!( 62 | voca_rs::escape::unescape_html("<p>wonderful world</p>"), 63 | "

wonderful world

" 64 | ); 65 | assert_eq!(voca_rs::escape::unescape_html("<span>"), ""); 66 | assert_eq!( 67 | voca_rs::escape::unescape_html( 68 | "<p>wonderful<span>world<span/></p>" 69 | ), 70 | "

wonderfulworld

" 71 | ); 72 | } 73 | #[test] 74 | fn _unescape_html() { 75 | assert_eq!( 76 | "<>&"'`"._unescape_html(), 77 | "<>&\"'`" 78 | ); 79 | } 80 | -------------------------------------------------------------------------------- /tests/unit/readme.rs: -------------------------------------------------------------------------------- 1 | // Readme's example testing 2 | 3 | #[test] 4 | fn example_functions() { 5 | let input_string = "LazyLoad with XMLHttpRequest and snake_case"; 6 | let string_in_words = voca_rs::split::words(&input_string); 7 | // => ["Lazy", "Load", "with", "XML", "Http", "Request", "and", "snake", "case"] 8 | assert_eq!( 9 | string_in_words, 10 | ["Lazy", "Load", "with", "XML", "Http", "Request", "and", "snake", "case"] 11 | ); 12 | let words_in_string = &string_in_words.join(" "); 13 | // => "Lazy Load with XML Http Request and snake case" 14 | assert_eq!( 15 | words_in_string, 16 | "Lazy Load with XML Http Request and snake case" 17 | ); 18 | let truncated_string = voca_rs::chop::prune(&words_in_string, 21, ""); 19 | // => "Lazy Load with XML..." 20 | assert_eq!(truncated_string, "Lazy Load with XML..."); 21 | let sliced_string = voca_rs::chop::slice(&truncated_string, 5, -2); 22 | // => "Load with XML." 23 | assert_eq!(sliced_string, "Load with XML."); 24 | let snaked_string = voca_rs::case::snake_case(&sliced_string); 25 | // => "load_with_xml" 26 | assert_eq!(snaked_string, "load_with_xml"); 27 | } 28 | 29 | #[test] 30 | fn example_traits() { 31 | use voca_rs::Voca; 32 | 33 | // Test #1 34 | let input_string1 = "LazyLoad with XMLHttpRequest and snake_case"; 35 | let expected_result1 = [ 36 | "Lazy", "Load", "with", "XML", "Http", "Request", "and", "snake", "case", 37 | ]; 38 | assert_eq!(input_string1._words(), expected_result1); 39 | 40 | // Test #2 41 | let input_string2 = "LazyLoad with XMLHttpRequest and snake_case"._words(); 42 | let expected_result2 = "Lazy Load with XML Http Request and snake case"; 43 | assert_eq!(input_string2.join(" "), expected_result2); 44 | 45 | // Test #3 46 | let input_string3 = "LazyLoad with XMLHttpRequest and snake_case" 47 | ._words() 48 | .join(" "); 49 | let expected_string3 = "Lazy Load with XML..."; 50 | assert_eq!(input_string3._prune(21, ""), expected_string3); 51 | 52 | // Test #4 53 | let input_string4 = "LazyLoad with XMLHttpRequest and snake_case" 54 | ._words() 55 | .join(" ") 56 | ._prune(21, ""); 57 | let expected_string4 = "Load with XML."; 58 | assert_eq!(input_string4._slice(5, -2), expected_string4); 59 | 60 | // Test #5 61 | let input_string5 = "LazyLoad with XMLHttpRequest and snake_case" 62 | ._words() 63 | .join(" ") 64 | ._prune(21, "") 65 | ._slice(5, -2); 66 | let expected_string5 = "load_with_xml"; 67 | assert_eq!(input_string5._snake_case(), expected_string5); 68 | 69 | // Test #6 70 | let input_string6 = "LazyLoad with XMLHttpRequest and snake_case" 71 | ._words() 72 | .join(" ") 73 | ._prune(21, "") 74 | ._slice(5, -2) 75 | ._snake_case(); 76 | let expected_string6 = "load_with_xml"; 77 | assert_eq!(input_string6, expected_string6); 78 | } 79 | -------------------------------------------------------------------------------- /src/escape.rs: -------------------------------------------------------------------------------- 1 | //! Escapes special characters in `subject`. 2 | 3 | /// Escapes HTML special characters < > & ' " ` in `subject`. 4 | /// 5 | /// # Arguments 6 | /// 7 | /// * `subject` - The string to escape. 8 | /// 9 | /// # Example 10 | /// 11 | /// ``` 12 | /// use voca_rs::*; 13 | /// escape::escape_html("

wonderful world

"); 14 | /// // => <p>wonderful world</p> 15 | /// use voca_rs::Voca; 16 | /// "

wonderful world

"._escape_html(); 17 | /// // => <p>wonderful world</p> 18 | /// ``` 19 | // TODO: check for optimizations #10 20 | // https://lise-henry.github.io/articles/optimising_strings.html 21 | pub fn escape_html(subject: &str) -> String { 22 | match subject.len() { 23 | 0 => "".to_string(), 24 | _ => subject 25 | .replace('&', "&") 26 | .replace('<', "<") 27 | .replace('>', ">") 28 | .replace('"', """) 29 | .replace('\'', "'") 30 | .replace('`', "`"), 31 | } 32 | } 33 | 34 | /// Escapes the regular expression special characters - [ ] / { } ( ) * + ? . \ ^ $ | in `subject`. 35 | /// 36 | /// # Arguments 37 | /// 38 | /// * `subject` - The string to escape. 39 | /// 40 | /// # Example 41 | /// 42 | /// ``` 43 | /// use voca_rs::*; 44 | /// escape::escape_regexp("(hours)[minutes]{seconds}"); 45 | /// // => \(hours\)\[minutes\]\{seconds\} 46 | /// use voca_rs::Voca; 47 | /// "(hours)[minutes]{seconds}"._escape_regexp(); 48 | /// // => \(hours\)\[minutes\]\{seconds\} 49 | /// ``` 50 | pub fn escape_regexp(subject: &str) -> String { 51 | let key = "-[]/{}()*+?.\\^$|"; 52 | match subject.len() { 53 | 0 => "".to_string(), 54 | _ => { 55 | let mut res = String::new(); 56 | for c in crate::split::chars(subject) { 57 | let push_char = if key.contains(c) { 58 | format!("\\{}", c) 59 | } else { 60 | c.to_string() 61 | }; 62 | res.push_str(&push_char); 63 | } 64 | res 65 | } 66 | } 67 | } 68 | 69 | /// Unescapes HTML special characters from < > & " ' ` to corresponding < > & " ' ` in `subject`. 70 | /// 71 | /// # Arguments 72 | /// 73 | /// * `subject` - The string to unescape. 74 | /// 75 | /// # Example 76 | /// 77 | /// ``` 78 | /// use voca_rs::*; 79 | /// escape::unescape_html("<p>wonderful world</p>"); 80 | /// // =>

wonderful world

81 | /// use voca_rs::Voca; 82 | /// "<p>wonderful world</p>"._unescape_html(); 83 | /// // =>

wonderful world

84 | /// ``` 85 | pub fn unescape_html(subject: &str) -> String { 86 | match subject.len() { 87 | 0 => "".to_string(), 88 | _ => subject 89 | .replace("<", "<") 90 | .replace(">", ">") 91 | .replace("&", "&") 92 | .replace(""", "\"") 93 | .replace("'", "'") 94 | .replace("`", "`"), 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /tests/unit/split.rs: -------------------------------------------------------------------------------- 1 | //! voca_rs::split testing 2 | use voca_rs::Voca; 3 | 4 | #[test] 5 | fn to_chars() { 6 | assert_eq!( 7 | voca_rs::split::chars("gravity"), 8 | ["g", "r", "a", "v", "i", "t", "y"] 9 | ); 10 | assert_eq!(voca_rs::split::chars(" "), [" ", " "]); 11 | assert_eq!(voca_rs::split::chars("a b"), ["a", " ", "b"]); 12 | assert_eq!(voca_rs::split::chars("ÜbER"), ["Ü", "b", "E", "R"]); 13 | assert_eq!(voca_rs::split::chars("\n\t"), ["\n", "\t"]); 14 | assert_eq!(voca_rs::split::chars(""), [""]); 15 | } 16 | #[test] 17 | fn _to_chars() { 18 | assert_eq!("gravity"._chars(), ["g", "r", "a", "v", "i", "t", "y"]); 19 | } 20 | #[test] 21 | fn by_pattern() { 22 | assert_eq!( 23 | voca_rs::split::split("gravity can cross dimensions", " "), 24 | ["gravity", "can", "cross", "dimensions"] 25 | ); 26 | assert_eq!( 27 | voca_rs::split::split("*dying*star*", "*"), 28 | ["", "dying", "star"] 29 | ); 30 | assert_eq!(voca_rs::split::split("dying star", ""), ["dying star"]); 31 | assert_eq!(voca_rs::split::split("Über Stern", ""), ["Über Stern"]); 32 | assert_eq!(voca_rs::split::split("", ""), [""]); 33 | } 34 | #[test] 35 | fn _by_pattern() { 36 | assert_eq!( 37 | "gravity can cross dimensions"._split(" "), 38 | ["gravity", "can", "cross", "dimensions"] 39 | ); 40 | } 41 | #[test] 42 | fn words() { 43 | assert_eq!( 44 | voca_rs::split::words("gravity can cross dimensions"), 45 | ["gravity", "can", "cross", "dimensions"] 46 | ); 47 | assert_eq!( 48 | voca_rs::split::words("gravity dying\r\nstar\tfalling"), 49 | ["gravity", "dying", "star", "falling"] 50 | ); 51 | assert_eq!( 52 | voca_rs::split::words("Zażółć gęślą jaźń"), 53 | ["Zażółć", "gęślą", "jaźń"] 54 | ); 55 | assert_eq!( 56 | voca_rs::split::words("splitCamelCase"), 57 | ["split", "Camel", "Case"] 58 | ); 59 | assert_eq!( 60 | voca_rs::split::words("split-some kind_of_mixed CaseHere"), 61 | ["split", "some", "kind", "of", "mixed", "Case", "Here"] 62 | ); 63 | assert_eq!( 64 | voca_rs::split::words("LazyLoad with XMLHttpRequest and snake_case"), 65 | ["Lazy", "Load", "with", "XML", "Http", "Request", "and", "snake", "case"] 66 | ); 67 | } 68 | #[test] 69 | fn _words() { 70 | assert_eq!( 71 | "gravity can cross dimensions"._words(), 72 | ["gravity", "can", "cross", "dimensions"] 73 | ); 74 | } 75 | #[test] 76 | fn to_graphemes() { 77 | assert_eq!( 78 | voca_rs::split::graphemes("a̐éö̲\r\n"), 79 | ["a̐", "é", "ö̲", "\r\n"] 80 | ); 81 | assert_eq!(voca_rs::split::graphemes(""), [""]); 82 | } 83 | #[test] 84 | fn _to_graphemes() { 85 | assert_eq!("a̐éö̲\r\n"._graphemes(), ["a̐", "é", "ö̲", "\r\n"]); 86 | } 87 | #[test] 88 | fn code_points() { 89 | assert_eq!(voca_rs::split::code_points(""), []); 90 | assert_eq!(voca_rs::split::code_points("rain"), [114, 97, 105, 110]); 91 | assert_eq!( 92 | voca_rs::split::code_points("Un garçon de café"), 93 | [85, 110, 32, 103, 97, 114, 231, 111, 110, 32, 100, 101, 32, 99, 97, 102, 233] 94 | ); 95 | assert_eq!( 96 | voca_rs::split::code_points("a̐éö̲\r\n"), 97 | [97, 784, 101, 769, 111, 776, 818, 13, 10] 98 | ); 99 | } 100 | #[test] 101 | fn _code_points() { 102 | assert_eq!("rain"._code_points(), [114, 97, 105, 110]); 103 | } 104 | -------------------------------------------------------------------------------- /tests/unit/utils.rs: -------------------------------------------------------------------------------- 1 | //! voca_rs::utils testing 2 | 3 | #[test] 4 | fn version() { 5 | assert_eq!(voca_rs::utils::VERSION, "1.15.2"); 6 | } 7 | #[test] 8 | fn ascii_letters() { 9 | assert_eq!( 10 | voca_rs::utils::ASCII_LETTERS, 11 | "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 12 | ); 13 | } 14 | #[test] 15 | fn ascii_lowercase() { 16 | assert_eq!( 17 | voca_rs::utils::ASCII_LOWERCASE, 18 | "abcdefghijklmnopqrstuvwxyz" 19 | ); 20 | } 21 | #[test] 22 | fn ascii_uppercase() { 23 | assert_eq!( 24 | voca_rs::utils::ASCII_UPPERCASE, 25 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 26 | ); 27 | } 28 | #[test] 29 | fn digits() { 30 | assert_eq!(voca_rs::utils::DIGITS, "0123456789"); 31 | } 32 | #[test] 33 | fn hexdigits() { 34 | assert_eq!(voca_rs::utils::HEXDIGITS, "0123456789abcdefABCDEF"); 35 | } 36 | #[test] 37 | fn octdigits() { 38 | assert_eq!(voca_rs::utils::OCTDIGITS, "01234567"); 39 | } 40 | #[test] 41 | fn punctuation() { 42 | assert_eq!( 43 | voca_rs::utils::PUNCTUATION, 44 | "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~" 45 | ); 46 | } 47 | #[test] 48 | fn printable() { 49 | assert_eq!(voca_rs::utils::PRINTABLE, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r"); 50 | } 51 | #[test] 52 | fn whitespace() { 53 | assert_eq!(voca_rs::utils::WHITESPACE, " \t\n\r"); 54 | } 55 | 56 | // https://github.com/chowdhurya/rust-unidecode/blob/master/tests/unidecode.rs 57 | // Tests that every character outputted by the unidecode() function is valid 58 | // ASCII. 59 | #[test] 60 | fn test_all_ascii() { 61 | use std::char; 62 | 63 | let valid_unicode = (0x0..0xD7FF + 1).chain(0x0E000..0x10FFFF + 1); 64 | for i in valid_unicode { 65 | match char::from_u32(i) { 66 | Some(ch) => { 67 | for ascii_ch in voca_rs::utils::unidecode(&ch.to_string()).chars() { 68 | let x = ascii_ch as u32; 69 | if x > 127 { 70 | panic!("Data contains non-ASCII character (Dec: {})", x); 71 | } 72 | } 73 | } 74 | None => panic!("Test written incorrectly; invalid Unicode"), 75 | } 76 | } 77 | } 78 | 79 | // https://github.com/chowdhurya/rust-unidecode/blob/master/tests/unidecode.rs 80 | // These tests were ported directly from the original `Text::Unidecode` Perl 81 | // module. 82 | #[test] 83 | fn test_conversion() { 84 | assert_eq!(voca_rs::utils::unidecode("Æneid"), "AEneid"); 85 | assert_eq!(voca_rs::utils::unidecode("étude"), "etude"); 86 | assert_eq!(voca_rs::utils::unidecode("北亰"), "Bei Jing "); 87 | assert_eq!(voca_rs::utils::unidecode("ᔕᓇᓇ"), "shanana"); 88 | assert_eq!(voca_rs::utils::unidecode("ᏔᎵᏆ"), "taliqua"); 89 | assert_eq!(voca_rs::utils::unidecode("ܦܛܽܐܺ"), "ptu'i"); 90 | assert_eq!(voca_rs::utils::unidecode("अभिजीत"), "abhijiit"); 91 | assert_eq!(voca_rs::utils::unidecode("অভিজীত"), "abhijiit"); 92 | assert_eq!(voca_rs::utils::unidecode("അഭിജീത"), "abhijiit"); 93 | assert_eq!(voca_rs::utils::unidecode("മലയാലമ്"), "mlyaalm"); 94 | assert_eq!(voca_rs::utils::unidecode("げんまい茶"), "genmaiCha "); 95 | } 96 | 97 | // https://github.com/chowdhurya/rust-unidecode/blob/master/tests/unidecode.rs 98 | #[test] 99 | fn test_unidecode_char() { 100 | assert_eq!(voca_rs::utils::unidecode_char('Æ'), "AE"); 101 | assert_eq!(voca_rs::utils::unidecode_char('北'), "Bei "); 102 | assert_eq!(voca_rs::utils::unidecode_char('亰'), "Jing "); 103 | assert_eq!(voca_rs::utils::unidecode_char('ᔕ'), "sha"); 104 | } 105 | -------------------------------------------------------------------------------- /tests/unit/index.rs: -------------------------------------------------------------------------------- 1 | //! voca_rs::index testing 2 | use voca_rs::Voca; 3 | 4 | #[test] 5 | fn index_of() { 6 | assert_eq!(voca_rs::index::index_of("", "", 0), 0); 7 | assert_eq!(voca_rs::index::index_of("rain", "r", 0), 0); 8 | assert_eq!(voca_rs::index::index_of("rain", "a", 0), 1); 9 | assert_eq!(voca_rs::index::index_of("rain", "n", 3), 0); 10 | assert_eq!(voca_rs::index::index_of("rain", "a", 10), -1); 11 | assert_eq!(voca_rs::index::index_of("Rain, dear rain", "ear", 0), 7); 12 | assert_eq!(voca_rs::index::index_of("Rain, dear rain", "ain", 0), 1); 13 | assert_eq!(voca_rs::index::index_of("rain", "z", 0), -1); 14 | assert_eq!(voca_rs::index::index_of("b\u{0142}\u{0105}d", "ą", 0), 2); 15 | assert_eq!(voca_rs::index::index_of("Zażółć gęślą jaźń", "gęślą", 0), 7); 16 | assert_eq!( 17 | voca_rs::index::index_of("Die Schildkröte fliegt über das Floß.", "Schildkröte", 4), 18 | 0 19 | ); 20 | assert_eq!( 21 | voca_rs::index::index_of("Как слышно, приём!", "слышно", 0), 22 | 4 23 | ); 24 | } 25 | #[test] 26 | fn _index_of() { 27 | assert_eq!("Rain, dear rain"._index_of("ear", 0), 7); 28 | } 29 | #[test] 30 | fn index_all() { 31 | assert_eq!(voca_rs::index::index_all("", "", 0), []); 32 | assert_eq!(voca_rs::index::index_all("rain", "r", 0), [0]); 33 | assert_eq!(voca_rs::index::index_all("rain", "a", 0), [1]); 34 | assert_eq!(voca_rs::index::index_all("rain", "n", 3), [0]); 35 | assert_eq!(voca_rs::index::index_all("rain", "a", 10), []); 36 | assert_eq!( 37 | voca_rs::index::index_all("Rain, dear rain", "ear", 0), 38 | [1, 7, 8, 9, 11, 12] 39 | ); 40 | assert_eq!( 41 | voca_rs::index::index_all("Rain, dear rain", "ain", 0), 42 | [1, 2, 3, 8, 12, 13, 14] 43 | ); 44 | assert_eq!(voca_rs::index::index_all("rain", "z", 0), []); 45 | assert_eq!(voca_rs::index::index_all("b\u{0142}\u{0105}d", "ą", 0), [2]); 46 | assert_eq!( 47 | voca_rs::index::index_all("Zażółć gęślą jaźń", "aęą", 0), 48 | [1, 8, 11, 14] 49 | ); 50 | assert_eq!( 51 | voca_rs::index::index_all("Die Schildkröte fliegt über das Floß.", "iöß", 4), 52 | [3, 8, 14, 31] 53 | ); 54 | assert_eq!( 55 | voca_rs::index::index_all("Как меня слышно, приём!", "н", 0), 56 | [6, 13] 57 | ); 58 | } 59 | #[test] 60 | fn _index_all() { 61 | assert_eq!("Rain, dear rain"._index_all("ear", 0), [1, 7, 8, 9, 11, 12]); 62 | } 63 | #[test] 64 | fn last_index_of() { 65 | assert_eq!(voca_rs::index::last_index_of("", "", 0), 0); 66 | assert_eq!(voca_rs::index::last_index_of("rain", "r", 0), 0); 67 | assert_eq!(voca_rs::index::last_index_of("rain", "a", 0), 1); 68 | assert_eq!(voca_rs::index::last_index_of("rain", "n", 3), 0); 69 | assert_eq!(voca_rs::index::last_index_of("rain", "a", 10), -1); 70 | assert_eq!( 71 | voca_rs::index::last_index_of("Rain, dear rain", "rain", 0), 72 | 11 73 | ); 74 | assert_eq!( 75 | voca_rs::index::last_index_of("Rain, dear rain", "ain", 0), 76 | 12 77 | ); 78 | assert_eq!(voca_rs::index::last_index_of("rain", "z", 0), -1); 79 | assert_eq!( 80 | voca_rs::index::last_index_of("b\u{0142}\u{0105}d", "ą", 0), 81 | 2 82 | ); 83 | assert_eq!( 84 | voca_rs::index::last_index_of("Zażółć gęślą jaźń", "gęślą", 0), 85 | 7 86 | ); 87 | assert_eq!( 88 | voca_rs::index::last_index_of("Die Schildkröte fliegt über das Floß.", "Schildkröte", 4), 89 | 0 90 | ); 91 | assert_eq!( 92 | voca_rs::index::last_index_of("Как слышно, приём!", "слышно", 0), 93 | 4 94 | ); 95 | } 96 | #[test] 97 | fn _last_index_of() { 98 | assert_eq!("Rain, dear rain"._last_index_of("rain", 0), 11); 99 | } 100 | #[test] 101 | fn search() { 102 | assert_eq!(voca_rs::index::search("", "", 0), 0); 103 | assert_eq!(voca_rs::index::search("morning", "rn", 0), 2); 104 | assert_eq!(voca_rs::index::search("evening", r"\d", 0), -1); 105 | assert_eq!( 106 | voca_rs::index::search("we have a mission", "mission", 0), 107 | 10 108 | ); 109 | assert_eq!(voca_rs::index::search("we have a mission", "a", 0), 4); 110 | assert_eq!(voca_rs::index::search("we have a mission", r"\s", 0), 2); 111 | assert_eq!(voca_rs::index::search("we have a mission", "", 0), 0); 112 | assert_eq!(voca_rs::index::search("we have a mission", "a", 6), 8); 113 | assert_eq!(voca_rs::index::search("we have a mission", "12", 0), -1); 114 | assert_eq!(voca_rs::index::search("we have a mission", r"\s^", 0), -1); 115 | assert_eq!(voca_rs::index::search("we have a mission", "we", 3), -1); 116 | assert_eq!( 117 | voca_rs::index::search("we have a mission", "mission", 100), 118 | -1 119 | ); 120 | assert_eq!(voca_rs::index::search("Zażółć gęślą jaźń", "gęślą", 6), 11); 121 | assert_eq!( 122 | voca_rs::index::search("Die Schildkröte fliegt über das Floß.", "Schildkröte", 4), 123 | 4 124 | ); 125 | assert_eq!(voca_rs::index::search("Как слышно, приём!", "слышно", 0), 7); 126 | assert_eq!( 127 | // [^] is not a valid regex 128 | voca_rs::index::search("Zażółć gęślą jaźń", "[^]", 0), 129 | -1 130 | ); 131 | } 132 | #[test] 133 | fn _search() { 134 | assert_eq!("we have a mission"._search("mission", 0), 10); 135 | } 136 | -------------------------------------------------------------------------------- /src/index.rs: -------------------------------------------------------------------------------- 1 | //! Returns the index of `search` in `subject`. 2 | 3 | use regex::Regex; 4 | /// Returns an array of all occurrence index of `search` in `subject` or an empty array if not found. Case sensitive. 5 | /// 6 | /// # Arguments 7 | /// 8 | /// * `subject` - The string where to search. 9 | /// * `search` - The string to search. 10 | /// * `from_index` - The index to start searching 11 | /// 12 | /// # Example 13 | /// ``` 14 | /// use voca_rs::*; 15 | /// index::index_all("morning", "n", 0); 16 | /// // => [3, 5] 17 | /// index::index_all("Zażółć gęślą jaźń", "aęą", 0); 18 | /// // => [1, 8, 11, 14] 19 | /// index::index_all("evening", "o", 0); 20 | /// // => [] 21 | /// use voca_rs::Voca; 22 | /// "morning"._index_all("n", 0); 23 | /// // => [3, 5] 24 | /// ``` 25 | pub fn index_all(subject: &str, search: &str, from_index: usize) -> Vec { 26 | if subject.is_empty() || crate::count::count(subject) < from_index { 27 | return vec![]; 28 | } 29 | let string_slice = &subject[subject.char_indices().nth(from_index).unwrap().0..]; 30 | let mut res = Vec::new(); 31 | for (i, c) in crate::split::chars(string_slice).iter().enumerate() { 32 | if search.contains(c) { 33 | res.push(i) 34 | } 35 | } 36 | res 37 | } 38 | 39 | /// Returns the first occurrence index of `search` in `subject` or -1 if not found. Case sensitive. 40 | /// 41 | /// # Arguments 42 | /// 43 | /// * `subject` - The string where to search. 44 | /// * `search` - The string to search. 45 | /// * `from_index` - The index to start searching 46 | /// 47 | /// # Example 48 | /// ``` 49 | /// use voca_rs::*; 50 | /// index::index_of("morning", "n", 0); 51 | /// // => 3 52 | /// index::index_of("Zażółć gęślą jaźń", "gęślą", 0); 53 | /// // => 7 54 | /// index::index_of("evening", "o", 0); 55 | /// // => -1 56 | /// use voca_rs::Voca; 57 | /// "morning"._index_of("n", 0); 58 | /// // => 3 59 | /// ``` 60 | pub fn index_of(subject: &str, search: &str, from_index: usize) -> i8 { 61 | match search.len() { 62 | 0 => 0, 63 | _ => { 64 | if crate::count::count(subject) < from_index { 65 | return -1; 66 | } 67 | let string_slice = &subject[subject.char_indices().nth(from_index).unwrap().0..]; 68 | match crate::split::chars(string_slice) 69 | .iter() 70 | .enumerate() 71 | .position(|(pos, _)| { 72 | match &string_slice[string_slice.char_indices().nth(pos).unwrap().0..] 73 | .find(search) 74 | { 75 | Some(x) => *x == 0, 76 | None => false, 77 | } 78 | }) { 79 | Some(x) => x as i8, 80 | None => -1, 81 | } 82 | } 83 | } 84 | } 85 | 86 | /// Returns the last occurrence index of `search` in `subject` or -1 if not found. Case sensitive. 87 | /// 88 | /// # Arguments 89 | /// 90 | /// * `subject` - The string where to search. 91 | /// * `search` - The string to search. 92 | /// * `from_index` - The index to start searching 93 | /// 94 | /// # Example 95 | /// ``` 96 | /// use voca_rs::*; 97 | /// index::last_index_of("morning", "n", 0); 98 | /// // => 5 99 | /// index::last_index_of("evening", "o", 0); 100 | /// // => -1 101 | /// use voca_rs::Voca; 102 | /// "morning"._last_index_of("n", 0); 103 | /// // => 5 104 | /// ``` 105 | pub fn last_index_of(subject: &str, search: &str, from_index: usize) -> i8 { 106 | match search.len() { 107 | 0 => 0, 108 | _ => { 109 | if crate::count::count(subject) < from_index { 110 | return -1; 111 | } 112 | let string_slice = &subject[subject.char_indices().nth(from_index).unwrap().0..]; 113 | let string_chars = crate::split::chars(string_slice); 114 | match string_chars.iter().enumerate().rev().position(|(pos, _)| { 115 | match &string_slice[string_slice.char_indices().nth(pos).unwrap().0..].find(search) 116 | { 117 | Some(x) => *x == 0, 118 | None => false, 119 | } 120 | }) { 121 | Some(x) => (string_chars.len() - x - 1) as i8, 122 | None => -1, 123 | } 124 | } 125 | } 126 | } 127 | 128 | /// Returns the first index of a `pattern` match in `subject`. 129 | /// NOTE: Executes regular expressions only on valid UTF-8 while exposing match locations as byte indices into the search string (see case #3). 130 | /// 131 | /// # Arguments 132 | /// 133 | /// * `subject` - The string where to search. 134 | /// * `pattern` - The RegExp pattern to search, it is transformed to Regex::new(pattern). 135 | /// * `from_index` - The index to start searching. 136 | /// 137 | /// # Example 138 | /// ``` 139 | /// use voca_rs::*; 140 | /// index::search("morning", "rn", 0); 141 | /// // => 2 142 | /// index::search("evening", r"\d", 0); 143 | /// // => -1 144 | /// index::search("Zażółć gęślą jaźń", "gęślą", 6); 145 | /// // => 11 (substring's position in `subject`), not 7 146 | /// use voca_rs::Voca; 147 | /// "morning"._search("rn", 0); 148 | /// // => 2 149 | /// ``` 150 | pub fn search(subject: &str, pattern: &str, from_index: usize) -> i8 { 151 | if from_index >= crate::split::chars(subject).len() { 152 | return -1; 153 | } 154 | match pattern.len() { 155 | 0 => 0, 156 | _ => { 157 | let re: Regex = match Regex::new(pattern) { 158 | Ok(re) => re, 159 | Err(_) => return -1, 160 | }; 161 | match re.find_at(subject, from_index) { 162 | None => -1, 163 | Some(x) => x.start() as i8, 164 | } 165 | } 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/count.rs: -------------------------------------------------------------------------------- 1 | //! Counts the characters in `subject`. 2 | 3 | /// Counts the characters in `subject`. 4 | /// 5 | /// # Arguments 6 | /// 7 | /// * `subject` - The string to count characters. 8 | /// 9 | /// # Example 10 | /// 11 | /// ``` 12 | /// use voca_rs::*; 13 | /// count::count("rain"); 14 | /// // => 4 15 | /// count::count("błąd"); 16 | /// // => 4 17 | /// use voca_rs::Voca; 18 | /// "rain"._count(); 19 | /// // => 4 20 | /// ``` 21 | pub fn count(subject: &str) -> usize { 22 | match subject.len() { 23 | 0 => 0, 24 | _ => crate::split::chars(subject).len(), 25 | } 26 | } 27 | 28 | /// Counts the graphemes in `subject` taking care of surrogate pairs and combining marks. 29 | /// 30 | /// # Arguments 31 | /// 32 | /// * `subject` - The string to count graphemes. 33 | /// 34 | /// # Example 35 | /// 36 | /// ``` 37 | /// use voca_rs::*; 38 | /// count::count_graphemes("cafe\u{0301}"); // or "café" 39 | /// // => 4 40 | /// count::count_graphemes("b\u{0142}\u{0105}d"); // or "błąd" 41 | /// // => 4 42 | /// count::count_graphemes("a̐éö̲"); 43 | /// // => 3 44 | /// count::count_graphemes("rain"); 45 | /// // => 4 46 | /// use voca_rs::Voca; 47 | /// "cafe\u{0301}"._count_graphemes(); // or "café" 48 | /// // => 4 49 | /// ``` 50 | pub fn count_graphemes(subject: &str) -> usize { 51 | match subject.len() { 52 | 0 => 0, 53 | _ => crate::split::graphemes(subject).len(), 54 | } 55 | } 56 | 57 | /// Counts the number of `substring` appearances in `subject`. 58 | /// 59 | /// # Arguments 60 | /// 61 | /// * `subject` - The string where to count. 62 | /// * `substring` - The substring to be counted. 63 | /// 64 | /// # Example 65 | /// 66 | /// ``` 67 | /// use voca_rs::*; 68 | /// count::count_substrings("bad boys, bad boys whatcha gonna do?", "boys"); 69 | /// // => 2 70 | /// count::count_substrings("Cafe\u{0301} del Mar", "Café"); // or "Café del Mar" 71 | /// // => 1 72 | /// count::count_substrings("every dog has its day", "cat"); 73 | /// // => 0 74 | /// use voca_rs::Voca; 75 | /// "bad boys, bad boys whatcha gonna do?"._count_substrings("boys"); 76 | /// // => 2 77 | /// ``` 78 | pub fn count_substrings(subject: &str, substring: &str) -> usize { 79 | fn match_substring(subject: &str, substring: &str) -> usize { 80 | match substring.len() { 81 | 0 => 0, 82 | _ => subject.matches(substring).count(), 83 | } 84 | } 85 | 86 | match subject.len() { 87 | 0 => 0, 88 | _ => match_substring(subject, substring), 89 | } 90 | } 91 | 92 | /// Counts the characters in `subject` for which `predicate` returns true. This function respects unicode. 93 | /// 94 | /// # Arguments 95 | /// 96 | /// * `subject` - The string to count characters. 97 | /// * `predicate` - The predicate function invoked on each character with a parameter `(string)`. 98 | /// 99 | /// # Example 100 | /// 101 | /// ``` 102 | /// use voca_rs::*; 103 | /// count::count_where("hola!", voca_rs::query::is_alpha); 104 | /// // => 4 105 | /// count::count_where("2022", |s: &str| -> bool { s == "2" }); 106 | /// // => 3 107 | /// use voca_rs::Voca; 108 | /// "hola!"._count_where(voca_rs::query::is_alpha); 109 | /// // => 4 110 | /// ``` 111 | pub fn count_where(subject: &str, f: fn(&str) -> bool) -> usize { 112 | match subject.len() { 113 | 0 => 0, 114 | _ => { 115 | let mut res = 0; 116 | for c in crate::split::graphemes(subject).iter() { 117 | if f(c) { 118 | res += 1; 119 | } 120 | } 121 | res 122 | } 123 | } 124 | } 125 | 126 | /// Counts the number of words in `subject`. 127 | /// 128 | /// # Arguments 129 | /// 130 | /// * `subject` - The string where to count. 131 | /// * `pattern` - The pattern to watch words. 132 | /// 133 | /// # Example 134 | /// 135 | /// ``` 136 | /// use voca_rs::*; 137 | /// count::count_words("Gravity - can cross dimensions!", ""); 138 | /// // => 4 139 | /// count::count_words("GravityCanCrossDimensions", ""); 140 | /// // => 4 141 | /// count::count_words("Cafe\u{0301}-del-Mar-andBossaNova1", "-"); 142 | /// // => 4 143 | /// use voca_rs::Voca; 144 | /// "Gravity - can cross dimensions!"._count_words(""); 145 | /// // => 4 146 | /// ``` 147 | pub fn count_words(subject: &str, pattern: &str) -> usize { 148 | fn match_substring(subject: &str, pattern: &str) -> usize { 149 | match pattern.len() { 150 | 0 => crate::split::words(subject).len(), 151 | _ => subject 152 | .split_terminator(pattern) 153 | .count(), 154 | } 155 | } 156 | match subject.len() { 157 | 0 => 0, 158 | _ => match_substring(subject, pattern), 159 | } 160 | } 161 | 162 | use std::collections::HashMap; 163 | /// Counting occurrences of unique words in `subject`. This function respects unicode. 164 | /// 165 | /// # Arguments 166 | /// 167 | /// * `subject` - The string to count characters. 168 | /// * `pattern` - The pattern to watch words. 169 | /// 170 | /// # Example 171 | /// 172 | /// ``` 173 | /// use voca_rs::*; 174 | /// count::count_unique_words("hello world wonderful world", ""); 175 | /// // => 3 176 | /// count::count_unique_words("Arabic: أنا قادر على أكل الزجاج و هذا لا يؤلمني. أنا قادر على أكل الزجاج و.", ""); 177 | /// // => ??????????????????????????????????? 178 | /// use voca_rs::Voca; 179 | /// "Hebrew: אני יכול לאכול זכוכית וזה לא מזיק לי. אני יכול לאכול זכוכית."._count_words(""); 180 | /// // => 9 181 | /// ``` 182 | pub fn count_unique_words(subject: &str, pattern: &str) -> usize { 183 | let mut unique_words = HashMap::new(); 184 | let words = match pattern.len() { 185 | 0 => crate::split::words(subject), 186 | _ => subject.split_terminator(pattern).collect::>(), 187 | }; 188 | if words.is_empty() { 189 | return 0; 190 | }; 191 | 192 | for word in words { 193 | unique_words.entry(word).or_insert(0); 194 | } 195 | unique_words.len() 196 | } 197 | -------------------------------------------------------------------------------- /src/split.rs: -------------------------------------------------------------------------------- 1 | //! Splits `subject` into an chuncks according to given rules. 2 | 3 | use stfu8; 4 | use unicode_segmentation::UnicodeSegmentation; 5 | /// Splits `subject` into an array of characters. 6 | /// 7 | /// # Arguments 8 | /// 9 | /// * `subject` - The string to split into characters. 10 | /// 11 | /// # Example 12 | /// 13 | /// ``` 14 | /// use voca_rs::*; 15 | /// split::chars("cloud"); 16 | /// // => ["c", "l", "o", "u", "d"] 17 | /// use voca_rs::Voca; 18 | /// "cloud"._chars(); 19 | /// // => ["c", "l", "o", "u", "d"] 20 | /// ``` 21 | pub fn chars(subject: &str) -> Vec<&str> { 22 | if subject.is_empty() { 23 | return vec![""]; 24 | } 25 | subject.split_terminator("").skip(1).collect::>() 26 | } 27 | 28 | /// Splits `subject` into an array of chunks by `separator`. 29 | /// 30 | /// # Arguments 31 | /// 32 | /// * `subject` - The string to split into characters. 33 | /// * `pattern` - The pattern to match the separator. 34 | /// 35 | /// # Example 36 | /// 37 | /// ```rust 38 | /// use voca_rs::*; 39 | /// split::split("rage against the dying of the light", ""); 40 | /// // => ["rage", "against", "the", "dying", "of", "the", "light"] 41 | /// use voca_rs::Voca; 42 | /// "rage against the dying of the light"._split(""); 43 | /// // => ["rage", "against", "the", "dying", "of", "the", "light"] 44 | /// ``` 45 | pub fn split<'a>(subject: &'a str, pattern: &str) -> Vec<&'a str> { 46 | if subject.is_empty() { 47 | return vec![""]; 48 | } 49 | if pattern.is_empty() { 50 | return vec![subject]; 51 | } 52 | subject.split_terminator(pattern).collect::>() 53 | } 54 | 55 | /// Splits `subject` into an array of words. 56 | /// 57 | /// # Arguments 58 | /// 59 | /// * `subject` - The string to split into characters. 60 | /// 61 | /// # Example 62 | /// 63 | /// ```rust 64 | /// use voca_rs::*; 65 | /// split::words("Sześć звёзд are dying"); 66 | /// // => ["Sześć", "звёзд", "are", "dying"] 67 | /// split::words("LazyLoad with XMLHttpRequest and snake_case"); 68 | /// // => ["Lazy", "Load", "with", "XML", "Http", "Request", "and", "snake", "case"] 69 | /// use voca_rs::Voca; 70 | /// "Sześć звёзд are dying"._words(); 71 | /// // => ["Sześć", "звёзд", "are", "dying"] 72 | /// ``` 73 | pub fn words(subject: &str) -> Vec<&str> { 74 | fn split_camel_case(string: &str) -> Vec<&str> { 75 | // https://github.com/withoutboats/heck/blob/master/src/lib.rs 76 | #[derive(Clone, Copy, PartialEq)] 77 | enum WordMode { 78 | /// There have been no lowercase or uppercase characters in the current word. 79 | Boundary, 80 | /// The previous cased character in the current word is lowercase. 81 | Lowercase, 82 | /// The previous cased character in the current word is uppercase. 83 | Uppercase, 84 | } 85 | let mut words = Vec::new(); 86 | let mut word_start = 0; 87 | let mut char_indices = string.char_indices().peekable(); 88 | let mut mode = WordMode::Boundary; 89 | while let Some((c_idx, c)) = char_indices.next() { 90 | if let Some(&(next_idx, next)) = char_indices.peek() { 91 | let next_mode = if c.is_lowercase() { 92 | WordMode::Lowercase 93 | } else if c.is_uppercase() { 94 | WordMode::Uppercase 95 | } else { 96 | mode 97 | }; 98 | 99 | // not uppercase and next is uppercase 100 | if next_mode == WordMode::Lowercase && next.is_uppercase() { 101 | words.push(&string[word_start..next_idx]); 102 | word_start = next_idx; 103 | mode = WordMode::Boundary; 104 | // Otherwise if current and previous are uppercase and next 105 | // is lowercase, word boundary before 106 | } else if mode == WordMode::Uppercase && c.is_uppercase() && next.is_lowercase() { 107 | words.push(&string[word_start..c_idx]); 108 | word_start = c_idx; 109 | mode = WordMode::Boundary; 110 | // Otherwise no word boundary, just update the mode 111 | } else { 112 | mode = next_mode; 113 | } 114 | } 115 | } 116 | words.push(&string[word_start..]); 117 | words 118 | } 119 | 120 | let splitting_punctuation = ['-', '_']; 121 | 122 | let split_by_whitespace_and_punctuation = subject 123 | .unicode_words() 124 | .flat_map(|w| w.split_terminator(|c| splitting_punctuation.contains(&c))) 125 | .filter(|w| !w.is_empty()); 126 | 127 | let res = split_by_whitespace_and_punctuation.flat_map(split_camel_case); 128 | res.collect() 129 | } 130 | 131 | /// Splits `subject` into an array of graphemes 132 | /// 133 | /// # Arguments 134 | /// 135 | /// * `subject` - The string to split into characters. 136 | /// 137 | /// # Example 138 | /// 139 | /// ```rust 140 | /// use voca_rs::*; 141 | /// split::graphemes("a̐éö̲\r\n"); 142 | /// // => ["a̐", "é", "ö̲", "\r\n"] 143 | /// use voca_rs::Voca; 144 | /// "a̐éö̲\r\n"._graphemes(); 145 | /// // => ["a̐", "é", "ö̲", "\r\n"] 146 | /// ``` 147 | pub fn graphemes(subject: &str) -> Vec<&str> { 148 | if subject.is_empty() { 149 | return vec![""]; 150 | } 151 | UnicodeSegmentation::graphemes(subject, true).collect::>() 152 | } 153 | 154 | /// Returns an array of Unicode code point values from characters of `subject`. 155 | /// NOTE: Unicode escape must not be a surrogate 156 | /// 157 | /// # Arguments 158 | /// 159 | /// * `subject` - The string to extract from. 160 | /// 161 | /// # Example 162 | /// 163 | /// ```rust 164 | /// use voca_rs::*; 165 | /// split::code_points("rain"); 166 | /// // => [114, 97, 105, 110] 167 | /// split::code_points("Un garçon de café"); 168 | /// // => [85, 110, 32, 103, 97, 114, 231, 111, 110, 32, 100, 101, 32, 99, 97, 102, 233] 169 | /// use voca_rs::Voca; 170 | /// "rain"._code_points(); 171 | /// // => [114, 97, 105, 110] 172 | /// ``` 173 | pub fn code_points(subject: &str) -> Vec { 174 | if subject.is_empty() { 175 | return vec![]; 176 | } 177 | stfu8::decode_u16(subject).unwrap() 178 | } 179 | -------------------------------------------------------------------------------- /tests/unit/count.rs: -------------------------------------------------------------------------------- 1 | //! voca_rs::count testing 2 | use voca_rs::Voca; 3 | 4 | #[test] 5 | fn count() { 6 | assert_eq!(voca_rs::count::count(""), 0); 7 | assert_eq!(voca_rs::count::count("rain"), 4); 8 | assert_eq!(voca_rs::count::count("b\u{0142}\u{0105}d"), 4); 9 | assert_eq!( 10 | voca_rs::count::count("Die Schildkröte fliegt über das Floß."), 11 | 37 12 | ); 13 | assert_eq!(voca_rs::count::count("Как слышно, приём!"), 18); 14 | } 15 | #[test] 16 | fn _count() { 17 | assert_eq!("rain"._count(), 4); 18 | } 19 | #[test] 20 | fn graphemes() { 21 | assert_eq!(voca_rs::count::count_graphemes(""), 0); 22 | assert_eq!(voca_rs::count::count_graphemes("rain"), 4); 23 | assert_eq!(voca_rs::count::count_graphemes("b\u{0142}\u{0105}d"), 4); 24 | assert_eq!(voca_rs::count::count_graphemes("błąd"), 4); 25 | assert_eq!(voca_rs::count::count_graphemes("a̐éö̲"), 3); 26 | assert_eq!( 27 | voca_rs::count::count_graphemes("Die Schildkröte fliegt über das Floß."), 28 | 37 29 | ); 30 | assert_eq!(voca_rs::count::count_graphemes("cafe\u{0301}"), 4); 31 | } 32 | #[test] 33 | fn _graphemes() { 34 | assert_eq!("rain"._count_graphemes(), 4); 35 | } 36 | #[test] 37 | fn substrings() { 38 | assert_eq!(voca_rs::count::count_substrings("", ""), 0); 39 | assert_eq!(voca_rs::count::count_substrings("******", "*"), 6); 40 | assert_eq!(voca_rs::count::count_substrings("******", "**"), 3); 41 | assert_eq!(voca_rs::count::count_substrings("******", "**-"), 0); 42 | assert_eq!(voca_rs::count::count_substrings("abc", ""), 0); 43 | assert_eq!(voca_rs::count::count_substrings("rain", "rain"), 1); 44 | assert_eq!( 45 | voca_rs::count::count_substrings("Die Schildkröte fliegt über das Floß.", "über"), 46 | 1 47 | ); 48 | assert_eq!( 49 | voca_rs::count::count_substrings("bad boys, bad boys whatcha gonna do?", "boys"), 50 | 2 51 | ); 52 | assert_eq!( 53 | voca_rs::count::count_substrings("Cafe\u{0301} del Mar", "Café"), 54 | 1 55 | ); 56 | assert_eq!( 57 | voca_rs::count::count_substrings("Cafe\u{0301} del Mar Café del Mar cafe\u{0301}", "Café"), 58 | 2 59 | ); 60 | assert_eq!( 61 | voca_rs::count::count_substrings("every dog has its day", "cat"), 62 | 0 63 | ); 64 | } 65 | #[test] 66 | fn _substrings() { 67 | assert_eq!("******"._count_substrings("*"), 6); 68 | } 69 | 70 | #[test] 71 | fn count_where() { 72 | assert_eq!( 73 | voca_rs::count::count_where("hola!", voca_rs::query::is_alpha), 74 | 4 75 | ); 76 | assert_eq!( 77 | voca_rs::count::count_where("2022", |s: &str| -> bool { s == "2" }), 78 | 3 79 | ); 80 | assert_eq!(voca_rs::count::count_where("", voca_rs::query::is_alpha), 0); 81 | assert_eq!( 82 | voca_rs::count::count_where("abc", voca_rs::query::is_alpha), 83 | 3 84 | ); 85 | assert_eq!( 86 | voca_rs::count::count_where("africa654", voca_rs::query::is_alpha), 87 | 6 88 | ); 89 | assert_eq!( 90 | voca_rs::count::count_where("790", voca_rs::query::is_alpha), 91 | 0 92 | ); 93 | assert_eq!( 94 | voca_rs::count::count_where("790", voca_rs::query::is_alphadigit), 95 | 3 96 | ); 97 | assert_eq!( 98 | voca_rs::count::count_where(voca_rs::utils::PRINTABLE, voca_rs::query::is_digit), 99 | 10 100 | ); 101 | assert_eq!( 102 | voca_rs::count::count_where("****--**--**", |s: &str| -> bool { s == "*" }), 103 | 8 104 | ); 105 | assert_eq!( 106 | voca_rs::count::count_where("****--**--**", |_s: &str| -> bool { false }), 107 | 0 108 | ); 109 | } 110 | #[test] 111 | fn _count_where() { 112 | assert_eq!("hola!"._count_where(voca_rs::query::is_alpha), 4); 113 | } 114 | 115 | #[test] 116 | fn words() { 117 | assert_eq!(voca_rs::count::count_words("", ""), 0); 118 | assert_eq!(voca_rs::count::count_words("ab c", ""), 2); 119 | assert_eq!( 120 | voca_rs::count::count_words("Gravity - can cross dimensions!", ""), 121 | 4 122 | ); 123 | assert_eq!( 124 | voca_rs::count::count_words("GravityCanCrossDimensions", ""), 125 | 4 126 | ); 127 | assert_eq!( 128 | voca_rs::count::count_words("Cafe\u{0301}-del-Mar-andBossaNova1", "-"), 129 | 4 130 | ); 131 | assert_eq!(voca_rs::count::count_words("Język /polski wywodzi się z` języka` praindoeuropejskiego za**pośrednictwem+języka-prasłowiańskiego.", ""), 11); 132 | assert_eq!( 133 | voca_rs::count::count_words("Στις--αρχές** (του) 21ου, αιώνα!'", ""), 134 | 5 135 | ); 136 | assert_eq!( 137 | voca_rs::count::count_words("Гравитация-Притягивает-ВСЕ!!", "-"), 138 | 3 139 | ); 140 | } 141 | #[test] 142 | fn _words() { 143 | assert_eq!("Gravity - can cross dimensions!"._count_words(""), 4); 144 | } 145 | 146 | #[test] 147 | fn unique_words() { 148 | assert_eq!(voca_rs::count::count_unique_words("", ""), 0); 149 | assert_eq!(voca_rs::count::count_unique_words("ab c", ""), 2); 150 | assert_eq!( 151 | voca_rs::count::count_unique_words("hello world wonderful world", ""), 152 | 3 153 | ); 154 | assert_eq!( 155 | voca_rs::count::count_unique_words("HelloWorldWonderfulWorld", ""), 156 | 3 157 | ); 158 | // Hebrew: אני יכול לאכול זכוכית וזה לא מזיק לי. 159 | assert_eq!( 160 | voca_rs::count::count_unique_words( 161 | "Hebrew: אני יכול לאכול זכוכית וזה לא מזיק לי. אני יכול לאכול זכוכית.", 162 | "" 163 | ), 164 | 9 165 | ); 166 | // Arabic: أنا قادر على أكل الزجاج و هذا لا يؤلمني. 167 | assert_eq!( 168 | voca_rs::count::count_unique_words( 169 | "Arabic: أنا قادر على أكل الزجاج و هذا لا يؤلمني. أنا قادر على أكل الزجاج و.", 170 | "" 171 | ), 172 | 10 173 | ); 174 | // Vietnamese: Tôi có thể ăn thủy tinh mà không hại gì. 175 | assert_eq!( 176 | voca_rs::count::count_unique_words( 177 | "Vietnamese: Tôi có thể ăn thủy tinh mà không hại gì. Tôi có thể ăn thủy tinh.", 178 | "" 179 | ), 180 | 11 181 | ); 182 | } 183 | 184 | #[test] 185 | fn _unique_words() { 186 | assert_eq!(""._count_unique_words(""), 0); 187 | assert_eq!("hello world wonderful world"._count_unique_words(""), 3); 188 | } 189 | -------------------------------------------------------------------------------- /src/strip.rs: -------------------------------------------------------------------------------- 1 | //! Strips specific characters from subject. 2 | 3 | use split::graphemes; 4 | 5 | /// Strips the byte order mark (BOM) from the beginning of `subject`. 6 | /// 7 | /// # Arguments 8 | /// 9 | /// * `subject` - The string to strip from. 10 | /// 11 | /// # Example 12 | /// 13 | /// ``` 14 | /// use voca_rs::*; 15 | /// strip::strip_bom("\u{FEFF}summertime sadness"); 16 | /// // => "summertime sadness" 17 | /// strip::strip_bom("summertime sadness"); 18 | /// // => "summertime sadness" 19 | /// use voca_rs::Voca; 20 | /// "\u{FEFF}summertime sadness"._strip_bom(); 21 | /// // => "summertime sadness" 22 | /// ``` 23 | pub fn strip_bom(subject: &str) -> String { 24 | match subject.len() { 25 | 0 => "".to_string(), 26 | _ => { 27 | if crate::chop::first(subject, 1) == "\u{FEFF}" { 28 | crate::chop::slice(subject, 1, 0) 29 | } else { 30 | subject.to_string() 31 | } 32 | } 33 | } 34 | } 35 | 36 | /// Strips all HTML tags from `subject`. 37 | /// 38 | /// # Arguments 39 | /// 40 | /// * `subject` - The string to strip from. 41 | /// 42 | /// # Example 43 | /// 44 | /// ``` 45 | /// use voca_rs::*; 46 | /// strip::strip_tags("Summer is nice"); 47 | /// // => "Summer is nice" 48 | /// use voca_rs::Voca; 49 | /// "Summer is nice"._strip_tags(); 50 | /// // => "Summer is nice" 51 | /// ``` 52 | pub fn strip_tags(subject: &str) -> String { 53 | match subject.len() { 54 | 0 => "".to_string(), 55 | _ => strip_html_tags(subject), 56 | } 57 | } 58 | 59 | #[derive(Clone, Copy, PartialEq)] 60 | enum StateMode { 61 | Output, 62 | Html, 63 | Exclamation, 64 | Comment, 65 | } 66 | 67 | fn unicode_string_range(subject: &str, start: usize, end: usize) -> String { 68 | graphemes(subject)[start..end] 69 | .iter() 70 | .map(|c| (*c).to_string()) 71 | .collect::() 72 | } 73 | 74 | fn strip_html_tags(subject: &str) -> String { 75 | // https://github.com/panzerdp/voca/blob/master/src/strip/strip_tags.js 76 | let length = subject.len(); 77 | let mut state: StateMode = StateMode::Output; 78 | let mut depth = 0; 79 | let mut output = String::with_capacity(length); 80 | let mut quote = String::with_capacity(4); 81 | let g = graphemes(subject); 82 | let g_length = g.len(); 83 | for (i, c) in g.iter().enumerate() { 84 | let mut advance = false; 85 | match *c { 86 | "<" => { 87 | if !quote.is_empty() { 88 | } else if i + 2 < g_length 89 | && crate::query::query( 90 | unicode_string_range(subject, i, i + 2).as_str(), 91 | "< ", 92 | 0, 93 | ) 94 | { 95 | advance = true; 96 | } else if state == StateMode::Output { 97 | advance = true; 98 | state = StateMode::Html; 99 | } else if state == StateMode::Html { 100 | depth += 1; 101 | } else { 102 | advance = true; 103 | } 104 | } 105 | "!" => { 106 | if state == StateMode::Html 107 | && i + 2 < g_length 108 | && crate::query::query( 109 | unicode_string_range(subject, i, i + 2).as_str(), 110 | " { 120 | if state == StateMode::Exclamation 121 | && i + 3 < g_length 122 | && crate::query::query( 123 | unicode_string_range(subject, i, i + 3).as_str(), 124 | "!--", 125 | 0, 126 | ) 127 | { 128 | state = StateMode::Comment; 129 | } else { 130 | advance = true; 131 | } 132 | } 133 | "\"" | "'" => { 134 | if state == StateMode::Html { 135 | let c_copy = (*c).to_string(); 136 | if quote.as_str() == c_copy { 137 | quote = String::from(""); 138 | } else if quote.is_empty() { 139 | quote = c_copy; 140 | } 141 | } else { 142 | advance = true; 143 | } 144 | } 145 | "E" | "e" => { 146 | if state == StateMode::Exclamation 147 | && i + 7 < g_length 148 | && crate::query::query( 149 | unicode_string_range(subject, i, i + 7).as_str(), 150 | "doctype", 151 | 0, 152 | ) 153 | { 154 | state = StateMode::Html; 155 | } else { 156 | advance = true; 157 | } 158 | } 159 | ">" => { 160 | if depth > 0 { 161 | depth -= 1; 162 | } else if !quote.is_empty() { 163 | } else if state == StateMode::Html 164 | || state == StateMode::Exclamation 165 | || state == StateMode::Comment 166 | && i + 3 < g_length 167 | && crate::query::query( 168 | unicode_string_range(subject, i, i + 3).as_str(), 169 | "-->", 170 | 0, 171 | ) 172 | { 173 | quote = String::from(""); 174 | state = StateMode::Output; 175 | } else { 176 | advance = true; 177 | } 178 | } 179 | _ => { 180 | advance = true; 181 | } 182 | } 183 | if advance { 184 | match state { 185 | StateMode::Output => { 186 | output.push_str(c); 187 | } 188 | StateMode::Html => {} 189 | _ => {} 190 | } 191 | } 192 | } 193 | output 194 | } 195 | -------------------------------------------------------------------------------- /tests/unit/case.rs: -------------------------------------------------------------------------------- 1 | //! voca_rs::case testing 2 | use voca_rs::Voca; 3 | 4 | #[test] 5 | fn camel_case() { 6 | assert_eq!( 7 | voca_rs::case::camel_case("The World - IS Yours"), 8 | "theWorldIsYours" 9 | ); 10 | assert_eq!( 11 | voca_rs::case::camel_case("_Zażółć-GĘŚLĄ_jaźń-"), 12 | "zażółćGęśląJaźń" 13 | ); 14 | assert_eq!( 15 | voca_rs::case::camel_case("say *** Hello\r\n to--ME++"), 16 | "sayHelloToMe" 17 | ); 18 | assert_eq!(voca_rs::case::camel_case(""), ""); 19 | } 20 | #[test] 21 | fn _camel_case() { 22 | assert_eq!("The World - IS Yours"._camel_case(), "theWorldIsYours"); 23 | } 24 | #[test] 25 | fn pascal_case() { 26 | assert_eq!( 27 | voca_rs::case::pascal_case("The World - IS Yours"), 28 | "TheWorldIsYours" 29 | ); 30 | assert_eq!( 31 | voca_rs::case::pascal_case("_Zażółć-GĘŚLĄ_jaźń-"), 32 | "ZażółćGęśląJaźń" 33 | ); 34 | assert_eq!( 35 | voca_rs::case::pascal_case("say *** Hello\r\n to--ME++"), 36 | "SayHelloToMe" 37 | ); 38 | assert_eq!(voca_rs::case::pascal_case(""), ""); 39 | } 40 | #[test] 41 | fn _pascal_case() { 42 | assert_eq!("The World - IS Yours"._pascal_case(), "TheWorldIsYours"); 43 | } 44 | #[test] 45 | fn capitalize() { 46 | assert_eq!( 47 | voca_rs::case::capitalize("The World IS YourS", true), 48 | "The world is yours" 49 | ); 50 | assert_eq!( 51 | voca_rs::case::capitalize("ZAżółć GĘŚLĄ jAźń", true), 52 | "Zażółć gęślą jaźń" 53 | ); 54 | assert_eq!( 55 | voca_rs::case::capitalize("say Hello to ME", false), 56 | "Say Hello to ME" 57 | ); 58 | assert_eq!(voca_rs::case::capitalize("", true), ""); 59 | } 60 | #[test] 61 | fn _capitalize() { 62 | assert_eq!("The World IS YourS"._capitalize(true), "The world is yours"); 63 | } 64 | #[test] 65 | fn decapitalize() { 66 | assert_eq!( 67 | voca_rs::case::decapitalize("The World IS YourS", true), 68 | "the world is yours" 69 | ); 70 | assert_eq!( 71 | voca_rs::case::decapitalize("ZAżółć GĘŚLĄ jAźń", true), 72 | "zażółć gęślą jaźń" 73 | ); 74 | assert_eq!( 75 | voca_rs::case::decapitalize("Say Hello to ME", false), 76 | "say Hello to ME" 77 | ); 78 | assert_eq!(voca_rs::case::decapitalize("", true), ""); 79 | } 80 | #[test] 81 | fn _decapitalize() { 82 | assert_eq!( 83 | "The World IS YourS"._decapitalize(true), 84 | "the world is yours" 85 | ); 86 | } 87 | #[test] 88 | fn kebab_case() { 89 | assert_eq!( 90 | voca_rs::case::kebab_case("The World - IS Yours"), 91 | "the-world-is-yours" 92 | ); 93 | assert_eq!( 94 | voca_rs::case::kebab_case("_Zażółć-GĘŚLĄ_jaźń-"), 95 | "zażółć-gęślą-jaźń" 96 | ); 97 | assert_eq!( 98 | voca_rs::case::kebab_case("say *** Hello\r\n to--ME++"), 99 | "say-hello-to-me" 100 | ); 101 | assert_eq!(voca_rs::case::kebab_case(""), ""); 102 | } 103 | #[test] 104 | fn _kebab_case() { 105 | assert_eq!("The World - IS Yours"._kebab_case(), "the-world-is-yours"); 106 | } 107 | #[test] 108 | fn shouty_kebab_case() { 109 | assert_eq!( 110 | voca_rs::case::shouty_kebab_case("The World - IS Yours"), 111 | "THE-WORLD-IS-YOURS" 112 | ); 113 | assert_eq!( 114 | voca_rs::case::shouty_kebab_case("_Zażółć-GĘŚLĄ_jaźń-"), 115 | "ZAŻÓŁĆ-GĘŚLĄ-JAŹŃ" 116 | ); 117 | assert_eq!( 118 | voca_rs::case::shouty_kebab_case("say *** Hello\r\n to--ME++"), 119 | "SAY-HELLO-TO-ME" 120 | ); 121 | assert_eq!(voca_rs::case::shouty_kebab_case(""), ""); 122 | } 123 | #[test] 124 | fn _shouty_kebab_case() { 125 | assert_eq!( 126 | "The World - IS Yours"._shouty_kebab_case(), 127 | "THE-WORLD-IS-YOURS" 128 | ); 129 | } 130 | #[test] 131 | fn train_case() { 132 | assert_eq!( 133 | voca_rs::case::train_case("The World - IS Yours"), 134 | "The-World-Is-Yours" 135 | ); 136 | assert_eq!( 137 | voca_rs::case::train_case("_Zażółć-GĘŚLĄ_jaźń-"), 138 | "Zażółć-Gęślą-Jaźń" 139 | ); 140 | assert_eq!( 141 | voca_rs::case::train_case("say *** Hello\r\n to--ME++"), 142 | "Say-Hello-To-Me" 143 | ); 144 | assert_eq!(voca_rs::case::train_case(""), ""); 145 | } 146 | #[test] 147 | fn _train_case() { 148 | assert_eq!("The World - IS Yours"._train_case(), "The-World-Is-Yours"); 149 | } 150 | #[test] 151 | fn lower_case() { 152 | assert_eq!( 153 | voca_rs::case::lower_case("The World IS YourS"), 154 | "the world is yours" 155 | ); 156 | assert_eq!( 157 | voca_rs::case::lower_case("Zażółć gęśLą jaźń"), 158 | "zażółć gęślą jaźń" 159 | ); 160 | assert_eq!(voca_rs::case::lower_case(""), ""); 161 | } 162 | #[test] 163 | fn _lower_case() { 164 | assert_eq!("The World IS YourS"._lower_case(), "the world is yours"); 165 | } 166 | #[test] 167 | fn snake_case() { 168 | assert_eq!( 169 | voca_rs::case::snake_case("The World - IS Yours"), 170 | "the_world_is_yours" 171 | ); 172 | assert_eq!( 173 | voca_rs::case::snake_case("_Zażółć-GĘŚLĄ_jaźń-"), 174 | "zażółć_gęślą_jaźń" 175 | ); 176 | assert_eq!( 177 | voca_rs::case::snake_case("say *** Hello\r\n to--ME++"), 178 | "say_hello_to_me" 179 | ); 180 | assert_eq!(voca_rs::case::snake_case(""), ""); 181 | } 182 | #[test] 183 | fn _snake_case() { 184 | assert_eq!("The World - IS Yours"._snake_case(), "the_world_is_yours"); 185 | } 186 | #[test] 187 | fn shouty_snake_case() { 188 | assert_eq!( 189 | voca_rs::case::shouty_snake_case("The World - IS Yours"), 190 | "THE_WORLD_IS_YOURS" 191 | ); 192 | assert_eq!( 193 | voca_rs::case::shouty_snake_case("_Zażółć-GĘŚLĄ_jaźń-"), 194 | "ZAŻÓŁĆ_GĘŚLĄ_JAŹŃ" 195 | ); 196 | assert_eq!( 197 | voca_rs::case::shouty_snake_case("say *** Hello\r\n to--ME++"), 198 | "SAY_HELLO_TO_ME" 199 | ); 200 | assert_eq!(voca_rs::case::shouty_snake_case(""), ""); 201 | } 202 | #[test] 203 | fn _shouty_snake_case() { 204 | assert_eq!( 205 | "The World - IS Yours"._shouty_snake_case(), 206 | "THE_WORLD_IS_YOURS" 207 | ); 208 | } 209 | #[test] 210 | fn swap_case() { 211 | assert_eq!( 212 | voca_rs::case::swap_case("The World - IS Yours"), 213 | "tHE wORLD - is yOURS" 214 | ); 215 | assert_eq!( 216 | voca_rs::case::swap_case("_Zażółć-GĘŚLĄ_jaźń-"), 217 | "_zAŻÓŁĆ-gęślą_JAŹŃ-" 218 | ); 219 | assert_eq!( 220 | voca_rs::case::swap_case("say über Hello to--ME++"), 221 | "SAY ÜBER hELLO TO--me++" 222 | ); 223 | assert_eq!(voca_rs::case::swap_case(""), ""); 224 | } 225 | #[test] 226 | fn _swap_case() { 227 | assert_eq!("The World - IS Yours"._swap_case(), "tHE wORLD - is yOURS"); 228 | } 229 | #[test] 230 | fn upper_case() { 231 | assert_eq!( 232 | voca_rs::case::upper_case("The World IS YourS"), 233 | "THE WORLD IS YOURS" 234 | ); 235 | assert_eq!( 236 | voca_rs::case::upper_case("Zażółć gęślą jaźń"), 237 | "ZAŻÓŁĆ GĘŚLĄ JAŹŃ" 238 | ); 239 | assert_eq!(voca_rs::case::upper_case(""), ""); 240 | } 241 | #[test] 242 | fn _upper_case() { 243 | assert_eq!("The World IS YourS"._upper_case(), "THE WORLD IS YOURS"); 244 | } 245 | #[test] 246 | fn title_case() { 247 | assert_eq!( 248 | voca_rs::case::title_case("The World - IS Yours"), 249 | "The World Is Yours" 250 | ); 251 | assert_eq!( 252 | voca_rs::case::title_case("_Zażółć-GĘŚLĄ_jaźń-"), 253 | "Zażółć Gęślą Jaźń" 254 | ); 255 | assert_eq!( 256 | voca_rs::case::title_case("say über Hello to--ME++"), 257 | "Say Über Hello To Me" 258 | ); 259 | assert_eq!(voca_rs::case::title_case(""), ""); 260 | } 261 | #[test] 262 | fn _title_case() { 263 | assert_eq!("The World - IS Yours"._title_case(), "The World Is Yours"); 264 | } 265 | #[test] 266 | fn lower_first() { 267 | assert_eq!(voca_rs::case::lower_first("Fred"), "fred"); 268 | assert_eq!(voca_rs::case::lower_first("FRED"), "fRED"); 269 | assert_eq!( 270 | voca_rs::case::lower_first("The World IS YourS"), 271 | "the World IS YourS" 272 | ); 273 | assert_eq!( 274 | voca_rs::case::lower_first("ZAżółć GĘŚLĄ jAźń"), 275 | "zAżółć GĘŚLĄ jAźń" 276 | ); 277 | assert_eq!( 278 | voca_rs::case::lower_first("Über Hello to ME"), 279 | "über Hello to ME" 280 | ); 281 | assert_eq!(voca_rs::case::lower_first(""), ""); 282 | } 283 | #[test] 284 | fn _lower_first() { 285 | assert_eq!("Fred"._lower_first(), "fred"); 286 | } 287 | #[test] 288 | fn upper_first() { 289 | assert_eq!(voca_rs::case::upper_first("Fred"), "Fred"); 290 | assert_eq!(voca_rs::case::upper_first("FRED"), "FRED"); 291 | assert_eq!( 292 | voca_rs::case::upper_first("the World IS YourS"), 293 | "The World IS YourS" 294 | ); 295 | assert_eq!( 296 | voca_rs::case::upper_first("The World IS YourS"), 297 | "The World IS YourS" 298 | ); 299 | assert_eq!( 300 | voca_rs::case::upper_first("zAżółć GĘŚLĄ jAźń"), 301 | "ZAżółć GĘŚLĄ jAźń" 302 | ); 303 | assert_eq!( 304 | voca_rs::case::upper_first("über Hello to ME"), 305 | "Über Hello to ME" 306 | ); 307 | assert_eq!(voca_rs::case::upper_first(""), ""); 308 | } 309 | #[test] 310 | fn _upper_first() { 311 | assert_eq!("fred"._upper_first(), "Fred"); 312 | } 313 | -------------------------------------------------------------------------------- /tests/unit/strip.rs: -------------------------------------------------------------------------------- 1 | //! voca_rs::strip testing 2 | use voca_rs::Voca; 3 | 4 | #[test] 5 | fn strip_bom() { 6 | assert_eq!(voca_rs::strip::strip_bom(""), ""); 7 | assert_eq!(voca_rs::strip::strip_bom("\u{FEFF}"), ""); 8 | assert_eq!( 9 | voca_rs::strip::strip_bom(voca_rs::utils::PRINTABLE), 10 | voca_rs::utils::PRINTABLE 11 | ); 12 | assert_eq!( 13 | voca_rs::strip::strip_bom("\u{FEFF}summertime sadness"), 14 | "summertime sadness" 15 | ); 16 | assert_eq!( 17 | voca_rs::strip::strip_bom("\\u{FEFF}summertime sadness"), 18 | "\\u{FEFF}summertime sadness" 19 | ); 20 | assert_eq!( 21 | voca_rs::strip::strip_bom("summertime sadness"), 22 | "summertime sadness" 23 | ); 24 | } 25 | #[test] 26 | fn _strip_bom() { 27 | assert_eq!( 28 | "\u{FEFF}summertime sadness"._strip_bom(), 29 | "summertime sadness" 30 | ); 31 | } 32 | #[test] 33 | fn strip_tags_general_tests() { 34 | // should not modify a string without tags 35 | assert_eq!(voca_rs::strip::strip_tags(""), ""); 36 | assert_eq!(voca_rs::strip::strip_tags("Hello world!"), "Hello world!"); 37 | assert_eq!(voca_rs::strip::strip_tags(" "), " "); 38 | // should strip tags 39 | assert_eq!( 40 | voca_rs::strip::strip_tags("Summer is nice"), 41 | "Summer is nice" 42 | ); 43 | assert_eq!( 44 | voca_rs::strip::strip_tags("Hello world!"), 45 | "Hello world!" 46 | ); 47 | assert_eq!( 48 | voca_rs::strip::strip_tags("Hello world!"), 49 | "Hello world!" 50 | ); 51 | assert_eq!( 52 | voca_rs::strip::strip_tags("Hello world!"), 53 | "Hello world!" 54 | ); 55 | assert_eq!( 56 | voca_rs::strip::strip_tags(""), 57 | " echo hello " 58 | ); 59 | // should strip tags which attributes contain < or > 60 | assert_eq!( 61 | voca_rs::strip::strip_tags("hello world"), 62 | "hello world" 63 | ); 64 | assert_eq!( 65 | voca_rs::strip::strip_tags("hello world"), 66 | "hello world" 67 | ); 68 | assert_eq!( 69 | voca_rs::strip::strip_tags("hello <'bar'\"> world"), 70 | "hello world" 71 | ); 72 | // should strip tags on multiple lines 73 | assert_eq!( 74 | voca_rs::strip::strip_tags("This's a string with quotes:\n\"strings in double quote\";\n'strings in single quote\';\nthis\\line is single quoted /with\\slashes"), 75 | "This\'s a string with quotes:\n\"strings in double quote\";\n\'strings in single quote\';\nthis\\line is single quoted /with\\slashes" 76 | ); 77 | // should strip comments and doctype 78 | assert_eq!( 79 | voca_rs::strip::strip_tags(""), 80 | "" 81 | ); 82 | assert_eq!( 83 | voca_rs::strip::strip_tags("Hello world!"), 84 | "Hello world!" 85 | ); 86 | assert_eq!( 87 | voca_rs::strip::strip_tags( 88 | "Hello world!" 89 | ), 90 | "Hello world!" 91 | ); 92 | assert_eq!( 93 | voca_rs::strip::strip_tags("Hello world!"), 94 | "Hello world!" 95 | ); 96 | assert_eq!( 97 | voca_rs::strip::strip_tags("Hello world!"), 98 | "Hello world!" 99 | ); 100 | } 101 | #[test] 102 | fn strip_tags_user_tests() { 103 | assert_eq!( 104 | voca_rs::strip::strip_tags("】มีมี่’ เด็กสาวที่นอนไม่ค่อยหลับเนื่องจากกลัวผี ขี้เหงา และอะไรหลายๆ อย่างทำให้เธอมึนได้โล่เพราะไม่ค่อยได้นอน การที่เธอ นอนไม่หลับทำให้เธอได้เจอกับ ‘ดีเจไททัน’ แห่งคลื่น 99.99 MHzเขาจัดรายการในช่วง Midnight Fantasy ตีสามถึงตีห้า และมีมี่ก็เป็นผู้ฟังเพียงคนเดียวของเขาจากที่ตอนแรกเธอฟังดีเจไททันเพื่อช่วยปลอบประโลมการที่เธอต้องมาอยู่หอเพียงลำพัง แต่ไปๆ มาๆกลับกลายเป็นว่าเธออยู่รอฟังเขาทุกคืนทำให้เธอไปเรียนแบบมึนๆ จนบังเอิญไปนอนหลับซบ ‘ธรรม’ผู้ชายจอมกวนที่บังเอิญมานอนให้เธอซบ! จนอาจารย์สั่งให้ไปทำรายงานคู่กัน และนั่นก็เป็นที่มาของการที่เธอเริ่มไม่แน่ใจแล้วว่าเธอปลื้มดีเจไททัน หรือแอบหวั่นไหวกับนายจอมกวนคนนี้กันแน่
"), 105 | "】มีมี่’ เด็กสาวที่นอนไม่ค่อยหลับเนื่องจากกลัวผี ขี้เหงา และอะไรหลายๆ อย่างทำให้เธอมึนได้โล่เพราะไม่ค่อยได้นอน การที่เธอ นอนไม่หลับทำให้เธอได้เจอกับ ‘ดีเจไททัน’ แห่งคลื่น 99.99 MHzเขาจัดรายการในช่วง Midnight Fantasy ตีสามถึงตีห้า และมีมี่ก็เป็นผู้ฟังเพียงคนเดียวของเขาจากที่ตอนแรกเธอฟังดีเจไททันเพื่อช่วยปลอบประโลมการที่เธอต้องมาอยู่หอเพียงลำพัง แต่ไปๆ มาๆกลับกลายเป็นว่าเธออยู่รอฟังเขาทุกคืนทำให้เธอไปเรียนแบบมึนๆ จนบังเอิญไปนอนหลับซบ ‘ธรรม’ผู้ชายจอมกวนที่บังเอิญมานอนให้เธอซบ! จนอาจารย์สั่งให้ไปทำรายงานคู่กัน และนั่นก็เป็นที่มาของการที่เธอเริ่มไม่แน่ใจแล้วว่าเธอปลื้มดีเจไททัน หรือแอบหวั่นไหวกับนายจอมกวนคนนี้กันแน่" 106 | ); 107 | } 108 | #[test] 109 | fn strip_tags_special_tests() { 110 | // should treat especially broken or invalid tags 111 | assert_eq!(voca_rs::strip::strip_tags("< html >"), "< html >"); 112 | assert_eq!(voca_rs::strip::strip_tags("<<>>"), ""); 113 | assert_eq!( 114 | voca_rs::strip::strip_tags("HtMl text"), 115 | "HtMl text" 116 | ); 117 | assert_eq!( 118 | voca_rs::strip::strip_tags("hello \t\tworld... strip_tags_test"), 119 | "hello \t\tworld... strip_tags_test" 120 | ); 121 | assert_eq!( 122 | voca_rs::strip::strip_tags("hello

world

"), 123 | "helloworld" 124 | ); 125 | assert_eq!( 126 | voca_rs::strip::strip_tags("He>llo < world!"), 127 | "He>llo < world!" 128 | ); 129 | // should handle unicode 130 | assert_eq!( 131 | voca_rs::strip::strip_tags(""), 132 | "Ω≈ç≈≈Ω" 133 | ); 134 | assert_eq!( 135 | voca_rs::strip::strip_tags(""), 136 | "片仮名平仮名" 137 | ); 138 | assert_eq!( 139 | voca_rs::strip::strip_tags("text here"), 140 | "text here" 141 | ); 142 | } 143 | #[test] 144 | fn strip_tags_xss_tests() { 145 | // should strip potential xss tags', function() { 146 | // @see https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet 147 | assert_eq!( 148 | voca_rs::strip::strip_tags( 149 | "3\">" 151 | ), 152 | "" 153 | ); 154 | assert_eq!( 155 | voca_rs::strip::strip_tags(""), 156 | "evil();" 157 | ); 158 | assert_eq!( 159 | voca_rs::strip::strip_tags(""), 160 | "" 161 | ); 162 | assert_eq!( 163 | voca_rs::strip::strip_tags("\">"), 164 | "" 165 | ); 166 | assert_eq!( 167 | voca_rs::strip::strip_tags(""), 168 | "" 169 | ); 170 | assert_eq!( 171 | voca_rs::strip::strip_tags(""), 172 | "" 173 | ); 174 | assert_eq!( 175 | voca_rs::strip::strip_tags(""), 176 | "" 177 | ); 178 | assert_eq!( 179 | voca_rs::strip::strip_tags("<"), 180 | "" 181 | ); 182 | assert_eq!( 183 | voca_rs::strip::strip_tags(""), 196 | "" 197 | ); 198 | assert_eq!( 199 | voca_rs::strip::strip_tags(""), 200 | "" 201 | ); 202 | assert_eq!( 203 | voca_rs::strip::strip_tags( 204 | "" 205 | ), 206 | "" 207 | ); 208 | assert_eq!( 209 | voca_rs::strip::strip_tags(""), 210 | "" 211 | ); 212 | assert_eq!( 213 | voca_rs::strip::strip_tags(""), 214 | "` SRC=\"httx://xss.rocks/xss.js\">" 215 | ); 216 | assert_eq!( 217 | voca_rs::strip::strip_tags(""), 218 | "" 219 | ); 220 | assert_eq!(voca_rs::strip::strip_tags("PT SRC=\"httx://xss.rocks/xss.js\">"), "document.write(\""); 221 | } 222 | #[test] 223 | fn _strip_tags() { 224 | assert_eq!( 225 | "Summer is nice"._strip_tags(), 226 | "Summer is nice" 227 | ); 228 | } 229 | 230 | #[test] 231 | fn partial_directive() { 232 | assert_eq!(voca_rs::strip::strip_tags("<"), ""); 233 | assert_eq!(voca_rs::strip::strip_tags("天地不仁<"), ">天地不仁"); 241 | assert_eq!(voca_rs::strip::strip_tags("\u{00a0} ["Lazy", "Load", "with", "XML", "Http", "Request", "and", "snake", "case"] 25 | let words_in_string = &string_in_words.join(" "); 26 | // => "Lazy Load with XML Http Request and snake case" 27 | let truncated_string = chop::prune(&words_in_string, 21, ""); 28 | // => "Lazy Load with XML..." 29 | let sliced_string = chop::slice(&truncated_string, 5, -2); 30 | // => "Load with XML." 31 | let snaked_string = case::snake_case(&sliced_string); 32 | // => "load_with_xml" 33 | ``` 34 | 35 | Using traits (all methods start from the underscore symbol): 36 | 37 | ```rust 38 | use voca_rs::Voca; 39 | 40 | "LazyLoad with XMLHttpRequest and snake_case" 41 | ._words() 42 | // => ["Lazy", "Load", "with", "XML", "Http", "Request", "and", "snake", "case"] 43 | .join(" ") 44 | // => "Lazy Load with XML Http Request and snake case" 45 | ._prune(21, "") 46 | // => "Lazy Load with XML..." 47 | ._slice(5, -2) 48 | // => "Load with XML." 49 | ._snake_case(); 50 | // => "load_with_xml" 51 | ``` 52 | 53 | ## Documentation 54 | 55 | See the complete documentation at 56 | 57 | Run tests: `cargo test`
58 | Build docs: `cargo doc` -> `./target/doc/voca_rs/index.html`
59 | Build a project: `cargo build` -> `./target/debug` 60 | 61 | ## Functions 62 | 63 | ### Case 64 | 65 | - [camel_case](https://docs.rs/voca_rs/*/voca_rs/case/fn.camel_case.html) 66 | - [capitalize](https://docs.rs/voca_rs/*/voca_rs/case/fn.capitalize.html) 67 | - [decapitalize](https://docs.rs/voca_rs/*/voca_rs/case/fn.decapitalize.html) 68 | - [kebab_case](https://docs.rs/voca_rs/*/voca_rs/case/fn.kebab_case.html) 69 | - [lower_case](https://docs.rs/voca_rs/*/voca_rs/case/fn.lower_case.html) 70 | - [lower_first](https://docs.rs/voca_rs/*/voca_rs/case/fn.lower_first.html) 71 | - [pascal_case](https://docs.rs/voca_rs/*/voca_rs/case/fn.pascal_case.html) 72 | - [shouty_kebab_case](https://docs.rs/voca_rs/*/voca_rs/case/fn.shouty_kebab_case.html) 73 | - [shouty_snake_case](https://docs.rs/voca_rs/*/voca_rs/case/fn.shouty_snake_case.html) 74 | - [snake_case](https://docs.rs/voca_rs/*/voca_rs/case/fn.snake_case.html) 75 | - [swap_case](https://docs.rs/voca_rs/*/voca_rs/case/fn.swap_case.html) 76 | - [title_case](https://docs.rs/voca_rs/*/voca_rs/case/fn.title_case.html) 77 | - [train_case](https://docs.rs/voca_rs/*/voca_rs/case/fn.train_case.html) 78 | - [upper_case](https://docs.rs/voca_rs/*/voca_rs/case/fn.upper_case.html) 79 | - [upper_first](https://docs.rs/voca_rs/*/voca_rs/case/fn.upper_first.html) 80 | 81 | ### Chop 82 | 83 | - [after](https://docs.rs/voca_rs/*/voca_rs/chop/fn.after.html) 84 | - [after_last](https://docs.rs/voca_rs/*/voca_rs/chop/fn.after_last.html) 85 | - [before](https://docs.rs/voca_rs/*/voca_rs/chop/fn.before.html) 86 | - [before_last](https://docs.rs/voca_rs/*/voca_rs/chop/fn.before_last.html) 87 | - [char_at](https://docs.rs/voca_rs/*/voca_rs/chop/fn.char_at.html) 88 | - [code_point_at](https://docs.rs/voca_rs/*/voca_rs/chop/fn.code_point_at.html) 89 | - [first](https://docs.rs/voca_rs/*/voca_rs/chop/fn.first.html) 90 | - [foreign_key](https://docs.rs/voca_rs/*/voca_rs/case/fn.foreign_key.html) 91 | - [grapheme_at](https://docs.rs/voca_rs/*/voca_rs/chop/fn.grapheme_at.html) 92 | - [last](https://docs.rs/voca_rs/*/voca_rs/chop/fn.last.html) 93 | - [limit_words](https://docs.rs/voca_rs/*/voca_rs/chop/fn.limit_words.html) 94 | - [max](https://docs.rs/voca_rs/*/voca_rs/chop/fn.max.html) 95 | - [min](https://docs.rs/voca_rs/*/voca_rs/chop/fn.min.html) 96 | - [prune](https://docs.rs/voca_rs/*/voca_rs/chop/fn.prune.html) 97 | - [removeprefix](https://docs.rs/voca_rs/*/voca_rs/chop/fn.removeprefix.html) 98 | - [removesuffix](https://docs.rs/voca_rs/*/voca_rs/chop/fn.removesuffix.html) 99 | - [slice](https://docs.rs/voca_rs/*/voca_rs/chop/fn.slice.html) 100 | - [substr](https://docs.rs/voca_rs/*/voca_rs/chop/fn.substr.html) 101 | - [substring](https://docs.rs/voca_rs/*/voca_rs/chop/fn.substring.html) 102 | - [truncate](https://docs.rs/voca_rs/*/voca_rs/chop/fn.truncate.html) 103 | 104 | ### Count 105 | 106 | - [count](https://docs.rs/voca_rs/*/voca_rs/count/fn.count.html) 107 | - [count_graphemes](https://docs.rs/voca_rs/*/voca_rs/count/fn.count_graphemes.html) 108 | - [count_substrings](https://docs.rs/voca_rs/*/voca_rs/count/fn.count_substrings.html) 109 | - [count_unique_words](https://docs.rs/voca_rs/*/voca_rs/count/fn.count_unique_words.html) 110 | - [count_where](https://docs.rs/voca_rs/*/voca_rs/count/fn.count_where.html) 111 | - [count_words](https://docs.rs/voca_rs/*/voca_rs/count/fn.count_words.html) 112 | 113 | ### Escape 114 | 115 | - [escape_html](https://docs.rs/voca_rs/*/voca_rs/escape/fn.escape_html.html) 116 | - [escape_regexp](https://docs.rs/voca_rs/*/voca_rs/escape/fn.escape_regexp.html) 117 | - [unescape_html](https://docs.rs/voca_rs/*/voca_rs/escape/fn.unescape_html.html) 118 | 119 | ### Index 120 | 121 | - [index_all](https://docs.rs/voca_rs/*/voca_rs/index/fn.index_all.html) 122 | - [index_of](https://docs.rs/voca_rs/*/voca_rs/index/fn.index_of.html) 123 | - [last_index_of](https://docs.rs/voca_rs/*/voca_rs/index/fn.last_index_of.html) 124 | - [search](https://docs.rs/voca_rs/*/voca_rs/index/fn.search.html) 125 | 126 | ### Manipulate 127 | 128 | - [expand_spaces](https://docs.rs/voca_rs/*/voca_rs/manipulate/fn.expand_spaces.html) 129 | - [expand_tabs](https://docs.rs/voca_rs/*/voca_rs/manipulate/fn.expand_tabs.html) 130 | - [finish](https://docs.rs/voca_rs/*/voca_rs/manipulate/fn.finish.html) 131 | - [insert](https://docs.rs/voca_rs/*/voca_rs/manipulate/fn.insert.html) 132 | - [latinise](https://docs.rs/voca_rs/*/voca_rs/manipulate/fn.latinise.html) 133 | - [pad](https://docs.rs/voca_rs/*/voca_rs/manipulate/fn.pad.html) 134 | - [pad_left](https://docs.rs/voca_rs/*/voca_rs/manipulate/fn.pad_left.html) 135 | - [pad_right](https://docs.rs/voca_rs/*/voca_rs/manipulate/fn.pad_right.html) 136 | - [repeat](https://docs.rs/voca_rs/*/voca_rs/manipulate/fn.repeat.html) 137 | - [replace](https://docs.rs/voca_rs/*/voca_rs/manipulate/fn.replace.html) 138 | - [replace_all](https://docs.rs/voca_rs/*/voca_rs/manipulate/fn.replace_all.html) 139 | - [reverse](https://docs.rs/voca_rs/*/voca_rs/manipulate/fn.reverse.html) 140 | - [reverse_grapheme](https://docs.rs/voca_rs/*/voca_rs/manipulate/fn.reverse_grapheme.html) 141 | - [slugify](https://docs.rs/voca_rs/*/voca_rs/manipulate/fn.slugify.html) 142 | - [splice](https://docs.rs/voca_rs/*/voca_rs/manipulate/fn.splice.html) 143 | - [start](https://docs.rs/voca_rs/*/voca_rs/manipulate/fn.start.html) 144 | - [tr](https://docs.rs/voca_rs/*/voca_rs/manipulate/fn.tr.html) 145 | - [trim](https://docs.rs/voca_rs/*/voca_rs/manipulate/fn.trim.html) 146 | - [trim_left](https://docs.rs/voca_rs/*/voca_rs/manipulate/fn.trim_left.html) 147 | - [trim_right](https://docs.rs/voca_rs/*/voca_rs/manipulate/fn.trim_right.html) 148 | - [word_wrap](https://docs.rs/voca_rs/*/voca_rs/manipulate/fn.word_wrap.html) 149 | - [zfill](https://docs.rs/voca_rs/*/voca_rs/manipulate/fn.zfill.html) 150 | 151 | ### Query 152 | 153 | - [ends_with](https://docs.rs/voca_rs/*/voca_rs/query/fn.ends_with.html) 154 | - [includes](https://docs.rs/voca_rs/*/voca_rs/query/fn.includes.html) 155 | - [is_alpha](https://docs.rs/voca_rs/*/voca_rs/query/fn.is_alpha.html) 156 | - [is_alphadigit](https://docs.rs/voca_rs/*/voca_rs/query/fn.is_alphadigit.html) 157 | - [is_blank](https://docs.rs/voca_rs/*/voca_rs/query/fn.is_blank.html) 158 | - [is_camel_case](https://docs.rs/voca_rs/*/voca_rs/query/fn.is_camel_case.html) 159 | - [is_capitalize](https://docs.rs/voca_rs/*/voca_rs/query/fn.is_capitalize.html) 160 | - [is_decapitalize](https://docs.rs/voca_rs/*/voca_rs/query/fn.is_decapitalize.html) 161 | - [is_digit](https://docs.rs/voca_rs/*/voca_rs/query/fn.is_digit.html) 162 | - [is_empty](https://docs.rs/voca_rs/*/voca_rs/query/fn.is_empty.html) 163 | - [is_foreign_key](https://docs.rs/voca_rs/*/voca_rs/query/fn.is_foreign_key.html) 164 | - [is_lowercase](https://docs.rs/voca_rs/*/voca_rs/query/fn.is_lowercase.html) 165 | - [is_lower_first](https://docs.rs/voca_rs/*/voca_rs/query/fn.is_lower_first.html) 166 | - [is_kebab_case](https://docs.rs/voca_rs/*/voca_rs/query/fn.is_kebab_case.html) 167 | - [is_shouty_kebab_case](https://docs.rs/voca_rs/*/voca_rs/query/fn.is_shouty_kebab_case.html) 168 | - [is_numeric](https://docs.rs/voca_rs/*/voca_rs/query/fn.is_numeric.html) 169 | - [is_pascal_case](https://docs.rs/voca_rs/*/voca_rs/query/fn.is_pascal_case.html) 170 | - [is_snake_case](https://docs.rs/voca_rs/*/voca_rs/query/fn.is_snake_case.html) 171 | - [is_shouty_snake_case](https://docs.rs/voca_rs/*/voca_rs/query/fn.is_shouty_snake_case.html) 172 | - [is_title](https://docs.rs/voca_rs/*/voca_rs/query/fn.is_title.html) 173 | - [is_train_case](https://docs.rs/voca_rs/*/voca_rs/query/fn.is_train_case.html) 174 | - [is_uppercase](https://docs.rs/voca_rs/*/voca_rs/query/fn.is_uppercase.html) 175 | - [is_upper_first](https://docs.rs/voca_rs/*/voca_rs/query/fn.is_upper_first.html) 176 | - [matches](https://docs.rs/voca_rs/*/voca_rs/query/fn.matches.html) 177 | - [query](https://docs.rs/voca_rs/*/voca_rs/query/fn.query.html) 178 | - [starts_with](https://docs.rs/voca_rs/*/voca_rs/query/fn.starts_with.html) 179 | 180 | ### Split 181 | 182 | - [chars](https://docs.rs/voca_rs/*/voca_rs/split/fn.chars.html) 183 | - [code_points](https://docs.rs/voca_rs/*/voca_rs/split/fn.code_points.html) 184 | - [graphemes](https://docs.rs/voca_rs/*/voca_rs/split/fn.graphemes.html) 185 | - [split](https://docs.rs/voca_rs/*/voca_rs/split/fn.split.html) 186 | - [words](https://docs.rs/voca_rs/*/voca_rs/split/fn.words.html) 187 | 188 | ### Strip 189 | 190 | - [strip_bom](https://docs.rs/voca_rs/*/voca_rs/strip/fn.strip_bom.html) 191 | - [strip_tags](https://docs.rs/voca_rs/*/voca_rs/strip/fn.strip_tags.html) 192 | 193 | ### Utils 194 | 195 | - [ASCII_LETTERS](https://docs.rs/voca_rs/*/voca_rs/utils/constant.ASCII_LETTERS.html) 196 | - [ASCII_LOWERCASE](https://docs.rs/voca_rs/*/voca_rs/utils/constant.ASCII_LOWERCASE.html) 197 | - [ASCII_UPPERCASE](https://docs.rs/voca_rs/*/voca_rs/utils/constant.ASCII_UPPERCASE.html) 198 | - [DIGITS](https://docs.rs/voca_rs/*/voca_rs/utils/constant.DIGITS.html) 199 | - [HEXDIGITS](https://docs.rs/voca_rs/*/voca_rs/utils/constant.HEXDIGITS.html) 200 | - [OCTDIGITS](https://docs.rs/voca_rs/*/voca_rs/utils/constant.OCTDIGITS.html) 201 | - [PUNCTUATION](https://docs.rs/voca_rs/*/voca_rs/utils/constant.PUNCTUATION.html) 202 | - [PRINTABLE](https://docs.rs/voca_rs/*/voca_rs/utils/constant.PRINTABLE.html) 203 | - [WHITESPACE](https://docs.rs/voca_rs/*/voca_rs/utils/constant.WHITESPACE.html) 204 | - [VERSION](https://docs.rs/voca_rs/*/voca_rs/utils/constant.VERSION.html) 205 | 206 | ## Copyright 207 | 208 | Coded by A. Merezhanyi 209 | 210 | ## License 211 | 212 | Licensed under [MIT License](LICENSE.md) 213 | -------------------------------------------------------------------------------- /src/case.rs: -------------------------------------------------------------------------------- 1 | //! Converts the `subject` to a selected case. 2 | 3 | /// Converts the `subject` to camel case. 4 | /// 5 | /// # Arguments 6 | /// 7 | /// * `subject` - The string to convert to camel case. 8 | /// 9 | /// # Example 10 | /// ``` 11 | /// use voca_rs::*; 12 | /// case::camel_case("bird flight"); 13 | /// // => "birdFlight" 14 | /// case::camel_case("BirdFlight"); 15 | /// // => "birdFlight" 16 | /// case::camel_case("-BIRD-FLIGHT-"); 17 | /// // => "birdFlight" 18 | /// use voca_rs::Voca; 19 | /// "bird flight"._camel_case(); 20 | /// // => "birdFlight" 21 | /// ``` 22 | pub fn camel_case(subject: &str) -> String { 23 | camel_and_pascal_case(subject, TitleMode::Normal) 24 | } 25 | 26 | #[derive(Clone, Copy, PartialEq)] 27 | enum TitleMode { 28 | Normal, 29 | Caps, 30 | } 31 | 32 | fn camel_and_pascal_case(subject: &str, title_mode: TitleMode) -> String { 33 | return match subject.len() { 34 | 0 => subject.to_string(), 35 | _ => return_string(subject, title_mode), 36 | }; 37 | 38 | fn return_string(subject: &str, title_mode: TitleMode) -> String { 39 | let mut res = String::with_capacity(subject.len()); 40 | for (i, c) in crate::split::words(subject).iter().enumerate() { 41 | let s = if i == 0 && title_mode == TitleMode::Normal { 42 | lower_case(c) 43 | } else { 44 | capitalize(c, true) 45 | }; 46 | res.push_str(&s); 47 | } 48 | 49 | res 50 | } 51 | } 52 | 53 | /// Converts the first character of `subject` to upper case. If `restToLower` is `true`, convert the rest of `subject` to lower case. 54 | /// 55 | /// # Arguments 56 | /// 57 | /// * `subject` - The string to capitalize. 58 | /// * `rest_to_lower` - Convert the rest of `subject` to lower case. 59 | /// 60 | /// # Example 61 | /// 62 | /// ``` 63 | /// use voca_rs::*; 64 | /// case::capitalize("green", true); 65 | /// // => "Green" 66 | /// case::capitalize("Say Hello to ME", true); 67 | /// // => "Say hello to me" 68 | /// use voca_rs::Voca; 69 | /// "green"._capitalize(true); 70 | /// // => "Green" 71 | /// ``` 72 | pub fn capitalize(subject: &str, rest_to_lower: bool) -> String { 73 | let rest_to_lower_mode = if rest_to_lower { 74 | RestMode::Lower 75 | } else { 76 | RestMode::Normal 77 | }; 78 | capitalize_decapitalize(subject, rest_to_lower_mode, CapsMode::Caps) 79 | } 80 | 81 | #[derive(Clone, Copy, PartialEq)] 82 | enum RestMode { 83 | Lower, 84 | Normal, 85 | } 86 | #[derive(Clone, Copy, PartialEq)] 87 | enum CapsMode { 88 | Caps, 89 | Small, 90 | } 91 | 92 | fn capitalize_decapitalize(subject: &str, rest_mode: RestMode, caps_mode: CapsMode) -> String { 93 | return match subject.len() { 94 | 0 => subject.to_string(), 95 | _ => return_string(subject, rest_mode, caps_mode), 96 | }; 97 | 98 | fn return_string(subject: &str, rest_mode: RestMode, caps_mode: CapsMode) -> String { 99 | let mut res = String::with_capacity(subject.len()); 100 | 101 | for (i, c) in crate::split::chars(subject).iter().enumerate() { 102 | let s = if i == 0 { 103 | match caps_mode { 104 | CapsMode::Caps => c.to_uppercase(), 105 | CapsMode::Small => c.to_lowercase(), 106 | } 107 | } else { 108 | match rest_mode { 109 | RestMode::Lower => c.to_lowercase(), 110 | RestMode::Normal => (*c).to_string(), 111 | } 112 | }; 113 | res.push_str(&s); 114 | } 115 | 116 | res 117 | } 118 | } 119 | /// Converts the first character of `subject` to lower case. If `restToLower` is `true`, convert the rest of `subject` to lower case. 120 | /// 121 | /// # Arguments 122 | /// 123 | /// * `subject` - The string to decapitalize. 124 | /// * `rest_to_lower` - Convert the rest of `subject` to lower case. 125 | /// # Example 126 | /// 127 | /// ``` 128 | /// use voca_rs::*; 129 | /// case::decapitalize("Green", true); 130 | /// // => "green" 131 | /// case::decapitalize("Say Hello to ME", false); 132 | /// // => "say Hello to ME" 133 | /// use voca_rs::Voca; 134 | /// "Green"._decapitalize(true); 135 | /// // => "green" 136 | /// ``` 137 | pub fn decapitalize(subject: &str, rest_to_lower: bool) -> String { 138 | let rest_to_lower_mode = if rest_to_lower { 139 | RestMode::Lower 140 | } else { 141 | RestMode::Normal 142 | }; 143 | capitalize_decapitalize(subject, rest_to_lower_mode, CapsMode::Small) 144 | } 145 | 146 | /// Converts the `subject` to kebab case. 147 | /// 148 | /// # Arguments 149 | /// 150 | /// * `subject` - The string to convert to kebab case. 151 | /// 152 | /// # Example 153 | /// ``` 154 | /// use voca_rs::*; 155 | /// case::kebab_case("goodbye blue sky"); 156 | /// // => "goodbye-blue-sky" 157 | /// case::kebab_case("GoodbyeBlueSky"); 158 | /// // => "goodbye-blue-sky" 159 | /// case::kebab_case("-Goodbye-Blue-Sky-"); 160 | /// // => "goodbye-blue-sky" 161 | /// use voca_rs::Voca; 162 | /// "goodbye blue sky"._kebab_case(); 163 | /// // => "goodbye-blue-sky" 164 | /// ``` 165 | pub fn kebab_case(subject: &str) -> String { 166 | kebab_and_shouty_kebab_and_train_case(subject, KebabMode::Normal) 167 | } 168 | 169 | /// Converts the `subject` to SHOUTY kebab case. 170 | /// 171 | /// # Arguments 172 | /// 173 | /// * `subject` - The string to convert to SHOUTY kebab case. 174 | /// 175 | /// # Example 176 | /// ``` 177 | /// use voca_rs::*; 178 | /// case::shouty_kebab_case("goodbye blue sky"); 179 | /// // => "GOODBYE-BLUE-SKY" 180 | /// case::shouty_kebab_case("GoodbyeBlueSky"); 181 | /// // => "GOODBYE-BLUE-SKY" 182 | /// case::shouty_kebab_case("-Goodbye-Blue-Sky-"); 183 | /// // => "GOODBYE-BLUE-SKY" 184 | /// use voca_rs::Voca; 185 | /// "goodbye blue sky"._shouty_kebab_case(); 186 | /// // => "GOODBYE-BLUE-SKY" 187 | /// ``` 188 | pub fn shouty_kebab_case(subject: &str) -> String { 189 | kebab_and_shouty_kebab_and_train_case(subject, KebabMode::Shouty) 190 | } 191 | 192 | #[derive(Clone, Copy, PartialEq)] 193 | enum KebabMode { 194 | Normal, 195 | Shouty, 196 | Train, 197 | } 198 | 199 | fn kebab_and_shouty_kebab_and_train_case(subject: &str, kebab_mode: KebabMode) -> String { 200 | match subject.len() { 201 | 0 => subject.to_string(), 202 | _ => crate::split::words(subject) 203 | .into_iter() 204 | .map(|c| match kebab_mode { 205 | KebabMode::Normal => lower_case(c), 206 | KebabMode::Shouty => upper_case(c), 207 | KebabMode::Train => capitalize(c, true), 208 | }) 209 | .collect::>() 210 | .join("-"), 211 | } 212 | } 213 | 214 | /// Converts the `subject` to lower case. 215 | /// 216 | /// # Arguments 217 | /// 218 | /// * `subject` - The string to convert to lower case. 219 | /// 220 | /// # Example 221 | /// 222 | /// ``` 223 | /// use voca_rs::*; 224 | /// case::lower_case("Green"); 225 | /// // => "green" 226 | /// case::lower_case("Say Hello to ME"); 227 | /// // => "say hello to me" 228 | /// use voca_rs::Voca; 229 | /// "Green"._lower_case(); 230 | /// // => "green" 231 | /// ``` 232 | pub fn lower_case(subject: &str) -> String { 233 | match subject.len() { 234 | 0 => subject.to_string(), 235 | _ => { 236 | let mut res = String::with_capacity(subject.len()); 237 | for c in crate::split::chars(subject).iter() { 238 | res.push_str(&c.to_lowercase()); 239 | } 240 | res 241 | } 242 | } 243 | } 244 | 245 | /// Converts the `subject` to pascal case. 246 | /// 247 | /// # Arguments 248 | /// 249 | /// * `subject` - The string to convert to pascal case. 250 | /// 251 | /// # Example 252 | /// ``` 253 | /// use voca_rs::*; 254 | /// case::pascal_case("bird flight"); 255 | /// // => "BirdFlight" 256 | /// case::pascal_case("BirdFlight"); 257 | /// // => "BirdFlight" 258 | /// case::pascal_case("-BIRD-FLIGHT-"); 259 | /// // => "BirdFlight" 260 | /// use voca_rs::Voca; 261 | /// "bird flight"._pascal_case(); 262 | /// // => "BirdFlight" 263 | /// ``` 264 | pub fn pascal_case(subject: &str) -> String { 265 | camel_and_pascal_case(subject, TitleMode::Caps) 266 | } 267 | 268 | /// Converts the `subject` to snake case. 269 | /// 270 | /// # Arguments 271 | /// 272 | /// * `subject` - The string to convert to snake case. 273 | /// 274 | /// # Example 275 | /// ``` 276 | /// use voca_rs::*; 277 | /// case::snake_case("learning to fly"); 278 | /// // => "learning_to_fly" 279 | /// case::snake_case("LearningToFly"); 280 | /// // => "learning_to_fly" 281 | /// case::snake_case("-Learning-To-Fly-"); 282 | /// // => "learning_to_fly" 283 | /// use voca_rs::Voca; 284 | /// "learning to fly"._snake_case(); 285 | /// // => "learning_to_fly" 286 | /// ``` 287 | pub fn snake_case(subject: &str) -> String { 288 | snake_and_shouty_snake_case(subject, false) 289 | } 290 | 291 | /// Converts the `subject` to SHOUTY snake case. 292 | /// 293 | /// # Arguments 294 | /// 295 | /// * `subject` - The string to convert to shouty snake case. 296 | /// 297 | /// # Example 298 | /// ``` 299 | /// use voca_rs::*; 300 | /// case::shouty_snake_case("learning to fly"); 301 | /// // => "LEARNING_TO_FLY" 302 | /// case::shouty_snake_case("LearningToFly"); 303 | /// // => "LEARNING_TO_FLY" 304 | /// case::shouty_snake_case("-Learning-To-Fly-"); 305 | /// // => "LEARNING_TO_FLY" 306 | /// use voca_rs::Voca; 307 | /// "learning to fly"._shouty_snake_case(); 308 | /// // => "LEARNING_TO_FLY" 309 | /// ``` 310 | pub fn shouty_snake_case(subject: &str) -> String { 311 | snake_and_shouty_snake_case(subject, true) 312 | } 313 | 314 | fn snake_and_shouty_snake_case(subject: &str, shouty: bool) -> String { 315 | match subject.len() { 316 | 0 => subject.to_string(), 317 | _ => crate::split::words(subject) 318 | .into_iter() 319 | .map(|c| { 320 | if shouty { 321 | upper_case(c) 322 | } else { 323 | lower_case(c) 324 | } 325 | }) 326 | .collect::>() 327 | .join("_"), 328 | } 329 | } 330 | 331 | /// Converts the uppercase alpha characters of `subject` to lowercase and lowercase characters to uppercase. 332 | /// 333 | /// # Arguments 334 | /// 335 | /// * `subject` - The string to swap the case. 336 | /// 337 | /// # Example 338 | /// ``` 339 | /// use voca_rs::*; 340 | /// case::swap_case("League of Shadows"); 341 | /// // => "lEAGUE OF sHADOWS" 342 | /// case::swap_case("2 üBer Bees"); 343 | /// // => "2 ÜbER bEES" 344 | /// use voca_rs::Voca; 345 | /// "League of Shadows"._swap_case(); 346 | /// // => "lEAGUE OF sHADOWS" 347 | /// ``` 348 | pub fn swap_case(subject: &str) -> String { 349 | match subject.len() { 350 | 0 => subject.to_string(), 351 | _ => crate::split::chars(subject) 352 | .into_iter() 353 | .map(|s| { 354 | s.chars() 355 | .filter_map(|c| { 356 | if c.is_lowercase() { 357 | c.to_uppercase().next() 358 | } else { 359 | c.to_lowercase().next() 360 | } 361 | }) 362 | .collect() 363 | }) 364 | .collect::>() 365 | .join(""), 366 | } 367 | } 368 | 369 | /// Converts the `subject` to title case. 370 | /// 371 | /// # Arguments 372 | /// 373 | /// * `subject` - The string to convert to title case. 374 | /// 375 | /// # Example 376 | /// ``` 377 | /// use voca_rs::*; 378 | /// case::title_case("bird flight"); 379 | /// // => "Bird Flight" 380 | /// case::title_case("BirdFlight"); 381 | /// // => "Bird Flight" 382 | /// case::title_case("-BIRD-FLIGHT-"); 383 | /// // => "Bird Flight" 384 | /// use voca_rs::Voca; 385 | /// "bird flight"._title_case(); 386 | /// // => "Bird Flight" 387 | /// ``` 388 | pub fn title_case(subject: &str) -> String { 389 | match subject.len() { 390 | 0 => subject.to_string(), 391 | _ => crate::split::words(subject) 392 | .into_iter() 393 | .map(|c| capitalize(c, true)) 394 | .collect::>() 395 | .join(" "), 396 | } 397 | } 398 | 399 | /// Converts the `subject` to train case. 400 | /// 401 | /// # Arguments 402 | /// 403 | /// * `subject` - The string to convert to train case. 404 | /// 405 | /// # Example 406 | /// ``` 407 | /// use voca_rs::*; 408 | /// case::train_case("goodbye blue sky"); 409 | /// // => "Goodbye-Blue-Sky" 410 | /// case::train_case("GoodbyeBlueSky"); 411 | /// // => "Goodbye-Blue-Sky" 412 | /// case::train_case("-Goodbye-Blue-Sky-"); 413 | /// // => "Goodbye-Blue-Sky" 414 | /// use voca_rs::Voca; 415 | /// "goodbye blue sky"._train_case(); 416 | /// // => "Goodbye-Blue-Sky" 417 | /// ``` 418 | pub fn train_case(subject: &str) -> String { 419 | kebab_and_shouty_kebab_and_train_case(subject, KebabMode::Train) 420 | } 421 | 422 | /// Converts the `subject` to upper case. 423 | /// 424 | /// # Arguments 425 | /// 426 | /// * `subject` - The string to convert to upper case. 427 | /// 428 | /// # Example 429 | /// 430 | /// ``` 431 | /// use voca_rs::*; 432 | /// case::upper_case("Green"); 433 | /// // => "GREEN" 434 | /// case::upper_case("Say Hello to ME"); 435 | /// // => "SAY HELLO TO ME" 436 | /// use voca_rs::Voca; 437 | /// "Green"._upper_case(); 438 | /// // => "GREEN" 439 | /// ``` 440 | pub fn upper_case(subject: &str) -> String { 441 | match subject.len() { 442 | 0 => subject.to_string(), 443 | _ => { 444 | let mut res = String::with_capacity(subject.len()); 445 | for c in crate::split::chars(subject).iter() { 446 | res.push_str(&c.to_uppercase()); 447 | } 448 | res 449 | } 450 | } 451 | } 452 | 453 | /// Converts the first character of the `subject` to lower case. 454 | /// 455 | /// # Arguments 456 | /// 457 | /// * `subject` - The string to convert. 458 | /// 459 | /// # Example 460 | /// 461 | /// ``` 462 | /// use voca_rs::*; 463 | /// case::lower_first("Fred"); 464 | /// // => "fred" 465 | /// case::lower_first("FRED"); 466 | /// // => "fRED" 467 | /// use voca_rs::Voca; 468 | /// "Fred"._lower_first(); 469 | /// // => "fred" 470 | /// ``` 471 | pub fn lower_first(subject: &str) -> String { 472 | decapitalize(subject, false) 473 | } 474 | 475 | /// Converts the first character of the `subject` to upper case. 476 | /// 477 | /// # Arguments 478 | /// 479 | /// * `subject` - The string to convert. 480 | /// 481 | /// # Example 482 | /// 483 | /// ``` 484 | /// use voca_rs::*; 485 | /// case::upper_first("fred"); 486 | /// // => "Fred" 487 | /// case::upper_first("FRED"); 488 | /// // => "FRED" 489 | /// use voca_rs::Voca; 490 | /// "fred"._upper_first(); 491 | /// // => "Fred" 492 | /// ``` 493 | pub fn upper_first(subject: &str) -> String { 494 | capitalize(subject, false) 495 | } 496 | -------------------------------------------------------------------------------- /tests/unit/chop.rs: -------------------------------------------------------------------------------- 1 | //! voca_rs::chop testing 2 | use voca_rs::Voca; 3 | 4 | #[test] 5 | fn after() { 6 | assert_eq!(voca_rs::chop::after("", ""), ""); 7 | assert_eq!( 8 | voca_rs::chop::after("This is my name", "This is"), 9 | " my name" 10 | ); 11 | assert_eq!( 12 | voca_rs::chop::after("S̃o̊m̋ȩ̈ gḷ̉y̌p̆ẖs a̋řẹ̆̇ hër̵ē̱", "gḷ̉y̌p̆ẖs"), 13 | " a̋řẹ̆̇ hër̵ē̱" 14 | ); 15 | assert_eq!( 16 | voca_rs::chop::after("S̃o̊m̋ȩ̈ gḷ̉y̌p̆ẖs a̋řẹ̆̇ hër̵ē̱", "Something"), 17 | "" 18 | ); 19 | } 20 | #[test] 21 | fn _after() { 22 | assert_eq!("This is my name"._after("This is"), " my name"); 23 | } 24 | #[test] 25 | fn after_last() { 26 | assert_eq!(voca_rs::chop::after_last("", ""), ""); 27 | assert_eq!( 28 | voca_rs::chop::after_last("To be, or not to be, that is the question", "be,"), 29 | " that is the question" 30 | ); 31 | assert_eq!( 32 | voca_rs::chop::after_last("To be, or not to be, that is the question", "ho-ho"), 33 | "" 34 | ); 35 | assert_eq!( 36 | voca_rs::chop::after_last("S̃o̊m̋ȩ̈ gḷ̉y̌p̆ẖs a̋řẹ̆̇ hër̵ē̱, but a̋řẹ̆̇ nŏt tẖër̵ē̱", "a̋řẹ̆̇"), 37 | " nŏt tẖër̵ē̱" 38 | ); 39 | } 40 | #[test] 41 | fn _after_last() { 42 | assert_eq!( 43 | "To be, or not to be, that is the question"._after_last("be,"), 44 | " that is the question" 45 | ); 46 | } 47 | #[test] 48 | fn before() { 49 | assert_eq!(voca_rs::chop::before("", ""), ""); 50 | assert_eq!( 51 | voca_rs::chop::before("This is my name", "my name"), 52 | "This is " 53 | ); 54 | assert_eq!( 55 | voca_rs::chop::before("S̃o̊m̋ȩ̈ gḷ̉y̌p̆ẖs a̋řẹ̆̇ hër̵ē̱", "gḷ̉y̌p̆ẖs"), 56 | "S̃o̊m̋ȩ̈ " 57 | ); 58 | assert_eq!( 59 | voca_rs::chop::before("S̃o̊m̋ȩ̈ gḷ̉y̌p̆ẖs a̋řẹ̆̇ hër̵ē̱", "Something"), 60 | "" 61 | ); 62 | } 63 | #[test] 64 | fn _before() { 65 | assert_eq!("This is my name"._before("my name"), "This is "); 66 | } 67 | #[test] 68 | fn before_last() { 69 | assert_eq!(voca_rs::chop::before_last("", ""), ""); 70 | assert_eq!( 71 | voca_rs::chop::before_last("To be, or not to be, that is the question", "be,"), 72 | "To be, or not to " 73 | ); 74 | assert_eq!( 75 | voca_rs::chop::before_last("To be, or not to be, that is the question", "ho-ho"), 76 | "" 77 | ); 78 | assert_eq!( 79 | voca_rs::chop::before_last("S̃o̊m̋ȩ̈ gḷ̉y̌p̆ẖs a̋řẹ̆̇ hër̵ē̱, but a̋řẹ̆̇ nŏt tẖër̵ē̱", "a̋řẹ̆̇"), 80 | "S̃o̊m̋ȩ̈ gḷ̉y̌p̆ẖs a̋řẹ̆̇ hër̵ē̱, but " 81 | ); 82 | } 83 | #[test] 84 | fn _before_last() { 85 | assert_eq!( 86 | "To be, or not to be, that is the question"._before_last("be,"), 87 | "To be, or not to " 88 | ); 89 | } 90 | #[test] 91 | fn char_at() { 92 | assert_eq!(voca_rs::chop::char_at("", 0), ""); 93 | assert_eq!(voca_rs::chop::char_at("rain", 0), "r"); 94 | assert_eq!(voca_rs::chop::char_at("rain", 2), "i"); 95 | assert_eq!(voca_rs::chop::char_at("rain", 3), "n"); 96 | assert_eq!(voca_rs::chop::char_at("rain", 40), "n"); 97 | assert_eq!(voca_rs::chop::char_at("b\u{0142}\u{0105}d", 2), "ą"); 98 | assert_eq!( 99 | voca_rs::chop::char_at("Die Schildkröte fliegt über das Floß.", 12), 100 | "ö" 101 | ); 102 | assert_eq!(voca_rs::chop::char_at("Как слышно, приём!", 15), "ё"); 103 | } 104 | #[test] 105 | fn _char_at() { 106 | assert_eq!("rain"._char_at(0), "r"); 107 | } 108 | #[test] 109 | fn code_point_at() { 110 | assert_eq!(voca_rs::chop::code_point_at("", 0), []); 111 | assert_eq!(voca_rs::chop::code_point_at("rain", 1), [97]); 112 | assert_eq!(voca_rs::chop::code_point_at("café", 4), [233]); 113 | assert_eq!(voca_rs::chop::code_point_at("cafe\u{0301}", 4), [101, 769]); 114 | assert_eq!(voca_rs::chop::code_point_at("b\u{0142}\u{0105}d", 1), [322]); 115 | assert_eq!(voca_rs::chop::code_point_at("über das Floß.", 0), [252]); 116 | assert_eq!(voca_rs::chop::code_point_at("a̐éö̲", 3), [111, 776, 818]); 117 | assert_eq!(voca_rs::chop::code_point_at("cafe\u{0301}", 0), [99]); 118 | assert_eq!(voca_rs::chop::code_point_at("cafe\u{0301}", 2), [102]); 119 | assert_eq!(voca_rs::chop::code_point_at("cafe\u{0301}!", 3), [101, 769]); 120 | assert_eq!(voca_rs::chop::code_point_at("cafe\u{0301}!", 4), [33]); 121 | assert_eq!(voca_rs::chop::code_point_at("cafe\u{0301}!", 5), [33]); 122 | assert_eq!(voca_rs::chop::code_point_at("cafe\u{0301}!", 30), [33]); 123 | } 124 | #[test] 125 | fn _code_point_at() { 126 | assert_eq!(voca_rs::chop::code_point_at("rain", 1), [97]); 127 | } 128 | #[test] 129 | fn first() { 130 | assert_eq!(voca_rs::chop::first("", 0), ""); 131 | assert_eq!(voca_rs::chop::first("a", 0), ""); 132 | assert_eq!(voca_rs::chop::first("rain", 2), "ra"); 133 | assert_eq!(voca_rs::chop::first("rain", 4), "rain"); 134 | assert_eq!(voca_rs::chop::first("rain", 20), "rain"); 135 | assert_eq!(voca_rs::chop::first("b\u{0142}\u{0105}d", 3), "błą"); 136 | assert_eq!(voca_rs::chop::first("über das Floß.", 1), "ü"); 137 | assert_eq!(voca_rs::chop::first("Как слышно, приём!", 3), "Как"); 138 | assert_eq!(voca_rs::chop::first("e\u{0301}", 1), "e"); 139 | } 140 | #[test] 141 | fn _first() { 142 | assert_eq!("rain"._first(2), "ra"); 143 | } 144 | #[test] 145 | fn foreign_key() { 146 | assert_eq!(voca_rs::chop::foreign_key(""), ""); 147 | assert_eq!(voca_rs::chop::foreign_key("foo_bar"), "foo_bar_id"); 148 | assert_eq!(voca_rs::chop::foreign_key("Foo bar"), "foo_bar_id"); 149 | assert_eq!(voca_rs::chop::foreign_key("Foo Bar"), "foo_bar_id"); 150 | assert_eq!(voca_rs::chop::foreign_key("Foo::Bar"), "bar_id"); 151 | assert_eq!(voca_rs::chop::foreign_key("Test::Foo::Bar"), "bar_id"); 152 | assert_eq!(voca_rs::chop::foreign_key("FooBar"), "foo_bar_id"); 153 | assert_eq!(voca_rs::chop::foreign_key("fooBar"), "foo_bar_id"); 154 | assert_eq!(voca_rs::chop::foreign_key("fooBar3"), "foo_bar3_id"); 155 | } 156 | #[test] 157 | fn _foreign_key() { 158 | assert_eq!("foo_bar"._foreign_key(), "foo_bar_id"); 159 | } 160 | #[test] 161 | fn grapheme_at() { 162 | assert_eq!(voca_rs::chop::grapheme_at("", 0), ""); 163 | assert_eq!(voca_rs::chop::grapheme_at("é", 0), "é"); 164 | assert_eq!(voca_rs::chop::grapheme_at("b\u{0142}\u{0105}d", 1), "ł"); 165 | assert_eq!(voca_rs::chop::grapheme_at("über das Floß.", 0), "ü"); 166 | assert_eq!(voca_rs::chop::grapheme_at("a̐éö̲", 0), "a̐"); 167 | assert_eq!(voca_rs::chop::grapheme_at("cafe\u{0301}", 0), "c"); 168 | assert_eq!(voca_rs::chop::grapheme_at("cafe\u{0301}", 1), "a"); 169 | assert_eq!(voca_rs::chop::grapheme_at("cafe\u{0301}", 2), "f"); 170 | assert_eq!(voca_rs::chop::grapheme_at("cafe\u{0301}", 3), "é"); 171 | assert_eq!(voca_rs::chop::grapheme_at("cafe\u{0301}", 4), "é"); 172 | assert_eq!(voca_rs::chop::grapheme_at("cafe\u{0301}", 5), "é"); 173 | assert_eq!(voca_rs::chop::grapheme_at("cafe\u{0301}", 30), "é"); 174 | assert_eq!(voca_rs::chop::grapheme_at("cafe\u{0301}!", 3), "é"); 175 | assert_eq!(voca_rs::chop::grapheme_at("cafe\u{0301}!", 4), "!"); 176 | assert_eq!(voca_rs::chop::grapheme_at("cafe\u{0301}!", 5), "!"); 177 | assert_eq!(voca_rs::chop::grapheme_at("cafe\u{0301}!", 30), "!"); 178 | } 179 | #[test] 180 | fn _grapheme_at() { 181 | assert_eq!("a̐éö̲"._grapheme_at(0), "a̐"); 182 | } 183 | #[test] 184 | fn last() { 185 | assert_eq!(voca_rs::chop::last("", 0), ""); 186 | assert_eq!(voca_rs::chop::last("a", 0), ""); 187 | assert_eq!(voca_rs::chop::last("a", 1), "a"); 188 | assert_eq!(voca_rs::chop::last("a", 2), "a"); 189 | assert_eq!(voca_rs::chop::last("aa", 2), "aa"); 190 | assert_eq!(voca_rs::chop::last("ab", 3), "ab"); 191 | assert_eq!(voca_rs::chop::last("ab", 20), "ab"); 192 | assert_eq!(voca_rs::chop::last("b\u{0142}\u{0105}d", 2), "ąd"); 193 | assert_eq!(voca_rs::chop::last("helicopter", 1), "r"); 194 | assert_eq!(voca_rs::chop::last("über das Floß.", 2), "ß."); 195 | assert_eq!(voca_rs::chop::last("e\u{0301}", 1), "\u{0301}"); 196 | } 197 | #[test] 198 | fn _last() { 199 | assert_eq!("helicopter"._last(1), "r"); 200 | } 201 | #[test] 202 | fn prune() { 203 | assert_eq!(voca_rs::chop::prune("", 0, ""), ""); 204 | assert_eq!(voca_rs::chop::prune("a", 0, ""), ""); 205 | assert_eq!(voca_rs::chop::prune("a", 1, ""), "a"); 206 | assert_eq!(voca_rs::chop::prune("a", 2, ""), "a"); 207 | assert_eq!(voca_rs::chop::prune("ab", 2, ""), "ab"); 208 | assert_eq!(voca_rs::chop::prune("ab", 3, ""), "ab"); 209 | assert_eq!(voca_rs::chop::prune("ab", 20, ""), "ab"); 210 | assert_eq!(voca_rs::chop::prune("Once upon a time", 7, ""), "Once..."); 211 | assert_eq!( 212 | voca_rs::chop::prune("Die Schildkröte fliegt über das Floß.", 19, "~~"), 213 | "Die Schildkröte~~" 214 | ); 215 | assert_eq!(voca_rs::chop::prune("once upon", 10, ""), "once upon"); 216 | assert_eq!( 217 | voca_rs::chop::prune("Как слышно, приём!", 14, ""), 218 | "Как слышно..." 219 | ); 220 | } 221 | #[test] 222 | fn _prune() { 223 | assert_eq!("Once upon a time"._prune(7, ""), "Once..."); 224 | } 225 | #[test] 226 | fn removeprefix() { 227 | assert_eq!(voca_rs::chop::removeprefix("", ""), ""); 228 | assert_eq!( 229 | voca_rs::chop::removeprefix("Once upon a time", ""), 230 | "Once upon a time" 231 | ); 232 | assert_eq!(voca_rs::chop::removeprefix("", "Once"), ""); 233 | assert_eq!( 234 | voca_rs::chop::removeprefix("Once upon a time", "None"), 235 | "Once upon a time" 236 | ); 237 | assert_eq!( 238 | voca_rs::chop::removeprefix("Once upon a time", "Once"), 239 | " upon a time" 240 | ); 241 | assert_eq!( 242 | voca_rs::chop::removeprefix("Once Once upon a time", "Once"), 243 | " Once upon a time" 244 | ); 245 | assert_eq!( 246 | voca_rs::chop::removeprefix("O̱̣̊ñç̉é ụ̈̇pǒ̵̱n ą̆ tímę", "O̱̣̊ñç̉é"), 247 | " ụ̈̇pǒ̵̱n ą̆ tímę" 248 | ); 249 | } 250 | #[test] 251 | fn _removeprefix() { 252 | assert_eq!("Once upon a time"._removeprefix("Once"), " upon a time"); 253 | } 254 | 255 | #[test] 256 | fn removesuffix() { 257 | assert_eq!(voca_rs::chop::removesuffix("", ""), ""); 258 | assert_eq!( 259 | voca_rs::chop::removesuffix("Once upon a time", ""), 260 | "Once upon a time" 261 | ); 262 | assert_eq!(voca_rs::chop::removesuffix("", "Once"), ""); 263 | assert_eq!( 264 | voca_rs::chop::removesuffix("Once upon a time", "None"), 265 | "Once upon a time" 266 | ); 267 | assert_eq!( 268 | voca_rs::chop::removesuffix("Once upon a time", "time"), 269 | "Once upon a " 270 | ); 271 | assert_eq!( 272 | voca_rs::chop::removesuffix("Once upon a time time", "time"), 273 | "Once upon a time " 274 | ); 275 | assert_eq!( 276 | voca_rs::chop::removesuffix("O̱̣̊ñç̉é ụ̈̇pǒ̵̱n ą̆ tímę", "tímę"), 277 | "O̱̣̊ñç̉é ụ̈̇pǒ̵̱n ą̆ " 278 | ); 279 | } 280 | #[test] 281 | fn _removesuffix() { 282 | assert_eq!("Once upon a time"._removesuffix("time"), "Once upon a "); 283 | } 284 | #[test] 285 | fn slice() { 286 | assert_eq!(voca_rs::chop::slice("", 0, 0), ""); 287 | assert_eq!(voca_rs::chop::slice("a", 0, 0), "a"); 288 | assert_eq!(voca_rs::chop::slice("a", 0, 1), "a"); 289 | assert_eq!(voca_rs::chop::slice("a", 0, 10), "a"); 290 | assert_eq!(voca_rs::chop::slice("a", 2, 0), ""); 291 | assert_eq!(voca_rs::chop::slice("ab", -2, 0), "ab"); 292 | assert_eq!(voca_rs::chop::slice("ab", -3, 0), "ab"); 293 | assert_eq!(voca_rs::chop::slice("ab", -20, 20), "ab"); 294 | assert_eq!(voca_rs::chop::slice("a", -20, -10), ""); 295 | assert_eq!(voca_rs::chop::slice("ab", -20, -1), "a"); 296 | assert_eq!(voca_rs::chop::slice("helicopter", 1, 0), "elicopter"); 297 | assert_eq!(voca_rs::chop::slice("b\u{0142}\u{0105}d", -2, 0), "ąd"); 298 | assert_eq!( 299 | voca_rs::chop::slice("Die Schildkröte fliegt.", 4, -8), 300 | "Schildkröte" 301 | ); 302 | assert_eq!(voca_rs::chop::slice("e\u{0301}", -1, 0), "\u{0301}"); 303 | assert_eq!(voca_rs::chop::slice("b\u{0142}\u{0105}d", -20, 10), "błąd"); 304 | assert_eq!(voca_rs::chop::slice("b\u{0142}\u{0105}d", -2, 100), "ąd"); 305 | } 306 | #[test] 307 | fn _slice() { 308 | assert_eq!("helicopter"._slice(1, 0), "elicopter"); 309 | } 310 | #[test] 311 | fn substr() { 312 | assert_eq!(voca_rs::chop::substr("", 0, 0), ""); 313 | assert_eq!(voca_rs::chop::substr("a", 0, 0), "a"); 314 | assert_eq!(voca_rs::chop::substr("a", 10, 0), ""); 315 | assert_eq!(voca_rs::chop::substr("a", 0, 1), "a"); 316 | assert_eq!(voca_rs::chop::substr("a", 0, 10), "a"); 317 | assert_eq!(voca_rs::chop::substr("a", 2, 0), ""); 318 | assert_eq!(voca_rs::chop::substr("ab", 1, 0), "b"); 319 | assert_eq!(voca_rs::chop::substr("abcd", 3, 1), "d"); 320 | assert_eq!(voca_rs::chop::substr("abcd", 30, 1), ""); 321 | assert_eq!(voca_rs::chop::substr("ab", 2, 0), ""); 322 | assert_eq!(voca_rs::chop::substr("ab", 3, 0), ""); 323 | assert_eq!(voca_rs::chop::substr("ab", 20, 20), ""); 324 | assert_eq!(voca_rs::chop::substr("a", 20, 10), ""); 325 | assert_eq!(voca_rs::chop::substr("helicopter", 1, 0), "elicopter"); 326 | assert_eq!(voca_rs::chop::substr("b\u{0142}\u{0105}d", 1, 2), "łą"); 327 | assert_eq!(voca_rs::chop::substr("über das Floß.", 9, 4), "Floß"); 328 | assert_eq!(voca_rs::chop::substr("e\u{0301}", 1, 0), "\u{0301}"); 329 | } 330 | #[test] 331 | fn _substr() { 332 | assert_eq!("helicopter"._substr(1, 0), "elicopter"); 333 | } 334 | #[test] 335 | fn substring() { 336 | assert_eq!(voca_rs::chop::substring("", 0, 0), ""); 337 | assert_eq!(voca_rs::chop::substring("a", 0, 0), "a"); 338 | assert_eq!(voca_rs::chop::substring("a", 10, 0), ""); 339 | assert_eq!(voca_rs::chop::substring("a", 0, 1), "a"); 340 | assert_eq!(voca_rs::chop::substring("a", 0, 10), "a"); 341 | assert_eq!(voca_rs::chop::substring("ab", 1, 0), "b"); 342 | assert_eq!(voca_rs::chop::substring("abcd", 3, 1), ""); 343 | assert_eq!(voca_rs::chop::substring("abcd", 3, 3), ""); 344 | assert_eq!(voca_rs::chop::substring("abcd", 3, 4), "d"); 345 | assert_eq!(voca_rs::chop::substring("abcd", 3, 10), "d"); 346 | assert_eq!(voca_rs::chop::substring("abcd", 30, 1), ""); 347 | assert_eq!(voca_rs::chop::substring("ab", 2, 0), ""); 348 | assert_eq!(voca_rs::chop::substring("ab", 3, 0), ""); 349 | assert_eq!(voca_rs::chop::substring("ab", 20, 20), ""); 350 | assert_eq!(voca_rs::chop::substring("a", 20, 10), ""); 351 | assert_eq!(voca_rs::chop::substring("helicopter", 1, 0), "elicopter"); 352 | assert_eq!(voca_rs::chop::substring("b\u{0142}\u{0105}d", 2, 4), "ąd"); 353 | assert_eq!(voca_rs::chop::substring("über das Floß.", 0, 1), "ü"); 354 | assert_eq!(voca_rs::chop::substring("e\u{0301}", 1, 0), "\u{0301}"); 355 | } 356 | #[test] 357 | fn _substring() { 358 | assert_eq!("helicopter"._substring(1, 0), "elicopter"); 359 | } 360 | #[test] 361 | fn truncate() { 362 | assert_eq!(voca_rs::chop::truncate("", 0, ""), ""); 363 | assert_eq!(voca_rs::chop::truncate("a", 1, ""), "a"); 364 | assert_eq!(voca_rs::chop::truncate("a", 2, ""), "a"); 365 | assert_eq!(voca_rs::chop::truncate("a", 3, ""), "a"); 366 | assert_eq!(voca_rs::chop::truncate("a", 4, ""), "a"); 367 | assert_eq!(voca_rs::chop::truncate("a", 5, ""), "a"); 368 | assert_eq!(voca_rs::chop::truncate("a", 10, ""), "a"); 369 | assert_eq!( 370 | voca_rs::chop::truncate("Once upon a time", 7, ""), 371 | "Once..." 372 | ); 373 | assert_eq!( 374 | voca_rs::chop::truncate("Die Schildkröte fliegt über das Floß.", 28, "(...)"), 375 | "Die Schildkröte fliegt (...)" 376 | ); 377 | assert_eq!(voca_rs::chop::truncate("Once upon", 10, ""), "Once upon"); 378 | assert_eq!( 379 | voca_rs::chop::truncate("Как слышно, приём!", 13, ""), 380 | "Как слышно..." 381 | ); 382 | } 383 | #[test] 384 | fn _truncate() { 385 | assert_eq!("Once upon a time"._truncate(7, ""), "Once..."); 386 | } 387 | #[test] 388 | fn limit_words() { 389 | assert_eq!(voca_rs::chop::limit_words("", 0, ""), ""); 390 | assert_eq!(voca_rs::chop::limit_words("a", 0, ""), ""); 391 | assert_eq!(voca_rs::chop::limit_words("a", 1, ""), "a"); 392 | assert_eq!(voca_rs::chop::limit_words("a", 2, ""), "a"); 393 | assert_eq!(voca_rs::chop::limit_words("a", 10, ""), "a"); 394 | assert_eq!( 395 | voca_rs::chop::limit_words("Once upon a time", 1, ""), 396 | "Once..." 397 | ); 398 | assert_eq!( 399 | voca_rs::chop::limit_words("Die Schildkröte, fliegt über das Floß.", 2, "(...)"), 400 | "Die Schildkröte(...)" 401 | ); 402 | assert_eq!(voca_rs::chop::limit_words("Once upon", 10, ""), "Once upon"); 403 | assert_eq!( 404 | voca_rs::chop::limit_words("Как слышно, приём!", 2, ""), 405 | "Как слышно..." 406 | ); 407 | } 408 | #[test] 409 | fn _limit_words() { 410 | assert_eq!("Once upon a time"._limit_words(1, ""), "Once..."); 411 | } 412 | #[test] 413 | fn max() { 414 | assert_eq!(voca_rs::chop::max(""), ""); 415 | assert_eq!(voca_rs::chop::max("rain"), "r"); 416 | assert_eq!(voca_rs::chop::max("cafe\u{0301}"), "\u{0301}"); 417 | assert_eq!(voca_rs::chop::max("café"), "\u{0301}"); 418 | assert_eq!(voca_rs::chop::max("b\u{0142}\u{0105}d"), "ł"); 419 | assert_eq!(voca_rs::chop::max("über das Floß."), "ü"); 420 | assert_eq!(voca_rs::chop::max("a̐éö̲"), "\u{332}"); 421 | } 422 | #[test] 423 | fn _max() { 424 | assert_eq!("rain"._max_code_point(), "r"); 425 | } 426 | #[test] 427 | fn min() { 428 | assert_eq!(voca_rs::chop::min(""), ""); 429 | assert_eq!(voca_rs::chop::min("rain"), "a"); 430 | assert_eq!(voca_rs::chop::min("cafe\u{0301}"), "a"); 431 | assert_eq!(voca_rs::chop::min("café"), "a"); 432 | assert_eq!(voca_rs::chop::min("b\u{0142}\u{0105}d"), "b"); 433 | assert_eq!(voca_rs::chop::min("Über das Floß."), " "); 434 | assert_eq!(voca_rs::chop::min("a̐éö̲"), "a"); 435 | } 436 | #[test] 437 | fn _min() { 438 | assert_eq!("rain"._min_code_point(), "a"); 439 | } 440 | -------------------------------------------------------------------------------- /tests/unit/query.rs: -------------------------------------------------------------------------------- 1 | //! voca_rs::query testing 2 | use voca_rs::Voca; 3 | 4 | #[test] 5 | fn ends_with() { 6 | assert!(voca_rs::query::ends_with("the world is yours", "is yours")); 7 | assert!(voca_rs::query::ends_with("Zażółć gęślą jaźń", "jaźń")); 8 | assert!(voca_rs::query::ends_with("the world is yours", "")); 9 | assert!(voca_rs::query::ends_with("", "")); 10 | } 11 | #[test] 12 | fn _ends_with() { 13 | assert!("the world is yours"._ends_with("is yours")); 14 | assert!("Zażółć gęślą jaźń"._ends_with("jaźń")); 15 | } 16 | #[test] 17 | fn includes() { 18 | assert!(voca_rs::query::includes("", "", 0)); 19 | assert!(voca_rs::query::includes("a", "a", 0)); 20 | assert!(voca_rs::query::includes("abc", "c", 2)); 21 | assert!(voca_rs::query::includes( 22 | "the world is yours", 23 | "the world", 24 | 0 25 | )); 26 | assert!(voca_rs::query::includes("Zażółć gęślą jaźń", "gęślą", 7)); 27 | assert!(voca_rs::query::includes("the world is yours", "", 0)); 28 | assert_eq!(voca_rs::query::includes("abc", "c", 20), false); 29 | assert_eq!(voca_rs::query::includes("abc", "z", 0), false); 30 | } 31 | #[test] 32 | fn _includes() { 33 | assert!("Zażółć gęślą jaźń"._includes("gęślą", 7)); 34 | assert!("abc"._includes("c", 2)); 35 | } 36 | #[test] 37 | fn is_alpha() { 38 | assert!(voca_rs::query::is_alpha("bart")); 39 | assert!(voca_rs::query::is_alpha("café")); 40 | assert!(voca_rs::query::is_alpha("cafe\u{0301}")); 41 | assert!(voca_rs::query::is_alpha("Zażółć")); 42 | assert_eq!(voca_rs::query::is_alpha(""), false); 43 | assert_eq!(voca_rs::query::is_alpha("T1000"), false); 44 | assert_eq!(voca_rs::query::is_alpha("\n\t"), false); 45 | assert_eq!(voca_rs::query::is_alpha("lisa!"), false); 46 | assert_eq!(voca_rs::query::is_alpha("lisa and bart"), false); 47 | assert_eq!(voca_rs::query::is_alpha("Zażółć gęślą jaźń"), false); 48 | } 49 | #[test] 50 | fn _is_alpha() { 51 | assert!("bart"._is_alpha()); 52 | assert!("café"._is_alpha()); 53 | assert!("cafe\u{0301}"._is_alpha()); 54 | } 55 | #[test] 56 | fn is_alphadigit() { 57 | assert!(voca_rs::query::is_alphadigit("bart")); 58 | assert!(voca_rs::query::is_alphadigit("café")); 59 | assert!(voca_rs::query::is_alphadigit("cafe\u{0301}")); 60 | assert!(voca_rs::query::is_alphadigit("T1000")); 61 | assert!(voca_rs::query::is_alphadigit("1000")); 62 | assert!(voca_rs::query::is_alphadigit("Zażółć")); 63 | assert_eq!(voca_rs::query::is_alphadigit(""), false); 64 | assert_eq!(voca_rs::query::is_alphadigit("10-00"), false); 65 | assert_eq!(voca_rs::query::is_alphadigit("\n\t"), false); 66 | assert_eq!(voca_rs::query::is_alphadigit("lisa!"), false); 67 | assert_eq!(voca_rs::query::is_alphadigit("lisa and bart"), false); 68 | assert_eq!(voca_rs::query::is_alphadigit("Zażółć gęślą jaźń"), false); 69 | } 70 | #[test] 71 | fn _is_alphadigit() { 72 | assert!("bart"._is_alphadigit()); 73 | assert_eq!("10-00"._is_alphadigit(), false); 74 | assert!("T1000"._is_alphadigit()); 75 | } 76 | #[test] 77 | fn is_blank() { 78 | assert!(voca_rs::query::is_blank("")); 79 | assert!(voca_rs::query::is_blank(" ")); 80 | assert!(voca_rs::query::is_blank("\n\t\r")); 81 | assert_eq!(voca_rs::query::is_blank("Zażółć gęślą jaźń"), false); 82 | } 83 | #[test] 84 | fn _is_blank() { 85 | assert!(" "._is_blank()); 86 | assert_eq!("Zażółć gęślą jaźń"._is_blank(), false); 87 | } 88 | #[test] 89 | fn is_camel_case() { 90 | assert!(voca_rs::query::is_camel_case("")); 91 | assert!(voca_rs::query::is_camel_case("birdFlight")); 92 | assert_eq!(voca_rs::query::is_camel_case("bird flight"), false); 93 | assert_eq!(voca_rs::query::is_camel_case("-BIRD-FLIGHT-"), false); 94 | assert_eq!(voca_rs::query::is_camel_case("Zażółć gęślą jaźń"), false); 95 | assert!(voca_rs::query::is_camel_case("zażółćGęśląJaźń")); 96 | } 97 | #[test] 98 | fn _is_camel_case() { 99 | assert!("birdFlight"._is_camel_case()); 100 | assert_eq!("bird flight"._is_camel_case(), false); 101 | assert!("zażółćGęśląJaźń"._is_camel_case()); 102 | } 103 | #[test] 104 | fn is_capitalize() { 105 | assert!(voca_rs::query::is_capitalize("")); 106 | assert!(voca_rs::query::is_capitalize("John has a motorcycle")); 107 | assert_eq!(voca_rs::query::is_capitalize("the world is yours"), false); 108 | assert!(voca_rs::query::is_capitalize("The world is yours")); 109 | assert_eq!(voca_rs::query::is_capitalize("The World IS YourS"), false); 110 | assert!(voca_rs::query::is_capitalize("Zażółć gęślą jaźń")); 111 | assert_eq!(voca_rs::query::is_capitalize("ZAżółć GĘŚLĄ jAźń"), false); 112 | assert!(voca_rs::query::is_capitalize("Это вообще работает?"),); 113 | assert_eq!(voca_rs::query::is_capitalize("это Вообще РАБОТАЕТ?"), false); 114 | } 115 | #[test] 116 | fn _is_capitalize() { 117 | assert!("John has a motorcycle"._is_capitalize()); 118 | assert_eq!("the world is yours"._is_capitalize(), false); 119 | } 120 | #[test] 121 | fn is_decapitalize() { 122 | assert!(voca_rs::query::is_decapitalize("")); 123 | assert_eq!( 124 | voca_rs::query::is_decapitalize("John has a motorcycle"), 125 | false 126 | ); 127 | assert!(voca_rs::query::is_decapitalize("the world is yours")); 128 | assert_eq!(voca_rs::query::is_decapitalize("The world is yours"), false); 129 | assert_eq!(voca_rs::query::is_decapitalize("the World IS YourS"), false); 130 | assert!(voca_rs::query::is_decapitalize("zażółć gęślą jaźń")); 131 | assert_eq!(voca_rs::query::is_decapitalize("ZAżółć GĘŚLĄ jAźń"), false); 132 | assert!(voca_rs::query::is_decapitalize("это вообще работает?")); 133 | assert_eq!( 134 | voca_rs::query::is_decapitalize("Это вообще работает?"), 135 | false 136 | ); 137 | } 138 | #[test] 139 | fn _is_decapitalize() { 140 | assert_eq!("John has a motorcycle"._is_decapitalize(), false); 141 | assert!("the world is yours"._is_decapitalize()); 142 | } 143 | #[test] 144 | fn is_digit() { 145 | assert!(voca_rs::query::is_digit("")); 146 | assert!(voca_rs::query::is_digit("0")); 147 | assert!(voca_rs::query::is_digit("100")); 148 | assert!(voca_rs::query::is_digit("100500")); 149 | assert_eq!(voca_rs::query::is_digit("1.5"), false); 150 | assert_eq!(voca_rs::query::is_digit("0xFF"), false); 151 | assert_eq!(voca_rs::query::is_digit("ten"), false); 152 | } 153 | #[test] 154 | fn _is_digit() { 155 | assert!("0"._is_digit()); 156 | assert_eq!("1.5"._is_digit(), false); 157 | } 158 | #[test] 159 | fn is_empty() { 160 | assert!(voca_rs::query::is_empty("")); 161 | assert_eq!(voca_rs::query::is_empty("Zażółć gęślą jaźń"), false); 162 | assert_eq!(voca_rs::query::is_empty("the world is yours"), false); 163 | } 164 | #[test] 165 | fn _is_empty() { 166 | assert!(""._is_empty()); 167 | assert_eq!("the world is yours"._is_empty(), false); 168 | } 169 | #[test] 170 | fn is_foreign_key() { 171 | assert!(voca_rs::query::is_foreign_key("")); 172 | assert!(voca_rs::query::is_foreign_key("foo_bar_id")); 173 | assert_eq!(voca_rs::query::is_foreign_key("foo_bar"), false); 174 | assert_eq!(voca_rs::query::is_foreign_key("the world is yours"), false); 175 | assert_eq!( 176 | voca_rs::query::is_foreign_key("foo-bar-string-that-is-really-really-long"), 177 | false 178 | ); 179 | assert_eq!( 180 | voca_rs::query::is_foreign_key("FooBarIsAReallyReallyLongString"), 181 | false 182 | ); 183 | assert_eq!( 184 | voca_rs::query::is_foreign_key("foo_bar_string_that_is_really_really_long"), 185 | false 186 | ); 187 | assert_eq!( 188 | voca_rs::query::is_foreign_key("voca::voca_rs::query::is_foreign_key"), 189 | false 190 | ); 191 | } 192 | #[test] 193 | fn _is_foreign_key() { 194 | assert!(""._is_foreign_key()); 195 | assert!("foo_bar_id"._is_foreign_key()); 196 | assert_eq!("foo_bar"._is_foreign_key(), false); 197 | assert_eq!("the world is yours"._is_foreign_key(), false); 198 | assert_eq!( 199 | "foo-bar-string-that-is-really-really-long"._is_foreign_key(), 200 | false 201 | ); 202 | } 203 | #[test] 204 | fn is_lowercase() { 205 | assert!(voca_rs::query::is_lowercase("")); 206 | assert!(voca_rs::query::is_lowercase("the world is yours")); 207 | assert_eq!(voca_rs::query::is_lowercase("Zażółć gęślą jaźń"), false); 208 | assert_eq!(voca_rs::query::is_lowercase("T1000"), false); 209 | } 210 | #[test] 211 | fn _is_lowercase() { 212 | assert!("the world is yours"._is_lowercase()); 213 | assert_eq!("T1000"._is_lowercase(), false); 214 | } 215 | #[test] 216 | fn is_lower_first() { 217 | assert!(voca_rs::query::is_lower_first("")); 218 | assert!(voca_rs::query::is_lower_first("the world is yours")); 219 | assert!(voca_rs::query::is_lower_first("tHE World")); 220 | assert!(voca_rs::query::is_lower_first("żółć niedźwiedzia")); 221 | assert_eq!(voca_rs::query::is_lower_first("Zażółć gęślą jaźń"), false); 222 | assert_eq!(voca_rs::query::is_lower_first("T1000"), false); 223 | } 224 | #[test] 225 | fn _is_lower_first() { 226 | assert!("the world is yours"._is_lower_first()); 227 | assert_eq!("Zażółć gęślą jaźń"._is_lower_first(), false); 228 | } 229 | #[test] 230 | fn is_kebab_case() { 231 | assert!(voca_rs::query::is_kebab_case("")); 232 | assert!(voca_rs::query::is_kebab_case("bird-flight")); 233 | assert!(voca_rs::query::is_kebab_case("is-kebab-case")); 234 | assert!(voca_rs::query::is_kebab_case("zażółć-gęślą-jaźń")); 235 | assert_eq!(voca_rs::query::is_kebab_case("-BIRD-FLIGHT-"), false); 236 | assert_eq!(voca_rs::query::is_kebab_case("tHE World"), false); 237 | assert_eq!(voca_rs::query::is_kebab_case("żółć niedźwiedzia"), false); 238 | assert_eq!(voca_rs::query::is_kebab_case("T1000"), false); 239 | } 240 | #[test] 241 | fn _is_kebab_case() { 242 | assert!("bird-flight"._is_kebab_case()); 243 | assert_eq!("-BIRD-FLIGHT-"._is_kebab_case(), false); 244 | } 245 | #[test] 246 | fn is_numeric() { 247 | assert!(voca_rs::query::is_numeric("")); 248 | assert!(voca_rs::query::is_numeric("0")); 249 | assert!(voca_rs::query::is_numeric("+0")); 250 | assert!(voca_rs::query::is_numeric("0.0")); 251 | assert!(voca_rs::query::is_numeric("1000")); 252 | assert!(voca_rs::query::is_numeric("1.56")); 253 | assert!(voca_rs::query::is_numeric("-10.888")); 254 | assert!(voca_rs::query::is_numeric("350")); 255 | assert!(voca_rs::query::is_numeric("-20.5")); 256 | assert!(voca_rs::query::is_numeric("1.5E+2")); 257 | assert!(voca_rs::query::is_numeric("1.25E-3")); 258 | assert!(voca_rs::query::is_numeric("125e5")); 259 | assert!(voca_rs::query::is_numeric("125e-3")); 260 | assert!(voca_rs::query::is_numeric("0xFF")); 261 | assert!(voca_rs::query::is_numeric("0x22")); 262 | assert!(voca_rs::query::is_numeric("0x123ABC")); 263 | assert!(voca_rs::query::is_numeric("0x123ABC")); 264 | assert!(voca_rs::query::is_numeric("0x1ab9")); 265 | assert_eq!(voca_rs::query::is_numeric("0x123z"), false); 266 | assert_eq!(voca_rs::query::is_numeric("five"), false); 267 | assert_eq!(voca_rs::query::is_numeric(".."), false); 268 | assert_eq!(voca_rs::query::is_numeric(" "), false); 269 | } 270 | #[test] 271 | fn _is_numeric() { 272 | assert!("0"._is_numeric()); 273 | assert!("1.5E+2"._is_numeric()); 274 | assert!("0x22"._is_numeric()); 275 | assert_eq!("0x123z"._is_numeric(), false); 276 | } 277 | #[test] 278 | fn is_pascal_case() { 279 | assert!(voca_rs::query::is_pascal_case("")); 280 | assert!(voca_rs::query::is_pascal_case("BirdFlight")); 281 | assert!(voca_rs::query::is_pascal_case("ЭтоПаскальКейс")); 282 | assert_eq!(voca_rs::query::is_pascal_case("birdFlight"), false); 283 | assert_eq!(voca_rs::query::is_pascal_case("bird flight"), false); 284 | assert_eq!(voca_rs::query::is_pascal_case("-BIRD-FLIGHT-"), false); 285 | assert_eq!(voca_rs::query::is_pascal_case("Zażółć gęślą jaźń"), false); 286 | assert!(voca_rs::query::is_pascal_case("ZażółćGęśląJaźń")); 287 | assert_eq!(voca_rs::query::is_pascal_case("zażółćGęśląJaźń"), false); 288 | } 289 | #[test] 290 | fn _is_pascal_case() { 291 | assert!("BirdFlight"._is_pascal_case()); 292 | assert_eq!("birdFlight"._is_pascal_case(), false); 293 | } 294 | #[test] 295 | fn is_shouty_kebab_case() { 296 | assert!(voca_rs::query::is_shouty_kebab_case("")); 297 | assert!(voca_rs::query::is_shouty_kebab_case("BIRD-FLIGHT")); 298 | assert!(voca_rs::query::is_shouty_kebab_case("ЭТО-ОЙ-КЕБАБ-КЕЙС")); 299 | assert_eq!(voca_rs::query::is_shouty_kebab_case("birdFlight"), false); 300 | assert_eq!(voca_rs::query::is_shouty_kebab_case("bird flight"), false); 301 | assert_eq!(voca_rs::query::is_shouty_kebab_case("-BIRD-FLIGHT-"), false); 302 | assert_eq!( 303 | voca_rs::query::is_shouty_kebab_case("Zażółć gęślą jaźń"), 304 | false 305 | ); 306 | assert!(voca_rs::query::is_shouty_kebab_case("ZAŻÓŁĆ-GĘŚLĄ-JAŹŃ")); 307 | assert_eq!( 308 | voca_rs::query::is_shouty_kebab_case("zażółćGęśląJaźń"), 309 | false 310 | ); 311 | } 312 | #[test] 313 | fn _is_shouty_kebab_case() { 314 | assert!("BIRD-FLIGHT"._is_shouty_kebab_case()); 315 | assert_eq!("birdFlight"._is_shouty_kebab_case(), false); 316 | } 317 | #[test] 318 | fn is_snake_case() { 319 | assert!(voca_rs::query::is_snake_case("")); 320 | assert!(voca_rs::query::is_snake_case("bird_flight")); 321 | assert!(voca_rs::query::is_snake_case("это_снэйк_кейс")); 322 | assert_eq!(voca_rs::query::is_snake_case("birdFlight"), false); 323 | assert_eq!(voca_rs::query::is_snake_case("bird flight"), false); 324 | assert_eq!(voca_rs::query::is_snake_case("-BIRD-FLIGHT-"), false); 325 | assert_eq!(voca_rs::query::is_snake_case("Zażółć gęślą jaźń"), false); 326 | assert!(voca_rs::query::is_snake_case("zażółć_gęślą_jaźń")); 327 | assert_eq!(voca_rs::query::is_snake_case("zażółćGęśląJaźń"), false); 328 | } 329 | #[test] 330 | fn _is_snake_case() { 331 | assert!("bird_flight"._is_snake_case()); 332 | assert_eq!("birdFlight"._is_snake_case(), false); 333 | } 334 | #[test] 335 | fn is_shouty_snake_case() { 336 | assert!(voca_rs::query::is_shouty_snake_case("")); 337 | assert!(voca_rs::query::is_shouty_snake_case("BIRD_FLIGHT")); 338 | assert_eq!(voca_rs::query::is_shouty_snake_case("bird_flight"), false); 339 | assert!(voca_rs::query::is_shouty_snake_case("ЭТО_СНЭЙК_КЕЙС")); 340 | assert_eq!(voca_rs::query::is_shouty_snake_case("birdFlight"), false); 341 | assert_eq!(voca_rs::query::is_shouty_snake_case("bird flight"), false); 342 | assert_eq!(voca_rs::query::is_shouty_snake_case("-BIRD-FLIGHT-"), false); 343 | assert_eq!( 344 | voca_rs::query::is_shouty_snake_case("Zażółć gęślą jaźń"), 345 | false 346 | ); 347 | assert!(voca_rs::query::is_shouty_snake_case("ZAŻÓŁĆ_GĘŚLĄ_JAŹŃ")); 348 | assert_eq!( 349 | voca_rs::query::is_shouty_snake_case("zażółćGęśląJaźń"), 350 | false 351 | ); 352 | } 353 | #[test] 354 | fn _is_shouty_snake_case() { 355 | assert!("BIRD_FLIGHT"._is_shouty_snake_case()); 356 | assert_eq!("bird_flight"._is_shouty_snake_case(), false); 357 | } 358 | #[test] 359 | fn is_title() { 360 | assert_eq!(voca_rs::query::is_title(""), false); 361 | assert_eq!(voca_rs::query::is_title("The World Is Yours"), true); 362 | assert_eq!(voca_rs::query::is_title("the world is yours"), false); 363 | assert!(voca_rs::query::is_title("This Is String Example...Wow!!!")); 364 | assert_eq!( 365 | voca_rs::query::is_title("This is string example....wow!!!"), 366 | false 367 | ); 368 | assert!(voca_rs::query::is_title("Zażółć Gęślą Jaźń")); 369 | assert_eq!(voca_rs::query::is_title("Zażółć gęślą jaźń"), false); 370 | assert_eq!(voca_rs::query::is_title("T1000"), true); 371 | } 372 | #[test] 373 | fn _is_title() { 374 | assert_eq!("The World Is Yours"._is_title(), true); 375 | assert_eq!("the world is yours"._is_title(), false); 376 | } 377 | #[test] 378 | fn is_train_case() { 379 | assert!(voca_rs::query::is_train_case("")); 380 | assert!(voca_rs::query::is_train_case("Goodbye-Blue-Sky")); 381 | assert_eq!(voca_rs::query::is_train_case("bird_flight"), false); 382 | assert!(voca_rs::query::is_train_case("Это-Снэйк-Кейс")); 383 | assert_eq!(voca_rs::query::is_train_case("birdFlight"), false); 384 | assert_eq!(voca_rs::query::is_train_case("bird flight"), false); 385 | assert_eq!(voca_rs::query::is_train_case("-BIRD-FLIGHT-"), false); 386 | assert_eq!(voca_rs::query::is_train_case("Zażółć gęślą jaźń"), false); 387 | assert!(voca_rs::query::is_train_case("Zażółć-Gęślą-Jaźń")); 388 | assert_eq!(voca_rs::query::is_train_case("zażółćGęśląJaźń"), false); 389 | } 390 | #[test] 391 | fn _is_train_case() { 392 | assert!(voca_rs::query::is_train_case("Goodbye-Blue-Sky")); 393 | assert_eq!(voca_rs::query::is_train_case("bird_flight"), false); 394 | } 395 | #[test] 396 | fn is_uppercase() { 397 | assert!(voca_rs::query::is_uppercase("")); 398 | assert!(voca_rs::query::is_uppercase("THE WORLD IS YOURS")); 399 | assert_eq!(voca_rs::query::is_uppercase("Zażółć gęślą jaźń"), false); 400 | assert_eq!(voca_rs::query::is_uppercase("t1000"), false); 401 | } 402 | #[test] 403 | fn _is_uppercase() { 404 | assert!("THE WORLD IS YOURS"._is_uppercase()); 405 | assert_eq!("Zażółć gęślą jaźń"._is_uppercase(), false); 406 | } 407 | #[test] 408 | fn is_upper_first() { 409 | assert!(voca_rs::query::is_upper_first("")); 410 | assert!(voca_rs::query::is_upper_first("The world is yours")); 411 | assert!(voca_rs::query::is_upper_first("Zażółć gęślą jaźń")); 412 | assert!(voca_rs::query::is_upper_first("T1000")); 413 | assert!(voca_rs::query::is_upper_first("Żółć niedźwiedzia")); 414 | assert_eq!(voca_rs::query::is_upper_first("żółć niedźwiedzia"), false); 415 | assert_eq!(voca_rs::query::is_upper_first("tHE World"), false); 416 | } 417 | #[test] 418 | fn _is_upper_first() { 419 | assert!(voca_rs::query::is_upper_first("The world is yours")); 420 | assert_eq!(voca_rs::query::is_upper_first("żółć niedźwiedzia"), false); 421 | } 422 | #[test] 423 | fn matches() { 424 | assert!(voca_rs::query::matches("", "", 0)); 425 | assert_eq!(voca_rs::query::matches("pluto", "a", 0), false); 426 | assert!(voca_rs::query::matches("pluto", r"plu.{2}", 0)); 427 | assert_eq!(voca_rs::query::matches("apollo 11", r"\d{3}", 0), false); 428 | assert!(voca_rs::query::matches("pacific", "", 0)); 429 | assert_eq!(voca_rs::query::matches("", "1", 0), false); 430 | assert!(voca_rs::query::matches("pacific ocean", "ocean", 0)); 431 | assert!(voca_rs::query::matches( 432 | "pacific ocean", 433 | "^pacific ocean$", 434 | 0 435 | )); 436 | assert!(voca_rs::query::matches("pacific ocean", r"\s", 0)); 437 | assert!(voca_rs::query::matches("1500", r"\d", 0)); 438 | assert!(voca_rs::query::matches("Zażółć gęślą jaźń", "gęślą", 0)); 439 | assert!(voca_rs::query::matches("Zażółć gęślą jaźń", "gęślą", 11)); 440 | assert_eq!( 441 | voca_rs::query::matches("Zażółć gęślą jaźń", "gęślą", 12), 442 | false 443 | ); 444 | assert_eq!( 445 | // [^] is not a valid regex 446 | voca_rs::query::matches("Zażółć gęślą jaźń", "[^]", 0), 447 | false 448 | ); 449 | } 450 | #[test] 451 | fn _matches() { 452 | assert_eq!("pluto"._matches("a", 0), false); 453 | assert!("pluto"._matches(r"plu.{2}", 0)); 454 | } 455 | #[test] 456 | fn query() { 457 | assert!(voca_rs::query::query("", "", 0)); 458 | assert!(voca_rs::query::query("a", "a", 0)); 459 | assert!(voca_rs::query::query("abc", "c", 2)); 460 | assert!(voca_rs::query::query("the world is yours", "the world", 0)); 461 | assert!(voca_rs::query::query("the world is yours", "te wld", 0)); 462 | assert!(voca_rs::query::query("the world is yours", "td", 0)); 463 | assert!(voca_rs::query::query("Zażółć gęślą jaźń", "gęślą", 7)); 464 | assert!(voca_rs::query::query("the world is yours", "", 0)); 465 | assert_eq!( 466 | voca_rs::query::query("the world is yours", "asdd", 0), 467 | false 468 | ); 469 | assert_eq!(voca_rs::query::query("the world is yours", "eht", 0), false); 470 | assert_eq!(voca_rs::query::query("abc", "c", 20), false); 471 | assert_eq!(voca_rs::query::query("abc", "z", 0), false); 472 | } 473 | #[test] 474 | fn _query() { 475 | assert!("the world is yours"._query("the world", 0)); 476 | assert_eq!("the world is yours"._query("eht", 0), false); 477 | } 478 | #[test] 479 | fn starts_with() { 480 | assert!(voca_rs::query::starts_with( 481 | "the world is yours", 482 | "the world" 483 | )); 484 | assert!(voca_rs::query::starts_with("Zażółć gęślą jaźń", "Zażółć")); 485 | assert!(voca_rs::query::starts_with("the world is yours", "")); 486 | assert!(voca_rs::query::starts_with("", "")); 487 | assert_eq!( 488 | voca_rs::query::starts_with("the world is yours", "s"), 489 | false 490 | ); 491 | } 492 | #[test] 493 | fn _starts_with() { 494 | assert!("the world is yours"._starts_with("the world")); 495 | assert_eq!("the world is yours"._starts_with("s"), false); 496 | } 497 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![crate_name = "voca_rs"] 2 | #![deny( 3 | warnings, 4 | unused_variables, 5 | missing_docs, 6 | unused_extern_crates 7 | )] 8 | #![forbid(unsafe_code)] 9 | #![cfg_attr(all(test, feature = "nightly"), feature(test))] 10 | 11 | //! Voca_rs is the ultimate Rust string library inspired by Voca.js and string.py 12 | //! 13 | //! Using functions: 14 | //! ```rust 15 | //! use voca_rs::*; 16 | //! let input_string = "LazyLoad with XMLHttpRequest and snake_case"; 17 | //! let string_in_words = split::words(&input_string); 18 | //! // => ["Lazy", "Load", "with", "XML", "Http", "Request", "and", "snake", "case"] 19 | //! let words_in_string = &string_in_words.join(" "); 20 | //! // => "Lazy Load with XML Http Request and snake case" 21 | //! let truncated_string = chop::prune(&words_in_string, 21, ""); 22 | //! // => "Lazy Load with XML..." 23 | //! let sliced_string = chop::slice(&truncated_string, 5, -2); 24 | //! // => "Load with XML." 25 | //! let snaked_string = case::snake_case(&sliced_string); 26 | //! // => "load_with_xml" 27 | //! ``` 28 | //! 29 | //! Using traits (all methods start from the underscore symbol): 30 | //! ```rust 31 | //! use voca_rs::Voca; 32 | //! "LazyLoad with XMLHttpRequest and snake_case" 33 | //! ._words() 34 | //! // => ["Lazy", "Load", "with", "XML", "Http", "Request", "and", "snake", "case"] 35 | //! .join(" ") 36 | //! // => "Lazy Load with XML Http Request and snake case" 37 | //! ._prune(21, "") 38 | //! // => "Lazy Load with XML..." 39 | //! ._slice(5, -2) 40 | //! // => "Load with XML." 41 | //! ._snake_case(); 42 | //! // => "load_with_xml" 43 | //! ``` 44 | 45 | extern crate regex; 46 | extern crate stfu8; 47 | extern crate unicode_segmentation; 48 | 49 | // #[macro_use] 50 | // extern crate lazy_static; 51 | 52 | pub mod case; 53 | pub mod chop; 54 | pub mod count; 55 | pub mod escape; 56 | pub mod index; 57 | pub mod manipulate; 58 | pub mod query; 59 | pub mod split; 60 | pub mod strip; 61 | pub mod utils; 62 | 63 | #[allow(missing_docs)] 64 | pub trait Voca { 65 | // case 66 | fn _camel_case(&self) -> String; 67 | fn _capitalize(&self, param: bool) -> String; 68 | fn _decapitalize(&self, param: bool) -> String; 69 | fn _kebab_case(&self) -> String; 70 | fn _shouty_kebab_case(&self) -> String; 71 | fn _lower_case(&self) -> String; 72 | fn _pascal_case(&self) -> String; 73 | fn _snake_case(&self) -> String; 74 | fn _shouty_snake_case(&self) -> String; 75 | fn _swap_case(&self) -> String; 76 | fn _title_case(&self) -> String; 77 | fn _train_case(&self) -> String; 78 | fn _upper_case(&self) -> String; 79 | fn _lower_first(&self) -> String; 80 | fn _upper_first(&self) -> String; 81 | // chop 82 | fn _after(&self, param: &str) -> String; 83 | fn _after_last(&self, param: &str) -> String; 84 | fn _before(&self, param: &str) -> String; 85 | fn _before_last(&self, param: &str) -> String; 86 | fn _char_at(&self, param: usize) -> String; 87 | fn _code_point_at(&self, param: usize) -> Vec; 88 | fn _first(&self, param: usize) -> String; 89 | fn _foreign_key(&self) -> String; 90 | fn _grapheme_at(&self, param: usize) -> String; 91 | fn _last(&self, param: usize) -> String; 92 | fn _prune(&self, param1: usize, param2: &str) -> String; 93 | fn _removeprefix(&self, param2: &str) -> String; 94 | fn _removesuffix(&self, param2: &str) -> String; 95 | fn _slice(&self, param1: isize, param2: isize) -> String; 96 | fn _substr(&self, param1: usize, param2: usize) -> String; 97 | fn _substring(&self, param1: usize, param2: usize) -> String; 98 | fn _truncate(&self, param1: usize, param2: &str) -> String; 99 | fn _limit_words(&self, param1: usize, param2: &str) -> String; 100 | fn _max_code_point(&self) -> String; 101 | fn _min_code_point(&self) -> String; 102 | // count 103 | fn _count(&self) -> usize; 104 | fn _count_graphemes(&self) -> usize; 105 | fn _count_substrings(&self, param1: &str) -> usize; 106 | fn _count_where(&self, param2: fn(&str) -> bool) -> usize; 107 | fn _count_words(&self, param1: &str) -> usize; 108 | fn _count_unique_words(&self, param1: &str) -> usize; 109 | // escape 110 | fn _escape_html(&self) -> String; 111 | fn _escape_regexp(&self) -> String; 112 | fn _unescape_html(&self) -> String; 113 | // index 114 | fn _index_all(&self, param1: &str, param2: usize) -> Vec; 115 | fn _index_of(&self, param1: &str, param2: usize) -> i8; 116 | fn _last_index_of(&self, param1: &str, param2: usize) -> i8; 117 | fn _search(&self, param1: &str, param2: usize) -> i8; 118 | // manipulate 119 | fn _expand_tabs(&self, param1: usize) -> String; 120 | fn _expand_spaces(&self, param1: usize) -> String; 121 | fn _finish(&self, param1: &str) -> String; 122 | fn _insert(&self, param1: &str, param2: usize) -> String; 123 | fn _latinise(&self) -> String; 124 | fn _pad(&self, param1: usize, param2: &str) -> String; 125 | fn _pad_left(&self, param1: usize, param2: &str) -> String; 126 | fn _pad_right(&self, param1: usize, param2: &str) -> String; 127 | fn _repeat(&self, param1: usize) -> String; 128 | fn _replace(&self, param1: &str, param2: &str) -> String; 129 | fn _replace_all(&self, param1: &str, param2: &str) -> String; 130 | fn _reverse(&self) -> String; 131 | fn _reverse_grapheme(&self) -> String; 132 | fn _slugify(&self) -> String; 133 | fn _splice(&self, param1: isize, param2: usize, param3: &str) -> String; 134 | fn _start(&self, param1: &str) -> String; 135 | fn _trim(&self, param1: &str) -> String; 136 | fn _trim_right(&self, param1: &str) -> String; 137 | fn _trim_left(&self, param1: &str) -> String; 138 | fn _zfill(&self, param1: usize) -> String; 139 | fn _tr(&self, param1: &str, param2: &str) -> String; 140 | fn _word_wrap(&self, param1: usize, param2: &str, param3: &str) -> String; 141 | // query 142 | fn _is_foreign_key(&self) -> bool; 143 | fn _ends_with(&self, param1: &str) -> bool; 144 | fn _includes(&self, param1: &str, param2: usize) -> bool; 145 | fn _is_alpha(&self) -> bool; 146 | fn _is_alphadigit(&self) -> bool; 147 | fn _is_blank(&self) -> bool; 148 | fn _is_camel_case(&self) -> bool; 149 | fn _is_capitalize(&self) -> bool; 150 | fn _is_decapitalize(&self) -> bool; 151 | fn _is_digit(&self) -> bool; 152 | fn _is_empty(&self) -> bool; 153 | fn _is_lowercase(&self) -> bool; 154 | fn _is_lower_first(&self) -> bool; 155 | fn _is_kebab_case(&self) -> bool; 156 | fn _is_numeric(&self) -> bool; 157 | fn _is_pascal_case(&self) -> bool; 158 | fn _is_shouty_kebab_case(&self) -> bool; 159 | fn _is_snake_case(&self) -> bool; 160 | fn _is_shouty_snake_case(&self) -> bool; 161 | fn _is_title(&self) -> bool; 162 | fn _is_train_case(&self) -> bool; 163 | fn _is_uppercase(&self) -> bool; 164 | fn _is_upper_first(&self) -> bool; 165 | fn _matches(&self, param1: &str, param2: usize) -> bool; 166 | fn _query(&self, param1: &str, param2: usize) -> bool; 167 | fn _starts_with(&self, param1: &str) -> bool; 168 | // split 169 | fn _chars(&self) -> Vec<&str>; 170 | fn _split(&self, param1: &str) -> Vec<&str>; 171 | fn _words(&self) -> Vec<&str>; 172 | fn _graphemes(&self) -> Vec<&str>; 173 | fn _code_points(&self) -> Vec; 174 | // strip 175 | fn _strip_bom(&self) -> String; 176 | fn _strip_tags(&self) -> String; 177 | } 178 | 179 | macro_rules! implement_string_for { 180 | ( $trt:ident; $($typ:ident), *) => { 181 | $( 182 | impl $trt for $typ { 183 | // case 184 | fn _camel_case(&self) -> String { 185 | case::camel_case(&self) 186 | } 187 | fn _capitalize(&self, param: bool) -> String { 188 | case::capitalize(&self, param) 189 | } 190 | fn _decapitalize(&self, param: bool) -> String { 191 | case::decapitalize(&self, param) 192 | } 193 | fn _kebab_case(&self) -> String { 194 | case::kebab_case(&self) 195 | } 196 | fn _shouty_kebab_case(&self) -> String { 197 | case::shouty_kebab_case(&self) 198 | } 199 | fn _lower_case(&self) -> String { 200 | case::lower_case(&self) 201 | } 202 | fn _pascal_case(&self) -> String { 203 | case::pascal_case(&self) 204 | } 205 | fn _snake_case(&self) -> String { 206 | case::snake_case(&self) 207 | } 208 | fn _shouty_snake_case(&self) -> String { 209 | case::shouty_snake_case(&self) 210 | } 211 | fn _swap_case(&self) -> String { 212 | case::swap_case(&self) 213 | } 214 | fn _title_case(&self) -> String { 215 | case::title_case(&self) 216 | } 217 | fn _train_case(&self) -> String { 218 | case::train_case(&self) 219 | } 220 | fn _upper_case(&self) -> String { 221 | case::upper_case(&self) 222 | } 223 | fn _lower_first(&self) -> String { 224 | case::lower_first(&self) 225 | } 226 | fn _upper_first(&self) -> String { 227 | case::upper_first(&self) 228 | } 229 | // chop 230 | fn _after(&self, param: &str) -> String { 231 | chop::after(&self, param) 232 | } 233 | fn _after_last(&self, param: &str) -> String { 234 | chop::after_last(&self, param) 235 | } 236 | fn _before(&self, param: &str) -> String { 237 | chop::before(&self, param) 238 | } 239 | fn _before_last(&self, param: &str) -> String { 240 | chop::before_last(&self, param) 241 | } 242 | fn _char_at(&self, param: usize) -> String { 243 | chop::char_at(&self, param) 244 | } 245 | fn _code_point_at(&self, param: usize) -> Vec { 246 | chop::code_point_at(&self, param) 247 | } 248 | fn _first(&self, param: usize) -> String { 249 | chop::first(&self, param) 250 | } 251 | fn _foreign_key(&self) -> String { 252 | chop::foreign_key(&self) 253 | } 254 | fn _grapheme_at(&self, param: usize) -> String { 255 | chop::grapheme_at(&self, param) 256 | } 257 | fn _last(&self, param: usize) -> String { 258 | chop::last(&self, param) 259 | } 260 | fn _prune(&self, param1: usize, param2: &str) -> String { 261 | chop::prune(&self, param1, param2) 262 | } 263 | fn _removeprefix(&self, param2: &str) -> String { 264 | chop::removeprefix(&self, param2) 265 | } 266 | fn _removesuffix(&self, param2: &str) -> String { 267 | chop::removesuffix(&self, param2) 268 | } 269 | fn _slice(&self, param1: isize, param2: isize) -> String { 270 | chop::slice(&self, param1, param2) 271 | } 272 | fn _substr(&self, param1: usize, param2: usize) -> String { 273 | chop::substr(&self, param1, param2) 274 | } 275 | fn _substring(&self, param1: usize, param2: usize) -> String { 276 | chop::substring(&self, param1, param2) 277 | } 278 | fn _truncate(&self, param1: usize, param2: &str) -> String { 279 | chop::truncate(&self, param1, param2) 280 | } 281 | fn _limit_words(&self, param1: usize, param2: &str) -> String { 282 | chop::limit_words(&self, param1, param2) 283 | } 284 | fn _max_code_point(&self) -> String { 285 | chop::max(&self) 286 | } 287 | fn _min_code_point(&self) -> String { 288 | chop::min(&self) 289 | } 290 | // count 291 | fn _count(&self) -> usize { 292 | count::count(&self) 293 | } 294 | fn _count_graphemes(&self) -> usize { 295 | count::count_graphemes(&self) 296 | } 297 | fn _count_substrings(&self, param1: &str) -> usize { 298 | count::count_substrings(&self, param1) 299 | } 300 | fn _count_where(&self, param1: fn(&str) -> bool) -> usize { 301 | count::count_where(&self, param1) 302 | } 303 | fn _count_words(&self, param1: &str) -> usize { 304 | count::count_words(&self, param1) 305 | } 306 | fn _count_unique_words(&self, param1: &str) -> usize { 307 | count::count_unique_words(&self, param1) 308 | } 309 | // escape 310 | fn _escape_html(&self) -> String { 311 | escape::escape_html(&self) 312 | } 313 | fn _escape_regexp(&self) -> String { 314 | escape::escape_regexp(&self) 315 | } 316 | fn _unescape_html(&self) -> String { 317 | escape::unescape_html(&self) 318 | } 319 | // index 320 | fn _index_all(&self, param1: &str, param2: usize) -> Vec { 321 | index::index_all(&self, param1, param2) 322 | } 323 | fn _index_of(&self, param1: &str, param2: usize) -> i8 { 324 | index::index_of(&self, param1, param2) 325 | } 326 | fn _last_index_of(&self, param1: &str, param2: usize) -> i8 { 327 | index::last_index_of(&self, param1, param2) 328 | } 329 | fn _search(&self, param1: &str, param2: usize) -> i8 { 330 | index::search(&self, param1, param2) 331 | } 332 | // manipulate 333 | fn _expand_tabs(&self, param1: usize) -> String { 334 | manipulate::expand_tabs(&self, param1) 335 | } 336 | fn _expand_spaces(&self, param1: usize) -> String { 337 | manipulate::expand_spaces(&self, param1) 338 | } 339 | fn _finish(&self, param1: &str) -> String { 340 | manipulate::finish(&self, param1) 341 | } 342 | fn _insert(&self, param1: &str, param2: usize) -> String { 343 | manipulate::insert(&self, param1, param2) 344 | } 345 | fn _latinise(&self) -> String { 346 | manipulate::latinise(&self) 347 | } 348 | fn _pad(&self, param1: usize, param2: &str ) -> String { 349 | manipulate::pad(&self, param1, param2) 350 | } 351 | fn _pad_left(&self, param1: usize, param2: &str ) -> String { 352 | manipulate::pad_left(&self, param1, param2) 353 | } 354 | fn _pad_right(&self, param1: usize, param2: &str ) -> String { 355 | manipulate::pad_right(&self, param1, param2) 356 | } 357 | fn _repeat(&self, param1: usize) -> String { 358 | manipulate::repeat(&self, param1) 359 | } 360 | fn _replace(&self, param1: &str, param2: &str ) -> String { 361 | manipulate::replace(&self, param1, param2) 362 | } 363 | fn _replace_all(&self, param1: &str, param2: &str) -> String { 364 | manipulate::replace_all(&self, param1, param2) 365 | } 366 | fn _reverse(&self) -> String { 367 | manipulate::reverse(&self) 368 | } 369 | fn _reverse_grapheme(&self) -> String { 370 | manipulate::reverse_grapheme(&self) 371 | } 372 | fn _slugify(&self) -> String { 373 | manipulate::slugify(&self) 374 | } 375 | fn _splice(&self, param1: isize, param2: usize, param3: &str) -> String { 376 | manipulate::splice(&self, param1, param2, param3) 377 | } 378 | fn _start(&self, param1: &str) -> String { 379 | manipulate::start(&self, param1) 380 | } 381 | fn _trim(&self, param1: &str) -> String { 382 | manipulate::trim(&self, param1) 383 | } 384 | fn _trim_right(&self, param1: &str) -> String { 385 | manipulate::trim_right(&self, param1) 386 | } 387 | fn _trim_left(&self, param1: &str) -> String { 388 | manipulate::trim_left(&self, param1) 389 | } 390 | fn _zfill(&self, param1: usize) -> String { 391 | manipulate::zfill(&self, param1) 392 | } 393 | fn _tr(&self, param1: &str, param2: &str) -> String { 394 | manipulate::tr(&self, param1, param2) 395 | } 396 | fn _word_wrap(&self, param1: usize, param2: &str, param3: &str) -> String { 397 | manipulate::word_wrap(&self, param1, param2, param3) 398 | } 399 | // query 400 | fn _is_foreign_key(&self) -> bool { 401 | query::is_foreign_key(&self) 402 | } 403 | fn _ends_with(&self, param1: &str) -> bool { 404 | query::ends_with(&self, param1) 405 | } 406 | fn _includes(&self, param1: &str, param2: usize) -> bool { 407 | query::includes(&self, param1, param2) 408 | } 409 | fn _is_alpha(&self) -> bool { 410 | query::is_alpha(&self) 411 | } 412 | fn _is_alphadigit(&self) -> bool { 413 | query::is_alphadigit(&self) 414 | } 415 | fn _is_blank(&self) -> bool { 416 | query::is_blank(&self) 417 | } 418 | fn _is_camel_case(&self) -> bool { 419 | query::is_camel_case(&self) 420 | } 421 | fn _is_capitalize(&self) -> bool { 422 | query::is_capitalize(&self) 423 | } 424 | fn _is_decapitalize(&self) -> bool { 425 | query::is_decapitalize(&self) 426 | } 427 | fn _is_digit(&self) -> bool { 428 | query::is_digit(&self) 429 | } 430 | fn _is_empty(&self) -> bool { 431 | query::is_empty(&self) 432 | } 433 | fn _is_lowercase(&self) -> bool { 434 | query::is_lowercase(&self) 435 | } 436 | fn _is_lower_first(&self) -> bool { 437 | query::is_lower_first(&self) 438 | } 439 | fn _is_kebab_case(&self) -> bool { 440 | query::is_kebab_case(&self) 441 | } 442 | fn _is_numeric(&self) -> bool { 443 | query::is_numeric(&self) 444 | } 445 | fn _is_pascal_case(&self) -> bool { 446 | query::is_pascal_case(&self) 447 | } 448 | fn _is_shouty_kebab_case(&self) -> bool { 449 | query::is_shouty_kebab_case(&self) 450 | } 451 | fn _is_snake_case(&self) -> bool { 452 | query::is_snake_case(&self) 453 | } 454 | fn _is_shouty_snake_case(&self) -> bool { 455 | query::is_shouty_snake_case(&self) 456 | } 457 | fn _is_title(&self) -> bool { 458 | query::is_title(&self) 459 | } 460 | fn _is_train_case(&self) -> bool { 461 | query::is_train_case(&self) 462 | } 463 | fn _is_uppercase(&self) -> bool { 464 | query::is_uppercase(&self) 465 | } 466 | fn _is_upper_first(&self) -> bool { 467 | query::is_upper_first(&self) 468 | } 469 | fn _matches(&self, param1: &str, param2: usize) -> bool { 470 | query::matches(&self, param1, param2) 471 | } 472 | fn _query(&self, param1: &str, param2: usize) -> bool { 473 | query::query(&self, param1, param2) 474 | } 475 | fn _starts_with(&self, param1: &str) -> bool { 476 | query::starts_with(&self, param1) 477 | } 478 | // split 479 | fn _chars(&self) -> Vec<&str> { 480 | split::chars(&self) 481 | } 482 | fn _split(&self, param1: &str) -> Vec<&str> { 483 | split::split(&self, param1) 484 | } 485 | fn _words(&self) -> Vec<&str> { 486 | split::words(&self) 487 | } 488 | fn _graphemes(&self) -> Vec<&str> { 489 | split::graphemes(&self) 490 | } 491 | fn _code_points(&self) -> Vec { 492 | split::code_points(&self) 493 | } 494 | // strip 495 | fn _strip_bom(&self) -> String { 496 | strip::strip_bom(&self) 497 | } 498 | fn _strip_tags(&self) -> String { 499 | strip::strip_tags(&self) 500 | } 501 | } 502 | )* 503 | } 504 | } 505 | 506 | implement_string_for![ 507 | Voca; 508 | String, str 509 | ]; 510 | -------------------------------------------------------------------------------- /src/query.rs: -------------------------------------------------------------------------------- 1 | //! Checks a `subject` against a query. 2 | 3 | use regex::Regex; 4 | /// Checks whether `subject` ends with `end`. 5 | /// 6 | /// # Arguments 7 | /// 8 | /// * `subject` - The string to verify. 9 | /// * `end` - The ending string. 10 | /// 11 | /// # Example 12 | /// 13 | /// ``` 14 | /// use voca_rs::*; 15 | /// query::ends_with("say hello to my little friend", "little friend"); 16 | /// // => true 17 | /// query::ends_with("say hello to my little friend", "little"); 18 | /// // => false 19 | /// use voca_rs::Voca; 20 | /// "say hello to my little friend"._ends_with("little friend"); 21 | /// // => true 22 | /// ``` 23 | pub fn ends_with(subject: &str, end: &str) -> bool { 24 | if subject.is_empty() || end.is_empty() { 25 | return true; 26 | } 27 | subject.ends_with(end) 28 | } 29 | 30 | /// Checks whether `subject` includes `search` starting from `position`. 31 | /// 32 | /// # Arguments 33 | /// 34 | /// * `subject` - The string to verify. 35 | /// * `search` - The ending string. 36 | /// * `position` - The position to start searching. 37 | /// 38 | /// # Example 39 | /// 40 | /// ``` 41 | /// use voca_rs::*; 42 | /// query::includes("starship", "star", 0); 43 | /// // => true 44 | /// query::includes("Zażółć gęślą jaźń", "gęślą", 7); 45 | /// // => true 46 | /// query::includes("galaxy", "g", 1); 47 | /// // => false 48 | /// use voca_rs::Voca; 49 | /// "starship"._includes("star", 0); 50 | /// // => true 51 | /// ``` 52 | pub fn includes(subject: &str, search: &str, position: usize) -> bool { 53 | let subject_len = crate::count::count(subject); 54 | if subject_len < position { 55 | return false; 56 | } 57 | if subject_len == 0 || search.is_empty() { 58 | return true; 59 | } 60 | subject.to_owned()[subject.char_indices().nth(position).unwrap().0..] 61 | .to_string() 62 | .contains(search) 63 | } 64 | 65 | /// Checks whether `subject` contains only alpha characters. 66 | /// 67 | /// # Arguments 68 | /// 69 | /// * `subject` - The string to verify. 70 | /// 71 | /// # Example 72 | /// 73 | /// ``` 74 | /// use voca_rs::*; 75 | /// query::is_alpha(""); 76 | /// // => false 77 | /// query::is_alpha("cafe\u{0301}"); // or "café" 78 | /// // => true 79 | /// query::is_alpha("bart"); 80 | /// // => true 81 | /// query::is_alpha("lisa!"); 82 | /// // => false 83 | /// query::is_alpha("Zażółć and bart"); 84 | /// // => false 85 | /// use voca_rs::Voca; 86 | /// "bart"._is_alpha(); 87 | /// // => true 88 | /// ``` 89 | pub fn is_alpha(subject: &str) -> bool { 90 | if is_empty(subject) { 91 | false 92 | } else { 93 | is_alpha_or_alphadigit(subject, false) 94 | } 95 | } 96 | 97 | fn is_alpha_or_alphadigit(subject: &str, count_digits: bool) -> bool { 98 | let mut subject_is_ok = true; 99 | let subject_grapheme_len = crate::count::count_graphemes(subject); 100 | let mut current_pos = 0; 101 | while current_pos < subject_grapheme_len { 102 | let current_char = crate::chop::grapheme_at(subject, current_pos); 103 | if (!count_digits 104 | && (is_digit(¤t_char) 105 | || is_blank(¤t_char) 106 | || crate::utils::PUNCTUATION.contains(¤t_char))) 107 | || (count_digits 108 | && (is_blank(¤t_char) || crate::utils::PUNCTUATION.contains(¤t_char))) 109 | { 110 | subject_is_ok = false; 111 | current_pos = subject_grapheme_len; 112 | } else { 113 | current_pos += 1; 114 | } 115 | } 116 | subject_is_ok 117 | } 118 | 119 | /// Checks whether `subject` contains contains only alpha and digit characters. 120 | /// 121 | /// # Arguments 122 | /// 123 | /// * `subject` - The string to verify. 124 | /// 125 | /// # Example 126 | /// 127 | /// ``` 128 | /// use voca_rs::*; 129 | /// query::is_alphadigit(""); 130 | /// // => false 131 | /// query::is_alphadigit("cafe\u{0301}"); // or "café" 132 | /// // => true 133 | /// query::is_alphadigit("year2020"); 134 | /// // => true 135 | /// query::is_alphadigit("1448"); 136 | /// // => true 137 | /// query::is_alphadigit("40-20"); 138 | /// // => false 139 | /// use voca_rs::Voca; 140 | /// "year2020"._is_alphadigit(); 141 | /// // => true 142 | /// ``` 143 | pub fn is_alphadigit(subject: &str) -> bool { 144 | if is_empty(subject) { 145 | false 146 | } else { 147 | is_alpha_or_alphadigit(subject, true) 148 | } 149 | } 150 | 151 | /// Checks whether `subject` is empty or contains only whitespaces. 152 | /// 153 | /// # Arguments 154 | /// 155 | /// * `subject` - The string to verify. 156 | /// 157 | /// # Example 158 | /// 159 | /// ``` 160 | /// use voca_rs::*; 161 | /// query::is_blank(""); 162 | /// // => true 163 | /// query::is_blank(" "); 164 | /// // => true 165 | /// query::is_blank("sun"); 166 | /// // => false 167 | /// use voca_rs::Voca; 168 | /// " "._is_blank(); 169 | /// // => true 170 | /// ``` 171 | pub fn is_blank(subject: &str) -> bool { 172 | if subject.is_empty() { 173 | return true; 174 | } 175 | 176 | subject.trim().is_empty() 177 | } 178 | 179 | /// Checks whether `subject` is camelCased. 180 | /// 181 | /// # Arguments 182 | /// 183 | /// * `subject` - The string to verify. 184 | /// 185 | /// # Example 186 | /// 187 | /// ``` 188 | /// use voca_rs::*; 189 | /// query::is_camel_case(""); 190 | /// // => true 191 | /// query::is_camel_case("birdFlight"); 192 | /// // => true 193 | /// query::is_camel_case("bird flight"); 194 | /// // => false 195 | /// query::is_camel_case("-BIRD-FLIGHT-"); 196 | /// // => false 197 | /// use voca_rs::Voca; 198 | /// "birdFlight"._is_camel_case(); 199 | /// // => true 200 | /// ``` 201 | pub fn is_camel_case(subject: &str) -> bool { 202 | subject == crate::case::camel_case(subject) 203 | } 204 | 205 | /// Checks whether `subject` is capitalized and the rest of `subject` is converted to lower case. 206 | /// 207 | /// # Arguments 208 | /// 209 | /// * `subject` - The string to verify. 210 | /// 211 | /// # Example 212 | /// 213 | /// ``` 214 | /// use voca_rs::*; 215 | /// query::is_capitalize(""); 216 | /// // => true 217 | /// query::is_capitalize("John has a motorcycle"); 218 | /// // => true 219 | /// query::is_capitalize("the world is yours"); 220 | /// // => false 221 | /// query::is_capitalize("Żółć niedźwiedzia"); 222 | /// // => true 223 | /// use voca_rs::Voca; 224 | /// "John has a motorcycle"._is_capitalize(); 225 | /// // => true 226 | /// ``` 227 | pub fn is_capitalize(subject: &str) -> bool { 228 | is_capitalize_or_decapitalize(subject, true) 229 | } 230 | 231 | /// Checks whether `subject` is decapitalized and the rest of `subject` is converted to lower case. 232 | /// 233 | /// # Arguments 234 | /// 235 | /// * `subject` - The string to verify. 236 | /// 237 | /// # Example 238 | /// 239 | /// ``` 240 | /// use voca_rs::*; 241 | /// query::is_decapitalize(""); 242 | /// // => true 243 | /// query::is_decapitalize("John has a motorcycle"); 244 | /// // => false 245 | /// query::is_decapitalize("the world is yours"); 246 | /// // => true 247 | /// query::is_decapitalize("Żółć niedźwiedzia"); 248 | /// // => false 249 | /// use voca_rs::Voca; 250 | /// "the world is yours"._is_decapitalize(); 251 | /// // => true 252 | /// ``` 253 | pub fn is_decapitalize(subject: &str) -> bool { 254 | is_capitalize_or_decapitalize(subject, false) 255 | } 256 | 257 | fn is_capitalize_or_decapitalize(subject: &str, if_capitalize: bool) -> bool { 258 | match subject.len() { 259 | 0 => true, 260 | _ => { 261 | let first_letter = crate::chop::first(subject, 1); 262 | let the_rest = crate::chop::slice(subject, 1, 0); 263 | let first_letter_to_check = if if_capitalize { 264 | is_uppercase(&first_letter) 265 | } else { 266 | is_lowercase(&first_letter) 267 | }; 268 | first_letter_to_check && is_lowercase(&the_rest) 269 | } 270 | } 271 | } 272 | /// Checks whether `subject` contains only digit characters. 273 | /// 274 | /// # Arguments 275 | /// 276 | /// * `subject` - The string to verify. 277 | /// 278 | /// # Example 279 | /// 280 | /// ``` 281 | /// use voca_rs::*; 282 | /// query::is_digit("35"); 283 | /// // => true 284 | /// query::is_digit("1.5"); 285 | /// // => false 286 | /// query::is_digit("0xFF"); 287 | /// // => false 288 | /// query::is_digit("ten"); 289 | /// // => false 290 | /// use voca_rs::Voca; 291 | /// "35"._is_digit(); 292 | /// // => true 293 | /// ``` 294 | pub fn is_digit(subject: &str) -> bool { 295 | let subject_len = subject.len(); 296 | if subject_len == 0 { 297 | return true; 298 | } 299 | 300 | crate::split::chars(subject) 301 | .iter() 302 | .filter(|c| { 303 | let mut current_char = String::new(); 304 | current_char.push_str(c); 305 | crate::utils::DIGITS.contains(¤t_char) 306 | }) 307 | .count() 308 | == subject_len 309 | } 310 | 311 | /// Checks whether `subject` is empty. 312 | /// 313 | /// # Arguments 314 | /// 315 | /// * `subject` - The string to verify. 316 | /// 317 | /// # Example 318 | /// 319 | /// ``` 320 | /// use voca_rs::*; 321 | /// query::is_empty(""); 322 | /// // => true 323 | /// query::is_empty(" "); 324 | /// // => false 325 | /// query::is_empty("sun"); 326 | /// // => false 327 | /// use voca_rs::Voca; 328 | /// ""._is_empty(); 329 | /// // => true 330 | /// ``` 331 | pub fn is_empty(subject: &str) -> bool { 332 | if subject.is_empty() { 333 | return true; 334 | } 335 | 336 | false 337 | } 338 | 339 | /// Checks whether `subject` is is a `foreign_key`. 340 | /// 341 | /// # Arguments 342 | /// 343 | /// * `subject` - The string to verify. 344 | /// 345 | /// # Example 346 | /// 347 | /// ``` 348 | /// use voca_rs::*; 349 | /// query::is_foreign_key(""); 350 | /// // => true 351 | /// query::is_foreign_key("foo_bar_id"); 352 | /// // => true 353 | /// query::is_foreign_key("foo_bar"); 354 | /// // => false 355 | /// use voca_rs::Voca; 356 | /// "foo_bar_id"._is_foreign_key(); 357 | /// // => true 358 | /// "foo_bar"._is_foreign_key(); 359 | /// // => false 360 | /// ``` 361 | pub fn is_foreign_key(subject: &str) -> bool { 362 | subject == crate::chop::foreign_key(subject) 363 | } 364 | 365 | /// Checks whether `subject` has only lower case characters. 366 | /// 367 | /// # Arguments 368 | /// 369 | /// * `subject` - The string to verify. 370 | /// 371 | /// # Example 372 | /// 373 | /// ``` 374 | /// use voca_rs::*; 375 | /// query::is_lowercase("motorcycle"); 376 | /// // => true 377 | /// query::is_lowercase("John"); 378 | /// // => false 379 | /// query::is_lowercase("T1000"); 380 | /// // => false 381 | /// use voca_rs::Voca; 382 | /// "motorcycle"._is_lowercase(); 383 | /// // => true 384 | /// ``` 385 | pub fn is_lowercase(subject: &str) -> bool { 386 | is_upper_or_lowercase(subject, true) 387 | } 388 | 389 | /// Checks whether `subject` has the first character in lower case. 390 | /// 391 | /// # Arguments 392 | /// 393 | /// * `subject` - The string to verify. 394 | /// 395 | /// # Example 396 | /// 397 | /// ``` 398 | /// use voca_rs::*; 399 | /// query::is_lower_first("motorcycle"); 400 | /// // => true 401 | /// query::is_lower_first("John"); 402 | /// // => false 403 | /// query::is_lower_first("T1000"); 404 | /// // => false 405 | /// query::is_lower_first("żółć niedźwiedzia"); 406 | /// // => true 407 | /// use voca_rs::Voca; 408 | /// "motorcycle"._is_lower_first(); 409 | /// // => true 410 | /// ``` 411 | pub fn is_lower_first(subject: &str) -> bool { 412 | match subject.len() { 413 | 0 => true, 414 | _ => { 415 | let first_letter = crate::split::chars(subject)[0]; 416 | is_upper_or_lowercase(first_letter, true) 417 | } 418 | } 419 | } 420 | 421 | /// Checks whether `subject` is kebab-cased. 422 | /// 423 | /// # Arguments 424 | /// 425 | /// * `subject` - The string to verify. 426 | /// 427 | /// # Example 428 | /// 429 | /// ``` 430 | /// use voca_rs::*; 431 | /// query::is_kebab_case(""); 432 | /// // => true 433 | /// query::is_kebab_case("bird-flight"); 434 | /// // => true 435 | /// query::is_kebab_case("bird flight"); 436 | /// // => false 437 | /// query::is_kebab_case("-BIRD-FLIGHT-"); 438 | /// // => false 439 | /// use voca_rs::Voca; 440 | /// "bird-flight"._is_kebab_case(); 441 | /// // => true 442 | /// ``` 443 | pub fn is_kebab_case(subject: &str) -> bool { 444 | subject == crate::case::kebab_case(subject) 445 | } 446 | 447 | /// Checks whether `subject` is numeric. 448 | /// 449 | /// # Arguments 450 | /// 451 | /// * `subject` - The string to verify. 452 | /// 453 | /// # Example 454 | /// 455 | /// ``` 456 | /// use voca_rs::*; 457 | /// query::is_numeric("350"); 458 | /// // => true 459 | /// query::is_numeric("-20.5"); 460 | /// // => true 461 | /// query::is_numeric("0xFF"); 462 | /// // => true 463 | /// query::is_numeric("1.5E+2"); 464 | /// // => true 465 | /// query::is_numeric("five"); 466 | /// // => false 467 | /// use voca_rs::Voca; 468 | /// "350"._is_numeric(); 469 | /// // => true 470 | /// ``` 471 | pub fn is_numeric(subject: &str) -> bool { 472 | if subject.is_empty() { 473 | return true; 474 | } 475 | 476 | fn parse_str_num(n: &str) -> bool { 477 | match n.find('.') { 478 | Some(_) => n.parse::().is_ok(), 479 | None => n.parse::().is_ok(), 480 | } 481 | } 482 | 483 | let sbj = subject.to_lowercase(); 484 | match subject.to_lowercase().find('e') { 485 | Some(_) => { 486 | let s: Vec<&str> = sbj.split('e').collect(); 487 | parse_str_num(s[0]) && parse_str_num(s[1]) 488 | } 489 | None => { 490 | if starts_with(&subject.to_lowercase(), "0x") { 491 | let s = sbj.trim_start_matches("0x"); 492 | i32::from_str_radix(s, 16).is_ok() 493 | } else { 494 | parse_str_num(subject) 495 | } 496 | } 497 | } 498 | } 499 | 500 | /// Checks whether `subject` is PascalCased. 501 | /// 502 | /// # Arguments 503 | /// 504 | /// * `subject` - The string to verify. 505 | /// 506 | /// # Example 507 | /// 508 | /// ``` 509 | /// use voca_rs::*; 510 | /// query::is_pascal_case(""); 511 | /// // => true 512 | /// query::is_pascal_case("BirdFlight"); 513 | /// // => true 514 | /// query::is_pascal_case("bird flight"); 515 | /// // => false 516 | /// query::is_pascal_case("-BIRD-FLIGHT-"); 517 | /// // => false 518 | /// use voca_rs::Voca; 519 | /// "BirdFlight"._is_pascal_case(); 520 | /// // => true 521 | /// ``` 522 | pub fn is_pascal_case(subject: &str) -> bool { 523 | subject == crate::case::pascal_case(subject) 524 | } 525 | 526 | /// Checks whether `subject` is SHOUTY-KEBAB-CASED. 527 | /// 528 | /// # Arguments 529 | /// 530 | /// * `subject` - The string to verify. 531 | /// 532 | /// # Example 533 | /// 534 | /// ``` 535 | /// use voca_rs::*; 536 | /// query::is_shouty_kebab_case(""); 537 | /// // => true 538 | /// query::is_shouty_kebab_case("BIRD-FLIGHT"); 539 | /// // => true 540 | /// query::is_shouty_kebab_case("bird flight"); 541 | /// // => false 542 | /// query::is_shouty_kebab_case("-BIRD-FLIGHT-"); 543 | /// // => false 544 | /// use voca_rs::Voca; 545 | /// "BIRD-FLIGHT"._is_shouty_kebab_case(); 546 | /// // => true 547 | /// ``` 548 | pub fn is_shouty_kebab_case(subject: &str) -> bool { 549 | subject == crate::case::shouty_kebab_case(subject) 550 | } 551 | 552 | /// Checks whether `subject` is snake_cased. 553 | /// 554 | /// # Arguments 555 | /// 556 | /// * `subject` - The string to verify. 557 | /// 558 | /// # Example 559 | /// 560 | /// ``` 561 | /// use voca_rs::*; 562 | /// query::is_snake_case(""); 563 | /// // => true 564 | /// query::is_snake_case("bird_flight"); 565 | /// // => true 566 | /// query::is_snake_case("bird flight"); 567 | /// // => false 568 | /// query::is_snake_case("-BIRD-FLIGHT-"); 569 | /// // => false 570 | /// use voca_rs::Voca; 571 | /// "bird_flight"._is_snake_case(); 572 | /// // => true 573 | /// ``` 574 | pub fn is_snake_case(subject: &str) -> bool { 575 | subject == crate::case::snake_case(subject) 576 | } 577 | 578 | /// Checks whether `subject` is SHOUTY_SNAKE_CASED. 579 | /// 580 | /// # Arguments 581 | /// 582 | /// * `subject` - The string to verify. 583 | /// 584 | /// # Example 585 | /// 586 | /// ``` 587 | /// use voca_rs::*; 588 | /// query::is_shouty_snake_case(""); 589 | /// // => true 590 | /// query::is_shouty_snake_case("BIRD_FLIGHT"); 591 | /// // => true 592 | /// query::is_shouty_snake_case("bird flight"); 593 | /// // => false 594 | /// query::is_shouty_snake_case("-BIRD-FLIGHT-"); 595 | /// // => false 596 | /// use voca_rs::Voca; 597 | /// "BIRD_FLIGHT"._is_shouty_snake_case(); 598 | /// // => true 599 | /// ``` 600 | pub fn is_shouty_snake_case(subject: &str) -> bool { 601 | subject == crate::case::shouty_snake_case(subject) 602 | } 603 | 604 | /// Checks whether `subject` is a titlecased string and there is at least one character. 605 | /// 606 | /// # Arguments 607 | /// 608 | /// * `subject` - The string to verify. 609 | /// 610 | /// # Example 611 | /// 612 | /// ``` 613 | /// use voca_rs::*; 614 | /// query::is_title("This Is String Example...Wow!!!"); 615 | /// // => true 616 | /// query::is_title("This is string example....wow!!!"); 617 | /// // => false 618 | /// use voca_rs::Voca; 619 | /// "This Is String Example...Wow!!!"._is_title(); 620 | /// // => true 621 | /// ``` 622 | pub fn is_title(subject: &str) -> bool { 623 | if subject.is_empty() { 624 | return false; 625 | } 626 | let words = crate::split::words(subject); 627 | let subject_len = words.len(); 628 | words 629 | .iter() 630 | .filter(|w| { 631 | let mut res = String::with_capacity(w.len()); 632 | for (i, c) in crate::split::chars(w).iter().enumerate() { 633 | if (i == 0 && c == &c.to_uppercase()) || (i > 0 && c == &c.to_lowercase()) { 634 | res.push_str(c) 635 | } 636 | } 637 | res.len() == w.len() 638 | }) 639 | .count() 640 | == subject_len 641 | } 642 | 643 | /// Checks whether `subject` is Train-Cased. 644 | /// 645 | /// # Arguments 646 | /// 647 | /// * `subject` - The string to verify. 648 | /// 649 | /// # Example 650 | /// 651 | /// ``` 652 | /// use voca_rs::*; 653 | /// query::is_train_case(""); 654 | /// // => true 655 | /// query::is_train_case("Goodbye-Blue-Sky"); 656 | /// // => true 657 | /// query::is_train_case("bird flight"); 658 | /// // => false 659 | /// query::is_train_case("-BIRD-FLIGHT-"); 660 | /// // => false 661 | /// use voca_rs::Voca; 662 | /// "Goodbye-Blue-Sky"._is_train_case(); 663 | /// // => true 664 | /// ``` 665 | pub fn is_train_case(subject: &str) -> bool { 666 | subject == crate::case::train_case(subject) 667 | } 668 | 669 | /// Checks whether `subject` has only upper case characters. 670 | /// 671 | /// # Arguments 672 | /// 673 | /// * `subject` - The string to verify. 674 | /// 675 | /// # Example 676 | /// 677 | /// ``` 678 | /// use voca_rs::*; 679 | /// query::is_uppercase("ACDC"); 680 | /// // => true 681 | /// query::is_uppercase("Morning"); 682 | /// // => false 683 | /// use voca_rs::Voca; 684 | /// "ACDC"._is_uppercase(); 685 | /// // => true 686 | /// ``` 687 | pub fn is_uppercase(subject: &str) -> bool { 688 | is_upper_or_lowercase(subject, false) 689 | } 690 | 691 | /// Checks whether `subject` has the first character in upper case. 692 | /// 693 | /// # Arguments 694 | /// 695 | /// * `subject` - The string to verify. 696 | /// 697 | /// # Example 698 | /// 699 | /// ``` 700 | /// use voca_rs::*; 701 | /// query::is_upper_first("motorcycle"); 702 | /// // => false 703 | /// query::is_upper_first("John"); 704 | /// // => true 705 | /// query::is_upper_first("T1000"); 706 | /// // => true 707 | /// query::is_upper_first("Żółć niedźwiedzia"); 708 | /// // => true 709 | /// use voca_rs::Voca; 710 | /// "John"._is_upper_first(); 711 | /// // => true 712 | /// ``` 713 | pub fn is_upper_first(subject: &str) -> bool { 714 | match subject.len() { 715 | 0 => true, 716 | _ => { 717 | let first_letter = crate::split::chars(subject)[0]; 718 | is_upper_or_lowercase(first_letter, false) 719 | } 720 | } 721 | } 722 | 723 | fn is_upper_or_lowercase(subject: &str, lowercase: bool) -> bool { 724 | if subject.is_empty() { 725 | return true; 726 | } 727 | 728 | let mut res = true; 729 | crate::split::chars(subject).into_iter().for_each(|s| { 730 | s.chars().for_each(|c| { 731 | if (lowercase && c.is_uppercase()) || (!lowercase && c.is_lowercase()) { 732 | res = false; 733 | } 734 | }) 735 | }); 736 | res 737 | } 738 | 739 | /// Checks whether `subject` matches the regular expression `pattern`. 740 | /// NOTE: Executes regular expressions only on valid UTF-8 while exposing match locations as byte indices into the search string (see case #4). 741 | /// # Arguments 742 | /// 743 | /// * `subject` - The string to verify. 744 | /// * `pattern` - The RegExp pattern to match, it is transformed to Regex::new(pattern). 745 | /// * `position` - The position to start matching. 746 | /// 747 | /// # Example 748 | /// 749 | /// ``` 750 | /// use voca_rs::*; 751 | /// query::matches("pluto", "a", 0); 752 | /// // => false 753 | /// query::matches("pluto", r"plu.{2}", 0); 754 | /// // => true 755 | /// query::matches("apollo 11", r"\d{3}", 0); 756 | /// // => false 757 | /// query::matches("Zażółć gęślą jaźń", "gęślą", 11); 758 | /// // => true (because "gęślą" starts from 11 not 7) 759 | /// use voca_rs::Voca; 760 | /// "pluto"._matches(r"plu.{2}", 0); 761 | /// // => true 762 | /// ``` 763 | pub fn matches(subject: &str, pattern: &str, position: usize) -> bool { 764 | let subject_len = crate::split::chars(subject).len(); 765 | if subject_len == 0 { 766 | return false; 767 | } 768 | if position >= subject_len { 769 | return false; 770 | } 771 | match pattern.len() { 772 | 0 => true, 773 | _ => { 774 | let re: Regex = match Regex::new(pattern) { 775 | Ok(re) => re, 776 | Err(_) => return false, 777 | }; 778 | re.is_match_at(subject, position) 779 | } 780 | } 781 | } 782 | 783 | /// Checks whether `subject` contains all characters from `search` starting from `position`. Respects an order of characters. 784 | /// 785 | /// # Arguments 786 | /// 787 | /// * `subject` - The string to verify. 788 | /// * `search` - The ending string. 789 | /// * `position` - The position to start searching. 790 | /// 791 | /// # Example 792 | /// 793 | /// ``` 794 | /// use voca_rs::*; 795 | /// query::query("starship", "star", 0); 796 | /// // => true 797 | /// query::query("the world is yours", "te wld", 0); 798 | /// // => true 799 | /// query::query("galaxy", "g", 1); 800 | /// // => false 801 | /// use voca_rs::Voca; 802 | /// "starship"._query("star", 0); 803 | /// // => true 804 | /// ``` 805 | pub fn query(subject: &str, search: &str, position: usize) -> bool { 806 | let subject_len = crate::count::count(subject); 807 | if subject_len < position { 808 | return false; 809 | } 810 | if subject_len == 0 || search.is_empty() { 811 | return true; 812 | } 813 | let mut i: usize = 0; 814 | let q = crate::split::chars(search); 815 | let q_len = crate::split::chars(search).len(); 816 | crate::split::chars(&subject.to_owned()[subject.char_indices().nth(position).unwrap().0..]) 817 | .into_iter() 818 | .filter(|c| { 819 | if i < q_len { 820 | if c == &q[i] { 821 | i += 1; 822 | true 823 | } else { 824 | false 825 | } 826 | } else { 827 | false 828 | } 829 | }) 830 | .count() 831 | == crate::count::count(search) 832 | } 833 | 834 | /// Checks whether `subject` starts with `start`. 835 | /// 836 | /// # Arguments 837 | /// 838 | /// * `subject` - The string to verify. 839 | /// * `start` - The starting string. 840 | /// 841 | /// # Example 842 | /// 843 | /// ``` 844 | /// use voca_rs::*; 845 | /// query::starts_with("say hello to my little friend", "say hello"); 846 | /// // => true 847 | /// query::starts_with("say hello to my little friend", "hello"); 848 | /// // => false 849 | /// use voca_rs::Voca; 850 | /// "say hello to my little friend"._starts_with("say hello"); 851 | /// // => true 852 | /// ``` 853 | pub fn starts_with(subject: &str, start: &str) -> bool { 854 | if subject.is_empty() || start.is_empty() { 855 | return true; 856 | } 857 | subject.starts_with(start) 858 | } 859 | -------------------------------------------------------------------------------- /src/manipulate.rs: -------------------------------------------------------------------------------- 1 | //! Manipulate with the `subject`. 2 | 3 | use index; 4 | /// Returns a copy of `subject` expands spaces using the tab characters. 5 | /// 6 | /// # Arguments 7 | /// 8 | /// * `subject` - The string to expand. 9 | /// * `tabsize` - The tab size. 10 | /// 11 | /// # Example 12 | /// 13 | /// ``` 14 | /// use voca_rs::*; 15 | /// manipulate::expand_spaces("This is good", 2); 16 | /// // => "This\tis\tgood" 17 | /// manipulate::expand_spaces("Café del Mar", 2); 18 | /// // => "Café del\tMar" 19 | /// use voca_rs::Voca; 20 | /// "This is good"._expand_spaces(2); 21 | /// // => "This\tis\tgood" 22 | /// ``` 23 | pub fn expand_spaces(subject: &str, tabsize: usize) -> String { 24 | if subject.is_empty() || tabsize == 0 { 25 | subject.to_string() 26 | } else { 27 | subject.replace(&" ".repeat(tabsize), "\t") 28 | } 29 | } 30 | 31 | /// Returns a copy of `subject` expands the tab characters using spaces. 32 | /// 33 | /// # Arguments 34 | /// 35 | /// * `subject` - The string to expand. 36 | /// * `tabsize` - The tab size. 37 | /// 38 | /// # Example 39 | /// 40 | /// ``` 41 | /// use voca_rs::*; 42 | /// manipulate::expand_tabs("This is\tgood", 4); 43 | /// // => "This is good" 44 | /// manipulate::expand_tabs("no\tspaces", 0); 45 | /// // => "nospaces" 46 | /// use voca_rs::Voca; 47 | /// "This is\tgood"._expand_tabs(4); 48 | /// // => "This is good" 49 | /// ``` 50 | pub fn expand_tabs(subject: &str, tabsize: usize) -> String { 51 | if subject.is_empty() { 52 | "".to_string() 53 | } else { 54 | subject.replace('\t', &" ".repeat(tabsize)) 55 | } 56 | } 57 | 58 | /// Inserts into `subject` a string `to_insert` at specified `position`. 59 | /// 60 | /// # Arguments 61 | /// 62 | /// * `subject` - The string where to insert. 63 | /// * `to_insert` - The string to be inserted 64 | /// * `position` - The position to insert. 65 | /// 66 | /// # Example 67 | /// 68 | /// ``` 69 | /// use voca_rs::*; 70 | /// manipulate::insert("ct", "a", 1); 71 | /// // => "cat" 72 | /// manipulate::insert("sunny", " day", 5); 73 | /// // => "sunny day" 74 | /// use voca_rs::Voca; 75 | /// "ct"._insert("a", 1); 76 | /// // => "cat" 77 | /// ``` 78 | pub fn insert(subject: &str, to_insert: &str, position: usize) -> String { 79 | let subject_len = subject.len(); 80 | if subject_len == 0 || to_insert.is_empty() { 81 | return subject.to_string(); 82 | } 83 | let insert_position = if position > subject_len { 84 | subject_len 85 | } else { 86 | position 87 | }; 88 | let prefix = crate::split::chars(subject)[..insert_position].join(""); 89 | let sufix = crate::split::chars(subject)[insert_position..].join(""); 90 | format!("{}{}{}", prefix, to_insert, sufix) 91 | } 92 | 93 | use utils::unidecode; 94 | /// Latinises the `subject` by removing diacritic characters. 95 | /// 96 | /// # Arguments 97 | /// 98 | /// * `subject` - The string to latinise. 99 | /// 100 | /// # Example 101 | /// 102 | /// ``` 103 | /// use voca_rs::*; 104 | /// manipulate::latinise("cafe\u{0301}"); 105 | /// // => "cafe" 106 | /// manipulate::latinise("août décembre"); 107 | /// // => aout decembre 108 | /// manipulate::latinise("как прекрасен этот мир"); 109 | /// // => kak prekrasen etot mir 110 | /// use voca_rs::Voca; 111 | /// "cafe\u{0301}"._latinise(); 112 | /// // => "cafe" 113 | /// ``` 114 | pub fn latinise(subject: &str) -> String { 115 | if subject.is_empty() { 116 | "".to_string() 117 | } else { 118 | unidecode(subject) 119 | } 120 | } 121 | 122 | /// Pads `subject` to a new `length`. 123 | /// 124 | /// # Arguments 125 | /// 126 | /// * `subject` - The string to pad. 127 | /// * `length` - The length to pad the string. No changes are made if `length` is less than `subject.len()`. 128 | /// * `pad` - The string to be used for padding. 129 | /// 130 | /// # Example 131 | /// 132 | /// ``` 133 | /// use voca_rs::*; 134 | /// manipulate::pad("dog", 5, ""); 135 | /// // => " dog " 136 | /// manipulate::pad("bird", 6, "-"); 137 | /// // => "-bird-" 138 | /// manipulate::pad("Café del Mar", 15, "-="); 139 | /// // => "-Café del Mar-=" 140 | /// use voca_rs::Voca; 141 | /// "dog"._pad(5, ""); 142 | /// // => " dog " 143 | /// ``` 144 | pub fn pad(subject: &str, length: usize, pad: &str) -> String { 145 | let subject_len = crate::count::count_graphemes(subject); 146 | match subject_len { 147 | 0 => "".to_string(), 148 | _ => { 149 | if subject_len >= length { 150 | subject.to_string() 151 | } else { 152 | pad_left_right(subject, length, pad, PadMode::Both) 153 | } 154 | } 155 | } 156 | } 157 | 158 | #[derive(Clone, Copy, PartialEq)] 159 | enum PadMode { 160 | Both, 161 | Left, 162 | Right, 163 | } 164 | 165 | fn pad_left_right(subject: &str, length: usize, pad: &str, pad_mode: PadMode) -> String { 166 | let width = length - crate::count::count_graphemes(subject); 167 | let to_add = if pad.is_empty() { " " } else { pad }; 168 | let times = width / to_add.len(); 169 | let str_to_add = to_add.repeat(times + 1); 170 | let string_to_add = crate::split::chars(&str_to_add); 171 | let padding = if pad_mode == PadMode::Left || pad_mode == PadMode::Right { 172 | string_to_add[..width].join("") 173 | } else { 174 | "".to_string() 175 | }; 176 | 177 | match pad_mode { 178 | PadMode::Both => { 179 | let string_to_add_len = string_to_add.len(); 180 | let middle = if string_to_add_len < width { 181 | string_to_add_len / 2 182 | } else { 183 | width / 2 184 | }; 185 | let add = usize::from(width % 2 != 0); 186 | let prefix = string_to_add[..middle].join(""); 187 | let sufix = string_to_add[..middle + add].join(""); 188 | format!("{}{}{}", prefix, subject, sufix) 189 | } 190 | PadMode::Left => format!("{}{}", padding, subject), 191 | PadMode::Right => format!("{}{}", subject, padding), 192 | } 193 | } 194 | 195 | /// Pads `subject` from left to a new `length`. 196 | /// 197 | /// # Arguments 198 | /// 199 | /// * `subject` - The string to pad. 200 | /// * `length` - The length to pad the string. No changes are made if `length` is less than `subject.len()`. 201 | /// * `pad` - The string to be used for padding. 202 | /// 203 | /// # Example 204 | /// 205 | /// ``` 206 | /// use voca_rs::*; 207 | /// manipulate::pad_left("dog", 5, ""); 208 | /// // => " dog" 209 | /// manipulate::pad_left("bird", 6, "-"); 210 | /// // => "--bird" 211 | /// manipulate::pad_left("Café del Mar", 15, "-="); 212 | /// // => "-=-Café del Mar" 213 | /// use voca_rs::Voca; 214 | /// "dog"._pad_left(5, ""); 215 | /// // => " dog" 216 | /// ``` 217 | pub fn pad_left(subject: &str, length: usize, pad: &str) -> String { 218 | let subject_len = crate::count::count_graphemes(subject); 219 | match subject_len { 220 | 0 => "".to_string(), 221 | _ => { 222 | if subject_len >= length { 223 | subject.to_string() 224 | } else { 225 | pad_left_right(subject, length, pad, PadMode::Left) 226 | } 227 | } 228 | } 229 | } 230 | 231 | /// Pads `subject` from right to a new `length`. 232 | /// 233 | /// # Arguments 234 | /// 235 | /// * `subject` - The string to pad. 236 | /// * `length` - The length to pad the string. No changes are made if `length` is less than `subject.len()`. 237 | /// * `pad` - The string to be used for padding. 238 | /// 239 | /// # Example 240 | /// 241 | /// ``` 242 | /// use voca_rs::*; 243 | /// manipulate::pad_right("dog", 5, ""); 244 | /// // => "dog " 245 | /// manipulate::pad_right("bird", 6, "-"); 246 | /// // => "bird--" 247 | /// manipulate::pad_right("Café del Mar", 15, "-="); 248 | /// // => "Café del Mar-=-" 249 | /// use voca_rs::Voca; 250 | /// "dog"._pad_right(5, ""); 251 | /// // => "dog " 252 | /// ``` 253 | pub fn pad_right(subject: &str, length: usize, pad: &str) -> String { 254 | let subject_len = crate::count::count_graphemes(subject); 255 | match subject_len { 256 | 0 => "".to_string(), 257 | _ => { 258 | if subject_len >= length { 259 | subject.to_string() 260 | } else { 261 | pad_left_right(subject, length, pad, PadMode::Right) 262 | } 263 | } 264 | } 265 | } 266 | 267 | /// Repeats the `subject` number of `times`. 268 | /// 269 | /// # Arguments 270 | /// 271 | /// * `subject` - The string to repeat. 272 | /// * `times` - The number of times to repeat. 273 | /// 274 | /// # Example 275 | /// 276 | /// ``` 277 | /// use voca_rs::*; 278 | /// manipulate::repeat("w", 3); 279 | /// // => "www" 280 | /// manipulate::repeat("world", 0); 281 | /// // => "" 282 | /// use voca_rs::Voca; 283 | /// "w"._repeat(3); 284 | /// // => "www" 285 | /// ``` 286 | pub fn repeat(subject: &str, times: usize) -> String { 287 | if subject.is_empty() || times == 0 { 288 | return "".to_string(); 289 | } 290 | 291 | subject.repeat(times) 292 | } 293 | 294 | /// Replaces the matches of `pattern` with `replacement`. 295 | /// 296 | /// # Arguments 297 | /// 298 | /// * `subject` - The string to verify. 299 | /// * `pattern` - The pattern which match is replaced. Only the first occurrence replaced. 300 | /// * `replacement` - The string which replaces `pattern` match. 301 | /// 302 | /// # Example 303 | /// 304 | /// ``` 305 | /// use voca_rs::*; 306 | /// manipulate::replace("swan", "wa", "u"); 307 | /// // => "sun" 308 | /// manipulate::replace("domestic duck", "d", "D"); 309 | /// // => "Domestic duck" 310 | /// manipulate::replace("Café del Mar cafe\u{0301}", "é", "e"); 311 | /// // => "Cafe del Mar café" 312 | /// use voca_rs::Voca; 313 | /// "swan"._replace("wa", "u"); 314 | /// // => "sun" 315 | /// ``` 316 | pub fn replace(subject: &str, pattern: &str, replacement: &str) -> String { 317 | if subject.is_empty() || pattern.is_empty() { 318 | return subject.to_string(); 319 | } 320 | match index::index_of(subject, pattern, 0) { 321 | -1 => subject.to_string(), 322 | x => splice( 323 | subject, 324 | x as isize, 325 | crate::count::count(pattern), 326 | replacement, 327 | ), 328 | } 329 | } 330 | 331 | /// Replaces all matches of `pattern` with `replacement`. 332 | /// 333 | /// # Arguments 334 | /// 335 | /// * `subject` - The string to verify. 336 | /// * `pattern` - The pattern which match is replaced. All matches are replaced. 337 | /// * `replacement` - The string which replaces `pattern` match. 338 | /// 339 | /// # Example 340 | /// 341 | /// ``` 342 | /// use voca_rs::*; 343 | /// manipulate::replace_all("swan", "wa", "u"); 344 | /// // => "sun" 345 | /// manipulate::replace_all("domestic duck", "d", "D"); 346 | /// // => "Domestic Duck" 347 | /// manipulate::replace_all("Café del Mar café", "é", "e"); 348 | /// // => "Cafe del Mar cafe" 349 | /// use voca_rs::Voca; 350 | /// "swan"._replace_all("wa", "u"); 351 | /// // => "sun" 352 | /// ``` 353 | pub fn replace_all(subject: &str, pattern: &str, replacement: &str) -> String { 354 | if subject.is_empty() || pattern.is_empty() { 355 | return subject.to_string(); 356 | } 357 | subject.replace(pattern, replacement) 358 | } 359 | 360 | /// Reverses the `subject`. 361 | /// 362 | /// # Arguments 363 | /// 364 | /// * `subject` - The string to reverse. 365 | /// 366 | /// # Example 367 | /// 368 | /// ``` 369 | /// use voca_rs::*; 370 | /// manipulate::reverse("winter"); 371 | /// // => "retniw" 372 | /// use voca_rs::Voca; 373 | /// "winter"._reverse(); 374 | /// // => "retniw" 375 | /// ``` 376 | pub fn reverse(subject: &str) -> String { 377 | if subject.is_empty() { 378 | return "".to_string(); 379 | } 380 | 381 | subject.chars().rev().collect() 382 | } 383 | 384 | use unicode_segmentation::UnicodeSegmentation; 385 | /// Reverses the `subject` taking care of surrogate pairs and combining marks. 386 | /// 387 | /// # Arguments 388 | /// 389 | /// * `subject` - The string to reverse. 390 | /// 391 | /// # Example 392 | /// 393 | /// ``` 394 | /// use voca_rs::*; 395 | /// manipulate::reverse_grapheme("café"); 396 | /// // => "éfac" 397 | /// manipulate::reverse_grapheme("a̐éö̲"); 398 | /// // => "ö̲éa̐" 399 | /// use voca_rs::Voca; 400 | /// "café"._reverse_grapheme(); 401 | /// // => "éfac" 402 | /// ``` 403 | pub fn reverse_grapheme(subject: &str) -> String { 404 | if subject.is_empty() { 405 | return "".to_string(); 406 | } 407 | 408 | UnicodeSegmentation::graphemes(subject, true) 409 | .rev() 410 | .collect::>() 411 | .join("") 412 | } 413 | 414 | /// Slugifies the `subject`. Cleans the `subject` by replacing diacritics with corresponding latin characters. 415 | /// 416 | /// # Arguments 417 | /// 418 | /// * `subject` - The string to latinise. 419 | /// 420 | /// # Example 421 | /// 422 | /// ``` 423 | /// use voca_rs::*; 424 | /// manipulate::slugify("Italian cappuccino drink"); 425 | /// // => "italian-cappuccino-drink" 426 | /// manipulate::slugify("caffé latté"); 427 | /// // => caffe-latte 428 | /// manipulate::slugify("Хорошая статья: 'XMLHttpRequest 101 Course' \\!/"); 429 | /// // => khoroshaia-statia-xmlhttprequest-101-course 430 | /// use voca_rs::Voca; 431 | /// "Italian cappuccino drink"._slugify(); 432 | /// // => "italian-cappuccino-drink" 433 | /// ``` 434 | pub fn slugify(subject: &str) -> String { 435 | if subject.is_empty() { 436 | "".to_string() 437 | } else { 438 | crate::split::words(unidecode(subject).replace('\'', "").to_lowercase().trim()).join("-") 439 | } 440 | } 441 | 442 | /// Changes `subject` by deleting `delete_count` of characters starting at position `start`. Places a new string `to_add` instead of deleted characters. 443 | /// 444 | /// # Arguments 445 | /// 446 | /// * `subject` - The string where to insert. 447 | /// * `start` - The position to start changing the string. For a negative position will start from the end of the string. 448 | /// * `delete_count` - The number of characters to delete from string. 449 | /// * `to_add` - The string to be added instead of deleted characters. 450 | /// 451 | /// # Example 452 | /// ``` 453 | /// use voca_rs::*; 454 | /// manipulate::splice("new year", 0, 4, ""); 455 | /// // => "year" 456 | /// manipulate::splice("to jest błąd", 0, 7, "mój"); 457 | /// // => "mój błąd" 458 | /// manipulate::splice("Die Schildkröte fliegt.", -7, 0, "und Kröte "); 459 | /// // => "Die Schildkröte und Kröte fliegt." 460 | /// manipulate::splice("Привет", 6, 0, ", Ёлка!"); 461 | /// // => "Привет, Ёлка!" 462 | /// use voca_rs::Voca; 463 | /// "new year"._splice(0, 4, ""); 464 | /// // => "year" 465 | /// ``` 466 | pub fn splice(subject: &str, start: isize, delete_count: usize, to_add: &str) -> String { 467 | let subject_len = crate::count::count(subject); 468 | fn calculate_start_position(start: isize, subject_len: usize) -> usize { 469 | if start < 0 { 470 | if start.unsigned_abs() > subject_len { 471 | 0 472 | } else { 473 | subject_len - start.unsigned_abs() 474 | } 475 | } else if (start as usize) >= subject_len { 476 | subject_len 477 | } else { 478 | start as usize 479 | } 480 | } 481 | 482 | match delete_count { 483 | 0 => match to_add.len() { 484 | 0 => subject.to_string(), 485 | _ => { 486 | let insert_position = calculate_start_position(start, subject_len); 487 | if insert_position >= subject_len { 488 | format!("{}{}", subject, to_add) 489 | } else { 490 | insert(subject, to_add, insert_position) 491 | } 492 | } 493 | }, 494 | _ => { 495 | let start_position = calculate_start_position(start, subject_len); 496 | let end_position = if delete_count > subject_len - start_position { 497 | subject_len 498 | } else { 499 | start_position + delete_count 500 | }; 501 | 502 | format!( 503 | "{}{}{}", 504 | crate::chop::first(subject, start_position), 505 | to_add, 506 | crate::chop::slice(subject, end_position as isize, 0) 507 | ) 508 | } 509 | } 510 | } 511 | 512 | /// Removes whitespaces from left and right sides of the `subject`. 513 | /// 514 | /// # Arguments 515 | /// 516 | /// * `subject` - The string to trim. 517 | /// * `whitespace` - The whitespace characters to trim. List all characters that you want to be stripped. 518 | /// 519 | /// # Example 520 | /// 521 | /// ``` 522 | /// use voca_rs::*; 523 | /// manipulate::trim(" Mother nature ", ""); 524 | /// // => "Mother nature" 525 | /// manipulate::trim("-~-Earth~-~", "-~"); 526 | /// // => "Earth" 527 | /// use voca_rs::Voca; 528 | /// " Mother nature "._trim(""); 529 | /// // => "Mother nature" 530 | /// ``` 531 | pub fn trim(subject: &str, whitespace: &str) -> String { 532 | trim_left_or_right(subject, whitespace, true, true) 533 | } 534 | 535 | /// Removes whitespaces from the left side of the `subject`. 536 | /// 537 | /// # Arguments 538 | /// 539 | /// * `subject` - The string to trim. 540 | /// * `whitespace` - The whitespace characters to trim. List all characters that you want to be stripped. 541 | /// 542 | /// # Example 543 | /// 544 | /// ``` 545 | /// use voca_rs::*; 546 | /// manipulate::trim_left(" Mother nature ", ""); 547 | /// // => "Mother nature " 548 | /// manipulate::trim_left("-~-Earth~-~", "-~"); 549 | /// // => "Earth~-~" 550 | /// use voca_rs::Voca; 551 | /// " Mother nature "._trim_left(""); 552 | /// // => "Mother nature " 553 | /// ``` 554 | pub fn trim_left(subject: &str, whitespace: &str) -> String { 555 | trim_left_or_right(subject, whitespace, true, false) 556 | } 557 | 558 | /// Removes whitespaces from the right side of the `subject`. 559 | /// 560 | /// # Arguments 561 | /// 562 | /// * `subject` - The string to trim. 563 | /// * `whitespace` - The whitespace characters to trim. List all characters that you want to be stripped. 564 | /// 565 | /// # Example 566 | /// 567 | /// ``` 568 | /// use voca_rs::*; 569 | /// manipulate::trim_right(" Mother nature ", ""); 570 | /// // => " Mother nature" 571 | /// manipulate::trim_right("-~-Earth~-~", "-~"); 572 | /// // => "-~-Earth" 573 | /// /// use voca_rs::Voca; 574 | /// " Mother nature "._trim_right(""); 575 | /// // => " Mother nature" 576 | /// ``` 577 | pub fn trim_right(subject: &str, whitespace: &str) -> String { 578 | trim_left_or_right(subject, whitespace, false, true) 579 | } 580 | 581 | fn trim_left_or_right(subject: &str, whitespace: &str, to_left: bool, to_right: bool) -> String { 582 | if subject.is_empty() { 583 | return subject.to_string(); 584 | } 585 | if whitespace.is_empty() { 586 | if to_left && to_right { 587 | return subject.trim().to_string(); 588 | } else if to_left { 589 | return subject.trim_start().to_string(); 590 | } else { 591 | return subject.trim_end().to_string(); 592 | } 593 | } 594 | 595 | if to_left && to_right { 596 | subject.trim_matches(|c| whitespace.contains(c)).to_owned() 597 | } else if to_left { 598 | subject 599 | .trim_start_matches(|c| whitespace.contains(c)) 600 | .to_owned() 601 | } else { 602 | subject 603 | .trim_end_matches(|c| whitespace.contains(c)) 604 | .to_owned() 605 | } 606 | } 607 | /// Pads `subject` from left with zeros to a new `length`. 608 | /// 609 | /// # Arguments 610 | /// 611 | /// * `subject` - The string to pad. 612 | /// * `length` - The length to pad the string. No changes are made if `length` is less than `subject.len()`. 613 | /// 614 | /// # Example 615 | /// 616 | /// ``` 617 | /// use voca_rs::*; 618 | /// manipulate::zfill("123", 5); 619 | /// // => "00123" 620 | /// manipulate::zfill("Café", 7); 621 | /// // => "000Café" 622 | /// use voca_rs::Voca; 623 | /// "123"._zfill(5); 624 | /// // => "00123" 625 | /// ``` 626 | pub fn zfill(subject: &str, length: usize) -> String { 627 | let subject_len = crate::count::count_graphemes(subject); 628 | match subject_len { 629 | 0 => "".to_string(), 630 | _ => { 631 | if subject_len >= length { 632 | subject.to_string() 633 | } else { 634 | pad_left_right(subject, length, "0", PadMode::Left) 635 | } 636 | } 637 | } 638 | } 639 | 640 | /// Translates characters or replaces substrings in `subject`. 641 | /// 642 | /// # Arguments 643 | /// 644 | /// * `subject` - The string to translate. 645 | /// * `from` - The string of characters to translate from. 646 | /// * `to` - The string of characters to translate to. 647 | /// 648 | /// # Example 649 | /// 650 | /// ``` 651 | /// use voca_rs::*; 652 | /// manipulate::tr("hello", "el", "ip"); 653 | /// // => "hippo" 654 | /// manipulate::tr("légèreté", "éè", "ee"); 655 | /// // => "legerete" 656 | /// use voca_rs::Voca; 657 | /// "hello"._tr("el", "ip"); 658 | /// // => "hippo" 659 | /// ``` 660 | pub fn tr(subject: &str, from: &str, to: &str) -> String { 661 | if from.is_empty() || subject.is_empty() { 662 | return subject.to_owned(); 663 | } 664 | let mut result = String::from(subject); 665 | let from_symbols = crate::split::graphemes(from); 666 | let to_symbols = crate::split::graphemes(to); 667 | let to_len = to_symbols.len(); 668 | 669 | for (i, c) in from_symbols.iter().enumerate() { 670 | let new_c = if i < to_len { to_symbols[i] } else { "" }; 671 | result = result.replace(c, new_c); 672 | } 673 | result 674 | } 675 | 676 | /// Wraps `subject` to a given number of characters using a string break character. 677 | /// 678 | /// # Arguments 679 | /// 680 | /// * `subject` - The string to wrap. 681 | /// * `width` - The number of characters at which to wrap. 682 | /// * `newline` - The string to add at the end of line. Default value is "\n" (if it's not given). 683 | /// * `indent` - The string to intend the line. Default value is "" (if it's not given). 684 | /// 685 | /// # Example 686 | /// 687 | /// ``` 688 | /// use voca_rs::*; 689 | /// manipulate::word_wrap("Hello world", 5, "", ""); 690 | /// // => "Hello\nworld" 691 | /// manipulate::word_wrap("Hello world", 5, "
", "__"); 692 | /// // => "__Hello
__world" 693 | /// use voca_rs::Voca; 694 | /// "Hello world"._word_wrap(5, "", ""); 695 | /// // => "Hello\nworld" 696 | /// ``` 697 | pub fn word_wrap(subject: &str, width: usize, newline: &str, indent: &str) -> String { 698 | let mut subject_len = crate::count::count_graphemes(subject); 699 | if subject.is_empty() || (subject_len < width && indent.is_empty()) { 700 | return subject.to_owned(); 701 | } 702 | let mut result = String::new(); 703 | let mut string = String::from(subject); 704 | let length = width + 1; 705 | let new_line = if newline.is_empty() { "\n" } else { newline }; 706 | let indent_sym = if indent.is_empty() { "" } else { indent }; 707 | 708 | while subject_len > width { 709 | let mut subj_part = crate::chop::prune(&string, length, "+"); 710 | subj_part = trim(&crate::chop::slice(&subj_part, 0, -1), ""); 711 | let length_to_cut = crate::count::count_graphemes(&subj_part); 712 | string = trim(&crate::chop::slice(&string, length_to_cut as isize, 0), ""); 713 | subject_len = crate::count::count_graphemes(&string); 714 | 715 | if subj_part == string || subj_part.is_empty() { 716 | break; 717 | } 718 | let mut is_finished = false; 719 | let mut subj_part_len = crate::count::count_graphemes(&subj_part); 720 | while !is_finished && subj_part_len < width { 721 | let first_char = crate::chop::first(&string, 1); 722 | 723 | if crate::utils::PUNCTUATION.contains(&first_char) { 724 | subj_part.push_str(&first_char); 725 | subj_part_len = crate::count::count_graphemes(&subj_part); 726 | string = trim(&crate::chop::slice(&string, 1, 0), ""); 727 | } else { 728 | is_finished = true; 729 | } 730 | } 731 | let str_to_insert = format!("{}{}{}", indent_sym, subj_part, new_line); 732 | result.push_str(&str_to_insert); 733 | } 734 | format!("{}{}{}", result, indent_sym, string) 735 | } 736 | 737 | /// Adds the `suffix` to the end of the string unless it already exists in the `subject`. 738 | /// 739 | /// # Arguments 740 | /// 741 | /// * `subject` - The string to extend. 742 | /// * `suffix` - The ending string. 743 | /// 744 | /// # Example 745 | /// 746 | /// ``` 747 | /// use voca_rs::*; 748 | /// manipulate::finish("foo bar", "bar"); 749 | /// // => "foo bar" 750 | /// manipulate::finish("fóo bąr", " ço¨oł"); 751 | /// // => "fóo bąr ço¨oł" 752 | /// use voca_rs::Voca; 753 | /// "foo bar"._finish(" cool"); 754 | /// // => "foo bar cool" 755 | /// ``` 756 | pub fn finish(subject: &str, suffix: &str) -> String { 757 | if suffix.is_empty() || subject.ends_with(suffix) { 758 | return subject.to_owned(); 759 | } 760 | format!("{}{}", subject, suffix) 761 | } 762 | /// Adds the `prefix` to the start of the string unless it already exists in the `subject`. 763 | /// 764 | /// # Arguments 765 | /// 766 | /// * `subject` - The string to extend. 767 | /// * `prefix` - The starting string. 768 | /// 769 | /// # Example 770 | /// 771 | /// ``` 772 | /// use voca_rs::*; 773 | /// manipulate::start("foo bar", "foo"); 774 | /// // => "foo bar" 775 | /// manipulate::start("fóo bąr", "ço¨oł "); 776 | /// // => "ço¨oł fóo bąr" 777 | /// use voca_rs::Voca; 778 | /// "foo bar"._start("cool "); 779 | /// // => "cool foo bar" 780 | /// ``` 781 | pub fn start(subject: &str, prefix: &str) -> String { 782 | if prefix.is_empty() || subject.starts_with(prefix) { 783 | return subject.to_owned(); 784 | } 785 | format!("{}{}", prefix, subject) 786 | } 787 | --------------------------------------------------------------------------------