pub(crate) fn floor_char_boundary(s: &str, pos: usize) -> usize {
if pos >= s.len() {
s.len()
} else if s.is_char_boundary(pos) {
pos
} else {
let mut p = pos;
while p > 0 && !s.is_char_boundary(p) {
p -= 1;
}
p
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_string_any_pos_yields_zero() {
assert_eq!(floor_char_boundary("", 0), 0);
assert_eq!(floor_char_boundary("", 10), 0);
}
#[test]
fn ascii_positions_are_all_boundaries() {
let s = "hello";
for p in 0..=s.len() {
assert_eq!(floor_char_boundary(s, p), p);
}
}
#[test]
fn pos_past_end_clamps_to_length() {
let s = "hi";
assert_eq!(floor_char_boundary(s, 5), s.len());
}
#[test]
fn mid_multibyte_walks_back_to_start_of_char() {
let s = "é";
assert_eq!(s.len(), 2);
assert_eq!(floor_char_boundary(s, 0), 0);
assert_eq!(floor_char_boundary(s, 1), 0);
assert_eq!(floor_char_boundary(s, 2), 2);
}
#[test]
fn mid_emoji_walks_back_to_start() {
let s = "😀";
assert_eq!(s.len(), 4);
assert_eq!(floor_char_boundary(s, 1), 0);
assert_eq!(floor_char_boundary(s, 2), 0);
assert_eq!(floor_char_boundary(s, 3), 0);
assert_eq!(floor_char_boundary(s, 4), 4);
}
}