Convert the given `start` and `count` to valid byte indices within `input` string. Input `start` and `count` are equivalent to PostgreSQL's `substr(s, start, count)`. `start` is 1-based; if `count` is not provided, returns indices to the end of the string. Input indices are character-based, and return values are byte indices. The input bounds can be outside string bounds; this function will retur
(
input: &str,
start: i64,
count: Option<i64>,
is_input_ascii_only: bool,
)
| 168 | /// get_true_start_end('Hi🌏', -10, Some(2)) -> Ok((0, 0)) |
| 169 | /// ``` |
| 170 | pub fn get_true_start_end( |
| 171 | input: &str, |
| 172 | start: i64, |
| 173 | count: Option<i64>, |
| 174 | is_input_ascii_only: bool, |
| 175 | ) -> Result<(usize, usize)> { |
| 176 | if let Some(count) = count |
| 177 | && count < 0 |
| 178 | { |
| 179 | return exec_err!("negative count not allowed: {count}"); |
| 180 | } |
| 181 | |
| 182 | // The caller-provided `start` is 1-indexed. |
| 183 | let Some(start) = start.checked_sub(1) else { |
| 184 | return exec_err!("start position overflow: {start}"); |
| 185 | }; |
| 186 | |
| 187 | let end = match count { |
| 188 | Some(count) => start.saturating_add(count), |
| 189 | None => input.len() as i64, |
| 190 | }; |
| 191 | |
| 192 | let start = start.clamp(0, input.len() as i64) as usize; |
| 193 | let end = end.clamp(0, input.len() as i64) as usize; |
| 194 | |
| 195 | // If input is ASCII-only, byte-based indices equal char-based indices |
| 196 | if is_input_ascii_only { |
| 197 | return Ok((start, end)); |
| 198 | } |
| 199 | |
| 200 | // Otherwise, calculate byte indices from char indices. We initialize both |
| 201 | // `byte_start` and `byte_end` to the string length to handle cases where |
| 202 | // the requested 'start' or 'end' positions are at or beyond the end of the |
| 203 | // string (resulting in an empty substring). |
| 204 | let mut byte_start = input.len(); |
| 205 | let mut byte_end = input.len(); |
| 206 | |
| 207 | for (char_idx, (byte_idx, _)) in input.char_indices().enumerate() { |
| 208 | if char_idx == start { |
| 209 | byte_start = byte_idx; |
| 210 | // If no length is specified, we only need the start offset. |
| 211 | if count.is_none() { |
| 212 | break; |
| 213 | } |
| 214 | } |
| 215 | if char_idx == end { |
| 216 | byte_end = byte_idx; |
| 217 | break; |
| 218 | } |
| 219 | } |
| 220 | |
| 221 | Ok((byte_start, byte_end)) |
| 222 | } |
| 223 | |
| 224 | // String characters are variable length encoded in UTF-8, `substr()` function's |
| 225 | // arguments are character-based, converting them into byte-based indices |
no test coverage detected
searching dependent graphs…