potential_utf/
writeable.rs1use crate::{PotentialUtf16, PotentialUtf8};
6use core::fmt::Write;
7use writeable::{LengthHint, Part, PartsWrite, TryWriteable};
8
9use core::{char::DecodeUtf16Error, fmt, str::Utf8Error};
10
11impl TryWriteable for &'_ PotentialUtf8 {
13 type Error = Utf8Error;
14
15 fn try_write_to_parts<S: PartsWrite + ?Sized>(
16 &self,
17 sink: &mut S,
18 ) -> Result<Result<(), Self::Error>, fmt::Error> {
19 let mut remaining = &self.0;
20 let mut r = Ok(());
21 loop {
22 match core::str::from_utf8(remaining) {
23 Ok(valid) => {
24 sink.write_str(valid)?;
25 return Ok(r);
26 }
27 Err(e) => {
28 let valid = unsafe {
30 core::str::from_utf8_unchecked(remaining.get_unchecked(..e.valid_up_to()))
31 };
32 sink.write_str(valid)?;
33 sink.with_part(Part::ERROR, |s| s.write_char(char::REPLACEMENT_CHARACTER))?;
34 if r.is_ok() {
35 r = Err(e);
36 }
37 let Some(error_len) = e.error_len() else {
38 return Ok(r); };
40 remaining = unsafe { remaining.get_unchecked(e.valid_up_to() + error_len..) }
42 }
43 }
44 }
45 }
46
47 fn writeable_length_hint(&self) -> LengthHint {
48 LengthHint::between(self.0.len(), self.0.len() * 3)
50 }
51}
52
53impl TryWriteable for &'_ PotentialUtf16 {
55 type Error = DecodeUtf16Error;
56
57 fn try_write_to_parts<S: PartsWrite + ?Sized>(
58 &self,
59 sink: &mut S,
60 ) -> Result<Result<(), Self::Error>, fmt::Error> {
61 let mut r = Ok(());
62 for c in char::decode_utf16(self.0.iter().copied()) {
63 match c {
64 Ok(c) => sink.write_char(c)?,
65 Err(e) => {
66 if r.is_ok() {
67 r = Err(e);
68 }
69 sink.with_part(Part::ERROR, |s| s.write_char(char::REPLACEMENT_CHARACTER))?;
70 }
71 }
72 }
73 Ok(r)
74 }
75
76 fn writeable_length_hint(&self) -> LengthHint {
77 LengthHint::between(self.0.len(), self.0.len() * 3)
79 }
80}
81
82#[cfg(test)]
83mod test {
84 #![allow(invalid_from_utf8)] use super::*;
86 use writeable::assert_try_writeable_parts_eq;
87
88 #[test]
89 fn test_utf8() {
90 assert_try_writeable_parts_eq!(
91 PotentialUtf8::from_bytes(b"Foo Bar"),
92 "Foo Bar",
93 Ok(()),
94 []
95 );
96 assert_try_writeable_parts_eq!(
97 PotentialUtf8::from_bytes(b"Foo\xFDBar"),
98 "Foo�Bar",
99 Err(core::str::from_utf8(b"Foo\xFDBar").unwrap_err()),
100 [(3, 6, Part::ERROR)]
101 );
102 assert_try_writeable_parts_eq!(
103 PotentialUtf8::from_bytes(b"Foo\xFDBar\xff"),
104 "Foo�Bar�",
105 Err(core::str::from_utf8(b"Foo\xFDBar\xff").unwrap_err()),
106 [(3, 6, Part::ERROR), (9, 12, Part::ERROR)],
107 );
108 }
109
110 #[test]
111 fn test_utf16() {
112 assert_try_writeable_parts_eq!(
113 PotentialUtf16::from_slice(&[0xD83E, 0xDD73]),
114 "🥳",
115 Ok(()),
116 []
117 );
118 assert_try_writeable_parts_eq!(
119 PotentialUtf16::from_slice(&[0xD83E, 0x20, 0xDD73]),
120 "� �",
121 Err(char::decode_utf16([0xD83E].into_iter())
122 .next()
123 .unwrap()
124 .unwrap_err()),
125 [(0, 3, Part::ERROR), (4, 7, Part::ERROR)]
126 );
127 }
128}