1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Michael Debertol <michael.debertol..AT..gmail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

//! Custom string comparisons.
//!
//! The goal is to compare strings without transforming them first (i.e. not allocating new strings)

use std::cmp::Ordering;

fn filter_char(c: char, ignore_non_printing: bool, ignore_non_dictionary: bool) -> bool {
    if ignore_non_dictionary && !(c.is_ascii_alphanumeric() || c.is_ascii_whitespace()) {
        return false;
    }
    if ignore_non_printing && (c.is_ascii_control() || !c.is_ascii()) {
        return false;
    }
    true
}

fn cmp_chars(a: char, b: char, ignore_case: bool) -> Ordering {
    if ignore_case {
        a.to_ascii_uppercase().cmp(&b.to_ascii_uppercase())
    } else {
        a.cmp(&b)
    }
}

pub fn custom_str_cmp(
    a: &str,
    b: &str,
    ignore_non_printing: bool,
    ignore_non_dictionary: bool,
    ignore_case: bool,
) -> Ordering {
    if !(ignore_case || ignore_non_dictionary || ignore_non_printing) {
        // There are no custom settings. Fall back to the default strcmp, which is faster.
        return a.cmp(b);
    }
    let mut a_chars = a
        .chars()
        .filter(|&c| filter_char(c, ignore_non_printing, ignore_non_dictionary));
    let mut b_chars = b
        .chars()
        .filter(|&c| filter_char(c, ignore_non_printing, ignore_non_dictionary));
    loop {
        let a_char = a_chars.next();
        let b_char = b_chars.next();
        match (a_char, b_char) {
            (None, None) => return Ordering::Equal,
            (Some(_), None) => return Ordering::Greater,
            (None, Some(_)) => return Ordering::Less,
            (Some(a_char), Some(b_char)) => {
                let ordering = cmp_chars(a_char, b_char, ignore_case);
                if ordering != Ordering::Equal {
                    return ordering;
                }
            }
        }
    }
}