# rstring
[](https://github.com/jntakpe/rstring)
[](Cargo.toml)
[](https://crates.io/crates/rstring)
[](https://github.com/jntakpe/rstring/actions)
[](LICENSE)
A comprehensive string manipulation library for Rust, inspired by [Java's Apache Commons Lang StringUtils](https://commons.apache.org/proper/commons-lang/javadocs/api-release/org/apache/commons/lang3/StringUtils.html).
Provides **100+ utility methods** as extension traits on `str`, covering validation, case conversion, padding, searching, substring extraction, and more. All Unicode-aware and with zero dependencies.
## Installation
Add to your `Cargo.toml`:
```toml
[dependencies]
rstring = "0.1"
```
## Quick Start
Import the traits you need and call methods directly on any string type:
```rust
use rstring::{StringChecks, StringCase, StringPad};
// Validation
assert!(" ".is_blank());
assert!("abc123".is_alphanumeric());
// Case conversion
assert_eq!("hello".capitalize(), "Hello");
assert_eq!("Hello World".swap_case(), "hELLO wORLD");
// Padding
assert_eq!("7".left_pad_with(3, '0'), "007");
assert_eq!("bat".center(7), " bat ");
```
You can also import everything at once:
```rust
use rstring::*;
```
## Modules
### `checks` — String Validation
Predicates that test string content
```rust
use rstring::StringChecks;
assert!("".is_blank());
assert!("12345".is_numeric());
assert!("abc".is_alpha());
assert!("abc123".is_alphanumeric());
assert!("Hello World".is_mixed_case());
assert!(" \t\n".is_whitespace());
```
| `is_blank` | Empty or whitespace only |
| `is_not_blank` | Has at least one non-whitespace character |
| `is_numeric` | Digits only (Unicode) |
| `is_numeric_space` | Digits and spaces only |
| `is_alpha` | Letters only (Unicode) |
| `is_alpha_space` | Letters and spaces only |
| `is_alphanumeric` | Letters and digits only |
| `is_alphanumeric_space` | Letters, digits, and spaces only |
| `is_whitespace` | Whitespace characters only |
| `is_ascii_printable` | ASCII printable characters only (32–126) |
| `is_all_lowercase` | All cased characters are lowercase |
| `is_all_uppercase` | All cased characters are uppercase |
| `is_mixed_case` | Contains both uppercase and lowercase |
### `case` — Case Conversion
```rust
use rstring::StringCase;
assert_eq!("hello".capitalize(), "Hello");
assert_eq!("Hello".uncapitalize(), "hello");
assert_eq!("Hello World".swap_case(), "hELLO wORLD");
```
| `capitalize` | Uppercase the first character |
| `uncapitalize` | Lowercase the first character |
| `swap_case` | Swap case of every character |
### `contains` — Containment Checks
```rust
use rstring::StringContains;
assert!("abc".contains_any_char(&['a', 'z']));
assert!("Hello".contains_ignore_case("hello"));
assert!("abc".contains_only("abcd"));
assert!("abc".contains_none_char(&['x', 'y', 'z']));
```
| `contains_any_char` | Contains any of the given characters |
| `contains_any_in` | Contains any character from a string |
| `contains_any` | Contains any of the given substrings |
| `contains_ignore_case` | Case-insensitive containment check |
| `contains_any_ignore_case` | Case-insensitive check for any substring |
| `contains_none_char` | Contains none of the given characters |
| `contains_none` | Contains none of the characters in a string |
| `contains_only_char` | Contains only characters from the given set |
| `contains_only` | Contains only characters from a string |
| `contains_whitespace` | Contains at least one whitespace character |
### `index` — Searching and Counting
```rust
use rstring::StringIndex;
assert_eq!("zzabyycdxx".index_of_any_char(&['a', 'b']), Some(2));
assert_eq!("ababab".ordinal_index_of("ab", 3), Some(4));
assert_eq!("abba".count_matches("a"), 2);
assert_eq!("Hello".index_of_ignore_case("hello"), Some(0));
```
| `index_of_any_char` | First index of any given character |
| `index_of_any_in` | First index of any character from a string |
| `index_of_any` | First index of any given substring |
| `index_of_any_but_char` | First index of a character **not** in the set |
| `index_of_any_but` | First index of a character **not** in a string |
| `last_index_of_any` | Last index of any given substring |
| `ordinal_index_of` | Nth occurrence of a substring |
| `last_ordinal_index_of` | Nth-from-last occurrence of a substring |
| `index_of_ignore_case` | Case-insensitive index search |
| `index_of_ignore_case_from` | Case-insensitive index search from position |
| `last_index_of_ignore_case` | Case-insensitive last index search |
| `last_index_of_ignore_case_from` | Case-insensitive last index search from position |
| `index_of_difference` | First index where two strings differ |
| `count_matches_char` | Count occurrences of a character |
| `count_matches` | Count non-overlapping occurrences of a substring |
### `substring` — Substring Extraction
Safe substring methods that never panic — they return what they can or `None`.
```rust
use rstring::StringSubstring;
assert_eq!("abc".left(2), "ab");
assert_eq!("abc".right(2), "bc");
assert_eq!("abc".mid(1, 2), "bc");
assert_eq!("abc-def".substring_before("-"), Some("abc"));
assert_eq!("abc-def".substring_after("-"), Some("def"));
assert_eq!("[a]and[b]".substrings_between("[", "]"), vec!["a", "b"]);
```
| `left` | Leftmost N characters |
| `right` | Rightmost N characters |
| `mid` | N characters starting at position |
| `substring_before` | Everything before the first occurrence |
| `substring_before_char` | Everything before the first char occurrence |
| `substring_after` | Everything after the first occurrence |
| `substring_after_char` | Everything after the first char occurrence |
| `substring_before_last` | Everything before the last occurrence |
| `substring_before_last_char` | Everything before the last char occurrence |
| `substring_after_last` | Everything after the last occurrence |
| `substring_after_last_char` | Everything after the last char occurrence |
| `substring_between` | Content between matching open/close tags |
| `substring_between_with` | Content between different open/close delimiters |
| `substrings_between` | All substrings between open/close delimiters |
### `pad` — Padding and Repeating
```rust
use rstring::StringPad;
assert_eq!("1".left_pad(3), " 1");
assert_eq!("1".left_pad_with(3, '0'), "001");
assert_eq!("bat".center(7), " bat ");
assert_eq!("ab".repeat_str(3), "ababab");
assert_eq!("ab".repeat_with_separator(", ", 3), "ab, ab, ab");
```
| `left_pad` | Left-pad with spaces |
| `left_pad_with` | Left-pad with a character |
| `left_pad_with_str` | Left-pad with a string |
| `right_pad` | Right-pad with spaces |
| `right_pad_with` | Right-pad with a character |
| `right_pad_with_str` | Right-pad with a string |
| `center` | Center with spaces |
| `center_with` | Center with a character |
| `center_with_str` | Center with a string |
| `repeat_str` | Repeat the string N times |
| `repeat_with_separator` | Repeat with a separator between copies |
Also provides a standalone function: `rstring::pad::repeat_char(c, n)`.
### `affixes` — Prefix and Suffix Operations
Returns `Cow<str>` to avoid allocation when the string is unchanged.
```rust
use rstring::StringAffixes;
assert_eq!(
"domain.com".append_if_missing("/"),
"domain.com/"
);
assert_eq!(
"domain.com/".append_if_missing("/"),
"domain.com/"
);
assert_eq!(
"domain.com".prepend_if_missing("https://"),
"https://domain.com"
);
```
| `append_if_missing` | Append suffix if not already present |
| `append_if_missing_ignore_ascii_case` | ASCII case-insensitive variant |
| `append_if_missing_ignore_case` | Unicode case-insensitive variant |
| `prepend_if_missing` | Prepend prefix if not already present |
| `prepend_if_missing_ignore_ascii_case` | ASCII case-insensitive variant |
| `prepend_if_missing_ignore_case` | Unicode case-insensitive variant |
### `remove` — Character and Substring Removal
Methods returning a slice use `&str`; those requiring a new string use `Cow<str>`.
```rust
use rstring::StringRemove;
assert_eq!("www.domain.com".remove_start("www."), "domain.com");
assert_eq!("file.txt".remove_end(".txt"), "file");
assert_eq!("queued".remove_char('u'), "qeed");
assert_eq!(" a b c ".delete_whitespace(), "abc");
```
| `remove_start` | Remove a prefix |
| `remove_start_ignore_ascii_case` | ASCII case-insensitive variant |
| `remove_start_ignore_case` | Unicode case-insensitive variant |
| `remove_start_char` | Remove a leading character |
| `remove_end` | Remove a suffix |
| `remove_end_ignore_ascii_case` | ASCII case-insensitive variant |
| `remove_end_ignore_case` | Unicode case-insensitive variant |
| `remove_end_char` | Remove a trailing character |
| `remove_char` | Remove all occurrences of a character |
| `remove_occurrence` | Remove all occurrences of a substring |
| `remove_ignore_case` | Case-insensitive removal of all occurrences |
| `delete_whitespace` | Remove all whitespace |
### `reverse` — Reversal and Rotation
```rust
use rstring::StringReverse;
assert_eq!("bat".reverse_str(), "tab");
assert_eq!("www.domain.com".reverse_delimited('.'), "com.domain.www");
assert_eq!("abcdefg".rotate(2), "fgabcde");
```
| `reverse_str` | Reverse the string |
| `reverse_delimited` | Reverse segments around a delimiter |
| `rotate` | Circular shift (positive = right, negative = left) |
### `wrap` — Wrapping and Unwrapping
```rust
use rstring::StringWrap;
assert_eq!("abc".wrap_with_char('\''), "'abc'");
assert_eq!("abc".wrap_if_missing_char('\''), "'abc'");
assert_eq!("'abc'".unwrap_with_char('\''), "abc");
```
| `wrap_with_char` | Wrap with a character |
| `wrap_with_str` | Wrap with a string |
| `wrap_if_missing_char` | Wrap only if not already wrapped |
| `wrap_if_missing_str` | Wrap only if not already wrapped |
| `unwrap_with_char` | Remove matching wrap character |
| `unwrap_with_str` | Remove matching wrap string |
### `abbreviate` — String Abbreviation
```rust
use rstring::StringAbbreviate;
assert_eq!(
"Much too long text".abbreviate(10).unwrap(),
"Much to..."
);
assert_eq!(
"Too long".abbreviate_middle("-", 6),
"To-ong"
);
```
| `abbreviate` | Abbreviate with `...` |
| `abbreviate_with_offset` | Abbreviate starting near an offset |
| `abbreviate_with_marker` | Abbreviate with a custom marker |
| `abbreviate_with_marker_and_offset` | Full control over marker and offset |
| `abbreviate_middle` | Replace the middle with a marker |
| `overlay` | Overlay part of a string with another |
## Design Principles
- **Extension traits on `str`** — Methods work on `&str`, `String`, `Box<str>`, and any type that derefs to `str`
- **No `std` duplicates** — Methods already in Rust's standard library (`is_empty`, `contains`, `starts_with`, `trim`, `to_lowercase`, etc.) are not reimplemented
- **Smart return types** — Returns `&str` for slices, `Cow<str>` when allocation may be avoidable, `String` when a new string is almost always created
- **Unicode-aware** — All methods handle Unicode correctly. Case-insensitive methods use full Unicode case folding. ASCII-only variants (`_ignore_ascii_case`) are provided for performance-sensitive code
- **Zero dependencies** — No external crates required
## License
MIT