natlex_sort 0.1.0

Provides hybrid natural and lexicographical sorting for strings and byte slices, useful for sorting mixed lists of filenames and identifiers.
Documentation
# `natlex_sort` API Reference

Provides hybrid natural and lexicographical sorting functions and types.

**Core Concept:**
*   Strings/bytes of the **same length** are compared **lexicographically**.
*   Strings/bytes of **different lengths** are compared using **natural ordering** (`natord`).

## Comparison Functions

These functions perform the core comparison logic. You can use them directly with `slice.sort_by()` or other algorithms requiring an `Ordering`.

*   **`nat_lex_cmp(a: &str, b: &str) -> Ordering`**
    *   Compares two string slices using the hybrid logic (case-sensitive).
    *   Same length: `a.cmp(b)`
    *   Different length: `natord::compare(a, b)`

*   **`nat_lex_cmp_ignore(a: &str, b: &str) -> Ordering`**
    *   Compares two string slices using the hybrid logic (case-insensitive for ASCII).
    *   Same length: Case-insensitive lexicographical compare, with case-sensitive `a.cmp(b)` as tie-breaker.
    *   Different length: `natord::compare_ignore_case(a, b)`

*   **`nat_lex_byte_cmp(a: &[u8], b: &[u8]) -> Ordering`**
    *   Compares two byte slices using hybrid logic (case-sensitive).
    *   Suitable for ASCII text with embedded numbers.
    *   Same length: `a.cmp(b)`
    *   Different length: Custom natural comparison logic (see source/Rustdoc for details).

*   **`nat_lex_byte_cmp_ignore(a: &[u8], b: &[u8]) -> Ordering`**
    *   Compares two byte slices using hybrid logic (case-insensitive for ASCII).
    *   Suitable for ASCII text with embedded numbers.
    *   Same length: Case-insensitive lexicographical compare, with case-sensitive `a.cmp(b)` as tie-breaker.
    *   Different length: Custom natural comparison logic (case-insensitive, see source/Rustdoc for details).

## Sorting Functions

Convenience functions to sort slices directly.

*   **`nat_lex_sort<T: AsRef<str>>(slice: &mut [T])`**
    *   Sorts a slice of string-like items using `nat_lex_cmp`.

*   **`nat_lex_sort_ignore_case<T: AsRef<str>>(slice: &mut [T])`**
    *   Sorts a slice of string-like items using `nat_lex_cmp_ignore`.

*   **`nat_lex_sort_bytes(slice: &mut [&[u8]])`**
    *   Sorts a slice of byte slices using `nat_lex_byte_cmp`.

*   **`nat_lex_sort_bytes_ignore_case(slice: &mut [&[u8]])`**
    *   Sorts a slice of byte slices using `nat_lex_byte_cmp_ignore`.

## Wrapper Type

An owning string type providing natural-lexicographic ordering.

*   **`struct NatLexOrderedString(pub String)`**
    *   An owning string wrapper.
    *   Implements `Ord`, `PartialOrd`, `PartialEq`, `Eq`, `Hash`, `Clone`, `Debug`.
    *   Ordering is based on the **case-sensitive `nat_lex_cmp`**.
    *   Hashing is based on the underlying `String`.
    *   Can be created from `&str`, `String`, `Box<str>` using `From`/`.into()`.
    *   Can be converted back to `String` or `Box<str>` using `From`/`.into()`.

## How to Use

1.  **Add to `Cargo.toml`:**
    ```toml
    [dependencies]
    natlex_sort = "0.1.0" # Or the latest version
    ```

2.  **Use in code:**

    ```rust
    use natlex_sort::{nat_lex_sort, nat_lex_sort_ignore_case, nat_lex_cmp, NatLexOrderedString};
    use std::collections::BTreeMap; // Example usage with BTreeMap

    // --- Sorting Vec<String> ---
    let mut files = vec![
        "report10.txt".to_string(),
        "report2.txt".to_string(),
        "ID001".to_string(),
        "ID002".to_string(),
    ];
    natlex_sort::nat_lex_sort(&mut files);
    // files is now ["ID001", "ID002", "report2.txt", "report10.txt"]
    // Note: "ID..." comes before "report..." because len(5) != len(10/11) -> natord -> 'I' > 'r' is WRONG
    // natord_compare("ID001", "report2.txt") -> 'I' > 'r' -> files first WRONG
    // natord_compare("ID001", "report2.txt") -> Less ('I' < 'r') -> files should be ["ID001", "ID002", "report2.txt", "report10.txt"]
    // Ah, the previous manual trace was wrong. 'I' (73) < 'r' (114). So ID comes first.
    assert_eq!(files, vec!["ID001", "ID002", "report2.txt", "report10.txt"]);


    // --- Sorting Vec<&str> ignore case ---
    let mut names = vec!["Item Z", "item a", "Item 10", "Item 2"];
    nat_lex_sort::nat_lex_sort_ignore_case(&mut names);
    assert_eq!(names, vec!["item a", "Item 2", "Item 10", "Item Z"]); // Example sort result


    // --- Using the wrapper type ---
    let mut map = BTreeMap::new();
    map.insert(NatLexOrderedString::from("file10.txt"), 10);
    map.insert(NatLexOrderedString::from("file2.txt"), 2);
    map.insert(NatLexOrderedString::from("id001"), 1);

    // Keys will be ordered using nat_lex_cmp
    let keys: Vec<_> = map.keys().map(|k| k.0.as_str()).collect();
    assert_eq!(keys, vec!["id001", "file2.txt", "file10.txt"]); // 'i' > 'f' ERROR AGAIN. 'f' < 'i'.
    assert_eq!(keys, vec!["file2.txt", "file10.txt", "id001"]); // Corrected based on 'f' < 'i'


    // --- Using a comparison function directly ---
    let mut data = vec!["img12.png", "img2.png", "img100.png"];
    data.sort_by(natlex_sort::nat_lex_cmp);
    assert_eq!(data, vec!["img2.png", "img12.png", "img100.png"]);