Skip to main content

use_utf8/
lib.rs

1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub struct Utf8Stats {
6    pub bytes: usize,
7    pub chars: usize,
8    pub is_ascii: bool,
9}
10
11#[must_use]
12pub fn is_valid_utf8(bytes: &[u8]) -> bool {
13    std::str::from_utf8(bytes).is_ok()
14}
15
16#[must_use]
17pub fn utf8_lossy(bytes: &[u8]) -> String {
18    String::from_utf8_lossy(bytes).into_owned()
19}
20
21#[must_use]
22pub fn utf8_stats(input: &str) -> Utf8Stats {
23    Utf8Stats {
24        bytes: input.len(),
25        chars: input.chars().count(),
26        is_ascii: input.is_ascii(),
27    }
28}
29
30#[must_use]
31pub fn byte_len(input: &str) -> usize {
32    input.len()
33}
34
35#[must_use]
36pub fn char_len(input: &str) -> usize {
37    input.chars().count()
38}
39
40#[must_use]
41pub fn truncate_utf8(input: &str, max_chars: usize) -> String {
42    input.chars().take(max_chars).collect()
43}
44
45#[must_use]
46pub fn truncate_utf8_bytes(input: &str, max_bytes: usize) -> String {
47    let boundary = safe_char_boundary(input, max_bytes);
48    input[..boundary].to_string()
49}
50
51#[must_use]
52pub fn safe_char_boundary(input: &str, index: usize) -> usize {
53    if index >= input.len() {
54        return input.len();
55    }
56
57    let mut boundary = index;
58    while boundary > 0 && !input.is_char_boundary(boundary) {
59        boundary -= 1;
60    }
61    boundary
62}