rstring 0.1.0

A comprehensive set of string manipulation utilities inspired by Apache Commons Lang3 StringUtils
Documentation
# rstring

[![Tests](https://img.shields.io/badge/tests-1017-brightgreen)](https://github.com/jntakpe/rstring)
[![Zero Dependencies](https://img.shields.io/badge/dependencies-0-brightgreen)](Cargo.toml)
[![Crates.io](https://img.shields.io/crates/v/rstring)](https://crates.io/crates/rstring)
[![Build Status](https://img.shields.io/github/actions/workflow/status/jntakpe/rstring/ci.yml?branch=master)](https://github.com/jntakpe/rstring/actions)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](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());
```

| Method                  | Description                               |
|-------------------------|-------------------------------------------|
| `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");
```

| Method         | Description                   |
|----------------|-------------------------------|
| `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']));
```

| Method                     | Description                                 |
|----------------------------|---------------------------------------------|
| `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));
```

| Method                           | Description                                      |
|----------------------------------|--------------------------------------------------|
| `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"]);
```

| Method                       | Description                                     |
|------------------------------|-------------------------------------------------|
| `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");
```

| Method                  | Description                            |
|-------------------------|----------------------------------------|
| `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"
);
```

| Method                                 | Description                           |
|----------------------------------------|---------------------------------------|
| `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");
```

| Method                           | Description                                 |
|----------------------------------|---------------------------------------------|
| `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");
```

| Method              | Description                                        |
|---------------------|----------------------------------------------------|
| `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");
```

| Method                 | Description                      |
|------------------------|----------------------------------|
| `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"
);
```

| Method                              | Description                           |
|-------------------------------------|---------------------------------------|
| `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