writeable/
impls.rs

1// This file is part of ICU4X. For terms of use, please see the file
2// called LICENSE at the top level of the ICU4X source tree
3// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4
5use crate::*;
6use core::fmt;
7
8macro_rules! impl_write_num {
9    ($u:ty, $i:ty, $test:ident) => {
10        impl $crate::Writeable for $u {
11            fn write_to<W: core::fmt::Write + ?Sized>(&self, sink: &mut W) -> core::fmt::Result {
12                const MAX_LEN: usize = <$u>::MAX.ilog10() as usize + 1;
13                let mut buf = [b'0'; MAX_LEN];
14                let mut n = *self;
15                let mut i = MAX_LEN;
16                #[allow(clippy::indexing_slicing)] // n < 10^i
17                while n != 0 {
18                    i -= 1;
19                    buf[i] = b'0' + (n % 10) as u8;
20                    n /= 10;
21                }
22                if i == MAX_LEN {
23                    debug_assert_eq!(*self, 0);
24                    i -= 1;
25                }
26                #[allow(clippy::indexing_slicing)] // buf is ASCII
27                let s = unsafe { core::str::from_utf8_unchecked(&buf[i..]) };
28                sink.write_str(s)
29            }
30
31            fn writeable_length_hint(&self) -> $crate::LengthHint {
32                LengthHint::exact(self.checked_ilog10().unwrap_or(0) as usize + 1)
33            }
34        }
35
36        impl $crate::Writeable for $i {
37            fn write_to<W: core::fmt::Write + ?Sized>(&self, sink: &mut W) -> core::fmt::Result {
38                if self.is_negative() {
39                    sink.write_str("-")?;
40                }
41                self.unsigned_abs().write_to(sink)
42            }
43
44            fn writeable_length_hint(&self) -> $crate::LengthHint {
45                $crate::LengthHint::exact(if self.is_negative() { 1 } else { 0 })
46                    + self.unsigned_abs().writeable_length_hint()
47            }
48        }
49
50        #[test]
51        fn $test() {
52            use $crate::assert_writeable_eq;
53            assert_writeable_eq!(&(0 as $u), "0");
54            assert_writeable_eq!(&(0 as $i), "0");
55            assert_writeable_eq!(&(-0 as $i), "0");
56            assert_writeable_eq!(&(1 as $u), "1");
57            assert_writeable_eq!(&(1 as $i), "1");
58            assert_writeable_eq!(&(-1 as $i), "-1");
59            assert_writeable_eq!(&(9 as $u), "9");
60            assert_writeable_eq!(&(9 as $i), "9");
61            assert_writeable_eq!(&(-9 as $i), "-9");
62            assert_writeable_eq!(&(10 as $u), "10");
63            assert_writeable_eq!(&(10 as $i), "10");
64            assert_writeable_eq!(&(-10 as $i), "-10");
65            assert_writeable_eq!(&(99 as $u), "99");
66            assert_writeable_eq!(&(99 as $i), "99");
67            assert_writeable_eq!(&(-99 as $i), "-99");
68            assert_writeable_eq!(&(100 as $u), "100");
69            assert_writeable_eq!(&(-100 as $i), "-100");
70            assert_writeable_eq!(&<$u>::MAX, <$u>::MAX.to_string());
71            assert_writeable_eq!(&<$i>::MAX, <$i>::MAX.to_string());
72            assert_writeable_eq!(&<$i>::MIN, <$i>::MIN.to_string());
73
74            use rand::{rngs::SmallRng, Rng, SeedableRng};
75            let mut rng = SmallRng::seed_from_u64(4); // chosen by fair dice roll.
76                                                      // guaranteed to be random.
77            for _ in 0..1000 {
78                let rand = rng.gen::<$u>();
79                assert_writeable_eq!(rand, rand.to_string());
80            }
81        }
82    };
83}
84
85impl_write_num!(u8, i8, test_u8);
86impl_write_num!(u16, i16, test_u16);
87impl_write_num!(u32, i32, test_u32);
88impl_write_num!(u64, i64, test_u64);
89impl_write_num!(u128, i128, test_u128);
90impl_write_num!(usize, isize, test_usize);
91
92impl Writeable for str {
93    #[inline]
94    fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
95        sink.write_str(self)
96    }
97
98    #[inline]
99    fn writeable_length_hint(&self) -> LengthHint {
100        LengthHint::exact(self.len())
101    }
102
103    /// Returns a borrowed `str`.
104    ///
105    /// # Examples
106    ///
107    /// ```
108    /// use std::borrow::Cow;
109    /// use writeable::Writeable;
110    ///
111    /// let cow = "foo".write_to_string();
112    /// assert!(matches!(cow, Cow::Borrowed(_)));
113    /// ```
114    #[inline]
115    fn write_to_string(&self) -> Cow<str> {
116        Cow::Borrowed(self)
117    }
118}
119
120impl Writeable for String {
121    #[inline]
122    fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
123        sink.write_str(self)
124    }
125
126    #[inline]
127    fn writeable_length_hint(&self) -> LengthHint {
128        LengthHint::exact(self.len())
129    }
130
131    #[inline]
132    fn write_to_string(&self) -> Cow<str> {
133        Cow::Borrowed(self)
134    }
135}
136
137impl Writeable for char {
138    #[inline]
139    fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
140        sink.write_char(*self)
141    }
142
143    #[inline]
144    fn writeable_length_hint(&self) -> LengthHint {
145        LengthHint::exact(self.len_utf8())
146    }
147
148    #[inline]
149    fn write_to_string(&self) -> Cow<str> {
150        let mut s = String::with_capacity(self.len_utf8());
151        s.push(*self);
152        Cow::Owned(s)
153    }
154}
155
156impl<T: Writeable + ?Sized> Writeable for &T {
157    #[inline]
158    fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
159        (*self).write_to(sink)
160    }
161
162    #[inline]
163    fn write_to_parts<W: PartsWrite + ?Sized>(&self, sink: &mut W) -> fmt::Result {
164        (*self).write_to_parts(sink)
165    }
166
167    #[inline]
168    fn writeable_length_hint(&self) -> LengthHint {
169        (*self).writeable_length_hint()
170    }
171
172    #[inline]
173    fn write_to_string(&self) -> Cow<str> {
174        (*self).write_to_string()
175    }
176}
177
178macro_rules! impl_write_smart_pointer {
179    ($ty:path, T: $extra_bound:path) => {
180        impl<'a, T: ?Sized + Writeable + $extra_bound> Writeable for $ty {
181            #[inline]
182            fn write_to<W: fmt::Write + ?Sized>(&self, sink: &mut W) -> fmt::Result {
183                core::borrow::Borrow::<T>::borrow(self).write_to(sink)
184            }
185            #[inline]
186            fn write_to_parts<W: PartsWrite + ?Sized>(&self, sink: &mut W) -> fmt::Result {
187                core::borrow::Borrow::<T>::borrow(self).write_to_parts(sink)
188            }
189            #[inline]
190            fn writeable_length_hint(&self) -> LengthHint {
191                core::borrow::Borrow::<T>::borrow(self).writeable_length_hint()
192            }
193            #[inline]
194            fn write_to_string(&self) -> Cow<str> {
195                core::borrow::Borrow::<T>::borrow(self).write_to_string()
196            }
197        }
198    };
199    ($ty:path) => {
200        // Add a harmless duplicate Writeable bound
201        impl_write_smart_pointer!($ty, T: Writeable);
202    };
203}
204
205impl_write_smart_pointer!(Cow<'a, T>, T: alloc::borrow::ToOwned);
206impl_write_smart_pointer!(alloc::boxed::Box<T>);
207impl_write_smart_pointer!(alloc::rc::Rc<T>);
208impl_write_smart_pointer!(alloc::sync::Arc<T>);
209
210#[test]
211fn test_string_impls() {
212    fn check_writeable_slice<W: Writeable + core::fmt::Display>(writeables: &[W]) {
213        assert_writeable_eq!(&writeables[0], "");
214        assert_writeable_eq!(&writeables[1], "abc");
215        assert!(matches!(writeables[0].write_to_string(), Cow::Borrowed(_)));
216        assert!(matches!(writeables[1].write_to_string(), Cow::Borrowed(_)));
217    }
218
219    // test str impl
220    let arr: &[&str] = &["", "abc"];
221    check_writeable_slice(arr);
222
223    // test String impl
224    let arr: &[String] = &[String::new(), "abc".to_owned()];
225    check_writeable_slice(arr);
226
227    // test char impl
228    let chars = ['a', 'β', '你', '😀'];
229    for i in 0..chars.len() {
230        let s = String::from(chars[i]);
231        assert_writeable_eq!(&chars[i], s);
232        for j in 0..chars.len() {
233            assert_eq!(
234                crate::cmp_str(&chars[j], &s),
235                chars[j].cmp(&chars[i]),
236                "{:?} vs {:?}",
237                chars[j],
238                chars[i]
239            );
240        }
241    }
242
243    // test Cow impl
244    let arr: &[Cow<str>] = &[Cow::Borrowed(""), Cow::Owned("abc".to_string())];
245    check_writeable_slice(arr);
246
247    // test Box impl
248    let arr: &[Box<str>] = &["".into(), "abc".into()];
249    check_writeable_slice(arr);
250
251    // test Rc impl
252    let arr: &[alloc::rc::Rc<str>] = &["".into(), "abc".into()];
253    check_writeable_slice(arr);
254
255    // test Arc impl
256    let arr: &[alloc::sync::Arc<str>] = &["".into(), "abc".into()];
257    check_writeable_slice(arr);
258
259    // test &T impl
260    let arr: &[&String] = &[&String::new(), &"abc".to_owned()];
261    check_writeable_slice(arr);
262}