ps_str/utf8_encoder/variants/bytes.rs
1use crate::{Utf8Encoder, CHARS};
2
3impl<T: AsRef<[u8]>> Utf8Encoder for T {
4 /// Converts a byte sequence into a UTF-8 string, replacing invalid bytes
5 /// with fallback characters.
6 ///
7 /// This method processes bytes sequentially, attempting to parse valid UTF-8
8 /// sequences. When valid UTF-8 is encountered, it is appended directly to the
9 /// result string. When an invalid UTF-8 sequence is detected, the valid portion
10 /// preceding the error is appended, then the offending byte is replaced, and
11 /// processing continues from the next byte.
12 ///
13 /// # Returns
14 ///
15 /// A `String` containing the decoded content, guaranteed to be valid UTF-8
16 /// even if the input contained invalid byte sequences.
17 ///
18 /// # Example
19 ///
20 /// ```
21 /// use ps_str::Utf8Encoder;
22 ///
23 /// let bytes = b"Hello\xFFWorld";
24 /// let result = bytes.to_utf8_string();
25 ///
26 /// assert_eq!(result, "Hello˙World");
27 /// ```
28 fn to_utf8_string(&self) -> String {
29 let bytes = self.as_ref();
30 let mut result = String::with_capacity(bytes.len());
31 let mut i = 0;
32
33 while i < bytes.len() {
34 match std::str::from_utf8(&bytes[i..]) {
35 Ok(valid_str) => {
36 result.push_str(valid_str);
37 break;
38 }
39 Err(e) => {
40 if e.valid_up_to() > 0 {
41 let valid_part = &bytes[i..i + e.valid_up_to()];
42
43 // SAFETY: valid_up_to() guarantees this slice contains only complete, valid UTF-8 characters
44 result.push_str(unsafe { std::str::from_utf8_unchecked(valid_part) });
45 i += e.valid_up_to();
46 }
47
48 if let Some(c) = bytes.get(i) {
49 result.push(CHARS[*c as usize]);
50 i += 1;
51 }
52 }
53 }
54 }
55
56 result
57 }
58}