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
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
//! Returns the index of `search` in `subject`.

use regex::Regex;
/// Returns an array of all occurrence index of `search` in `subject` or an empty array if not found. Case sensitive.
///
/// # Arguments
///
/// * `subject` - The string where to search.
/// * `search` - The string to search.
/// * `from_index` - The index to start searching
///
/// # Example
/// ```
/// use voca_rs::*;
/// index::index_all("morning", "n", 0);
/// // => [3, 5]
/// index::index_all("Zażółć gęślą jaźń", "aęą", 0);
/// // => [1, 8, 11, 14]
/// index::index_all("evening", "o", 0);
/// // => []
/// use voca_rs::Voca;
/// "morning"._index_all("n", 0);
/// // => [3, 5]
/// ```
pub fn index_all(subject: &str, search: &str, from_index: usize) -> Vec<usize> {
    if subject.is_empty() || crate::count::count(&subject) < from_index {
        return vec![];
    }
    let string_slice = &subject[subject.char_indices().nth(from_index).unwrap().0..];
    let mut res = Vec::new();
    for (i, c) in crate::split::chars(string_slice).iter().enumerate() {
        if search.contains(c) {
            res.push(i)
        }
    }
    res
}

/// Returns the first occurrence index of `search` in `subject` or -1 if not found. Case sensitive.
///
/// # Arguments
///
/// * `subject` - The string where to search.
/// * `search` - The string to search.
/// * `from_index` - The index to start searching
///
/// # Example
/// ```
/// use voca_rs::*;
/// index::index_of("morning", "n", 0);
/// // => 3
/// index::index_of("Zażółć gęślą jaźń", "gęślą", 0);
/// // => 7
/// index::index_of("evening", "o", 0);
/// // => -1
/// use voca_rs::Voca;
/// "morning"._index_of("n", 0);
/// // => 3
/// ```
pub fn index_of(subject: &str, search: &str, from_index: usize) -> i8 {
    match search.len() {
        0 => 0,
        _ => {
            if crate::count::count(&subject) < from_index {
                return -1;
            }
            let string_slice = &subject[subject.char_indices().nth(from_index).unwrap().0..];
            match crate::split::chars(string_slice)
                .iter()
                .enumerate()
                .position(|(pos, _)| {
                    match &string_slice[string_slice.char_indices().nth(pos).unwrap().0..]
                        .find(search)
                    {
                        Some(x) => *x == 0,
                        None => false,
                    }
                }) {
                Some(x) => x as i8,
                None => -1,
            }
        }
    }
}

/// Returns the last occurrence index of `search` in `subject` or -1 if not found. Case sensitive.
///
/// # Arguments
///
/// * `subject` - The string where to search.
/// * `search` - The string to search.
/// * `from_index` - The index to start searching
///
/// # Example
/// ```
/// use voca_rs::*;
/// index::last_index_of("morning", "n", 0);
/// // => 5
/// index::last_index_of("evening", "o", 0);
/// // => -1
/// use voca_rs::Voca;
/// "morning"._last_index_of("n", 0);
/// // => 5
/// ```
pub fn last_index_of(subject: &str, search: &str, from_index: usize) -> i8 {
    match search.len() {
        0 => 0,
        _ => {
            if crate::count::count(&subject) < from_index {
                return -1;
            }
            let string_slice = &subject[subject.char_indices().nth(from_index).unwrap().0..];
            let string_chars = crate::split::chars(string_slice);
            match string_chars.iter().enumerate().rev().position(|(pos, _)| {
                match &string_slice[string_slice.char_indices().nth(pos).unwrap().0..].find(search)
                {
                    Some(x) => *x == 0,
                    None => false,
                }
            }) {
                Some(x) => (string_chars.len() - x - 1) as i8,
                None => -1,
            }
        }
    }
}

/// Returns the first index of a `pattern` match in `subject`.
/// NOTE: Executes regular expressions only on valid UTF-8 while exposing match locations as byte indices into the search string (see case #3).
///
/// # Arguments
///
/// * `subject` - The string where to search.
/// * `pattern` - The RegExp pattern to search, it is transformed to Regex::new(pattern).
/// * `from_index` - The index to start searching.
///
/// # Example
/// ```
/// use voca_rs::*;
/// index::search("morning", "rn", 0);
/// // => 2
/// index::search("evening", r"\d", 0);
/// // => -1
/// index::search("Zażółć gęślą jaźń", "gęślą", 6);
/// // => 11 (substring's position in `subject`), not 7
/// use voca_rs::Voca;
/// "morning"._search("rn", 0);
/// // => 2
/// ```
pub fn search(subject: &str, pattern: &str, from_index: usize) -> i8 {
    if from_index >= crate::split::chars(&subject).len() {
        return -1;
    }
    match pattern.len() {
        0 => 0,
        _ => {
            let re: Regex = match Regex::new(pattern) {
                Ok(re) => re,
                Err(_) => return -1,
            };
            match re.find_at(&subject, from_index) {
                None => -1,
                Some(x) => x.start() as i8,
            }
        }
    }
}