1use alloc::string::String;
2use core::fmt::{
3 self,
4 Write,
5};
6use core::num;
7
8use castaway::{
9 match_type,
10 LifetimeFree,
11};
12
13use super::repr::{
14 IntoRepr,
15 Repr,
16};
17use crate::{
18 CompactString,
19 ToCompactStringError,
20 UnwrapWithMsg,
21};
22
23pub trait ToCompactString {
30 #[inline]
51 #[track_caller]
52 fn to_compact_string(&self) -> CompactString {
53 self.try_to_compact_string().unwrap_with_msg()
54 }
55
56 fn try_to_compact_string(&self) -> Result<CompactString, ToCompactStringError>;
62}
63
64unsafe impl LifetimeFree for CompactString {}
70unsafe impl LifetimeFree for Repr {}
71
72impl<T: fmt::Display> ToCompactString for T {
90 #[inline]
91 fn try_to_compact_string(&self) -> Result<CompactString, ToCompactStringError> {
92 let repr = match_type!(self, {
93 &u8 as s => s.into_repr()?,
94 &i8 as s => s.into_repr()?,
95 &u16 as s => s.into_repr()?,
96 &i16 as s => s.into_repr()?,
97 &u32 as s => s.into_repr()?,
98 &i32 as s => s.into_repr()?,
99 &u64 as s => s.into_repr()?,
100 &i64 as s => s.into_repr()?,
101 &u128 as s => s.into_repr()?,
102 &i128 as s => s.into_repr()?,
103 &usize as s => s.into_repr()?,
104 &isize as s => s.into_repr()?,
105 &f32 as s => s.into_repr()?,
106 &f64 as s => s.into_repr()?,
107 &bool as s => s.into_repr()?,
108 &char as s => s.into_repr()?,
109 &String as s => Repr::new(s)?,
110 &CompactString as s => Repr::new(s)?,
111 &num::NonZeroU8 as s => s.into_repr()?,
112 &num::NonZeroI8 as s => s.into_repr()?,
113 &num::NonZeroU16 as s => s.into_repr()?,
114 &num::NonZeroI16 as s => s.into_repr()?,
115 &num::NonZeroU32 as s => s.into_repr()?,
116 &num::NonZeroI32 as s => s.into_repr()?,
117 &num::NonZeroU64 as s => s.into_repr()?,
118 &num::NonZeroI64 as s => s.into_repr()?,
119 &num::NonZeroUsize as s => s.into_repr()?,
120 &num::NonZeroIsize as s => s.into_repr()?,
121 &num::NonZeroU128 as s => s.into_repr()?,
122 &num::NonZeroI128 as s => s.into_repr()?,
123 s => {
124 let mut c = CompactString::const_new("");
125 write!(c, "{}", s)?;
126 return Ok(c);
127 }
128 });
129
130 Ok(CompactString(repr))
131 }
132}
133
134pub trait CompactStringExt {
156 fn concat_compact(&self) -> CompactString;
168
169 fn join_compact<S: AsRef<str>>(&self, separator: S) -> CompactString;
182}
183
184impl<I, C> CompactStringExt for C
185where
186 I: AsRef<str>,
187 for<'a> &'a C: IntoIterator<Item = &'a I>,
188{
189 fn concat_compact(&self) -> CompactString {
190 self.into_iter()
191 .fold(CompactString::const_new(""), |mut s, item| {
192 s.push_str(item.as_ref());
193 s
194 })
195 }
196
197 fn join_compact<S: AsRef<str>>(&self, separator: S) -> CompactString {
198 let mut compact_string = CompactString::const_new("");
199
200 let mut iter = self.into_iter().peekable();
201 let sep = separator.as_ref();
202
203 while let Some(item) = iter.next() {
204 compact_string.push_str(item.as_ref());
205 if iter.peek().is_some() {
206 compact_string.push_str(sep);
207 }
208 }
209
210 compact_string
211 }
212}
213
214#[cfg(test)]
215mod tests {
216 use alloc::string::{
217 String,
218 ToString,
219 };
220 use alloc::vec::Vec;
221 use core::num;
222
223 use proptest::prelude::*;
224 use test_strategy::proptest;
225
226 use super::{
227 CompactStringExt,
228 ToCompactString,
229 };
230 use crate::CompactString;
231
232 #[test]
233 fn test_join() {
234 let slice = ["hello", "world"];
235 let c = slice.join_compact(" ");
236 assert_eq!(c, "hello world");
237
238 let vector = vec!["🍎", "🍊", "🍌"];
239 let c = vector.join_compact(",");
240 assert_eq!(c, "🍎,🍊,🍌");
241 }
242
243 #[proptest]
244 #[cfg_attr(miri, ignore)]
245 fn proptest_join(items: Vec<String>, separator: String) {
246 let c: CompactString = items.join_compact(&separator);
247 let s: String = items.join(&separator);
248 assert_eq!(c, s);
249 }
250
251 #[test]
252 fn test_concat() {
253 let items = vec!["hello", "world"];
254 let c = items.join_compact(" ");
255 assert_eq!(c, "hello world");
256
257 let vector = vec!["🍎", "🍊", "🍌"];
258 let c = vector.concat_compact();
259 assert_eq!(c, "🍎🍊🍌");
260 }
261
262 #[proptest]
263 #[cfg_attr(miri, ignore)]
264 fn proptest_concat(items: Vec<String>) {
265 let c: CompactString = items.concat_compact();
266 let s: String = items.concat();
267 assert_eq!(c, s);
268 }
269
270 #[proptest]
271 #[cfg_attr(miri, ignore)]
272 fn proptest_to_compact_string_u8(val: u8) {
273 let compact = val.to_compact_string();
274 prop_assert_eq!(compact.as_str(), val.to_string());
275 }
276
277 #[proptest]
278 #[cfg_attr(miri, ignore)]
279 fn proptest_to_compact_string_i8(val: i8) {
280 let compact = val.to_compact_string();
281 prop_assert_eq!(compact.as_str(), val.to_string());
282 }
283
284 #[proptest]
285 #[cfg_attr(miri, ignore)]
286 fn proptest_to_compact_string_u16(val: u16) {
287 let compact = val.to_compact_string();
288 prop_assert_eq!(compact.as_str(), val.to_string());
289 }
290
291 #[proptest]
292 #[cfg_attr(miri, ignore)]
293 fn proptest_to_compact_string_i16(val: i16) {
294 let compact = val.to_compact_string();
295 prop_assert_eq!(compact.as_str(), val.to_string());
296 }
297 #[proptest]
298 #[cfg_attr(miri, ignore)]
299 fn proptest_to_compact_string_u32(val: u32) {
300 let compact = val.to_compact_string();
301 prop_assert_eq!(compact.as_str(), val.to_string());
302 }
303 #[proptest]
304 #[cfg_attr(miri, ignore)]
305 fn proptest_to_compact_string_i32(val: i32) {
306 let compact = val.to_compact_string();
307 prop_assert_eq!(compact.as_str(), val.to_string());
308 }
309 #[proptest]
310 #[cfg_attr(miri, ignore)]
311 fn proptest_to_compact_string_u64(val: u64) {
312 let compact = val.to_compact_string();
313 prop_assert_eq!(compact.as_str(), val.to_string());
314 }
315 #[proptest]
316 #[cfg_attr(miri, ignore)]
317 fn proptest_to_compact_string_i64(val: i64) {
318 let compact = val.to_compact_string();
319 prop_assert_eq!(compact.as_str(), val.to_string());
320 }
321 #[proptest]
322 #[cfg_attr(miri, ignore)]
323 fn proptest_to_compact_string_usize(val: usize) {
324 let compact = val.to_compact_string();
325 prop_assert_eq!(compact.as_str(), val.to_string());
326 }
327 #[proptest]
328 #[cfg_attr(miri, ignore)]
329 fn proptest_to_compact_string_isize(val: isize) {
330 let compact = val.to_compact_string();
331 prop_assert_eq!(compact.as_str(), val.to_string());
332 }
333 #[proptest]
334 #[cfg_attr(miri, ignore)]
335 fn proptest_to_compact_string_u128(val: u128) {
336 let compact = val.to_compact_string();
337 prop_assert_eq!(compact.as_str(), val.to_string());
338 }
339 #[proptest]
340 #[cfg_attr(miri, ignore)]
341 fn proptest_to_compact_string_i128(val: i128) {
342 let compact = val.to_compact_string();
343 prop_assert_eq!(compact.as_str(), val.to_string());
344 }
345
346 #[proptest]
347 #[cfg_attr(miri, ignore)]
348 fn proptest_to_compact_string_non_zero_u8(
349 #[strategy((1..=u8::MAX).prop_map(|x| unsafe { num::NonZeroU8::new_unchecked(x)} ))]
350 val: num::NonZeroU8,
351 ) {
352 let compact = val.to_compact_string();
353 prop_assert_eq!(compact.as_str(), val.to_string());
354 }
355
356 #[proptest]
357 #[cfg_attr(miri, ignore)]
358 fn proptest_to_compact_string_non_zero_u16(
359 #[strategy((1..=u16::MAX).prop_map(|x| unsafe { num::NonZeroU16::new_unchecked(x)} ))]
360 val: num::NonZeroU16,
361 ) {
362 let compact = val.to_compact_string();
363 prop_assert_eq!(compact.as_str(), val.to_string());
364 }
365
366 #[proptest]
367 #[cfg_attr(miri, ignore)]
368 fn proptest_to_compact_string_non_zero_u32(
369 #[strategy((1..=u32::MAX).prop_map(|x| unsafe { num::NonZeroU32::new_unchecked(x)} ))]
370 val: num::NonZeroU32,
371 ) {
372 let compact = val.to_compact_string();
373 prop_assert_eq!(compact.as_str(), val.to_string());
374 }
375
376 #[proptest]
377 #[cfg_attr(miri, ignore)]
378 fn proptest_to_compact_string_non_zero_u64(
379 #[strategy((1..=u64::MAX).prop_map(|x| unsafe { num::NonZeroU64::new_unchecked(x)} ))]
380 val: num::NonZeroU64,
381 ) {
382 let compact = val.to_compact_string();
383 prop_assert_eq!(compact.as_str(), val.to_string());
384 }
385
386 #[proptest]
387 #[cfg_attr(miri, ignore)]
388 fn proptest_to_compact_string_non_zero_u128(
389 #[strategy((1..=u128::MAX).prop_map(|x| unsafe { num::NonZeroU128::new_unchecked(x)} ))]
390 val: num::NonZeroU128,
391 ) {
392 let compact = val.to_compact_string();
393 prop_assert_eq!(compact.as_str(), val.to_string());
394 }
395
396 #[proptest]
397 #[cfg_attr(miri, ignore)]
398 fn proptest_to_compact_string_non_zero_usize(
399 #[strategy((1..=usize::MAX).prop_map(|x| unsafe { num::NonZeroUsize::new_unchecked(x)} ))]
400 val: num::NonZeroUsize,
401 ) {
402 let compact = val.to_compact_string();
403 prop_assert_eq!(compact.as_str(), val.to_string());
404 }
405
406 #[proptest]
407 #[cfg_attr(miri, ignore)]
408 fn proptest_to_compact_string_non_zero_i8(
409 #[strategy((1..=u8::MAX).prop_map(|x| unsafe { num::NonZeroI8::new_unchecked(x as i8)} ))]
410 val: num::NonZeroI8,
411 ) {
412 let compact = val.to_compact_string();
413 prop_assert_eq!(compact.as_str(), val.to_string());
414 }
415
416 #[proptest]
417 #[cfg_attr(miri, ignore)]
418 fn proptest_to_compact_string_non_zero_i16(
419 #[strategy((1..=u16::MAX).prop_map(|x| unsafe { num::NonZeroI16::new_unchecked(x as i16)} ))]
420 val: num::NonZeroI16,
421 ) {
422 let compact = val.to_compact_string();
423 prop_assert_eq!(compact.as_str(), val.to_string());
424 }
425
426 #[proptest]
427 #[cfg_attr(miri, ignore)]
428 fn proptest_to_compact_string_non_zero_i32(
429 #[strategy((1..=u32::MAX).prop_map(|x| unsafe { num::NonZeroI32::new_unchecked(x as i32)} ))]
430 val: num::NonZeroI32,
431 ) {
432 let compact = val.to_compact_string();
433 prop_assert_eq!(compact.as_str(), val.to_string());
434 }
435
436 #[proptest]
437 #[cfg_attr(miri, ignore)]
438 fn proptest_to_compact_string_non_zero_i64(
439 #[strategy((1..=u64::MAX).prop_map(|x| unsafe { num::NonZeroI64::new_unchecked(x as i64)} ))]
440 val: num::NonZeroI64,
441 ) {
442 let compact = val.to_compact_string();
443 prop_assert_eq!(compact.as_str(), val.to_string());
444 }
445
446 #[proptest]
447 #[cfg_attr(miri, ignore)]
448 fn proptest_to_compact_string_non_zero_i128(
449 #[strategy((1..=u128::MAX).prop_map(|x| unsafe { num::NonZeroI128::new_unchecked(x as i128)} ))]
450 val: num::NonZeroI128,
451 ) {
452 let compact = val.to_compact_string();
453 prop_assert_eq!(compact.as_str(), val.to_string());
454 }
455
456 #[proptest]
457 #[cfg_attr(miri, ignore)]
458 fn proptest_to_compact_string_non_zero_isize(
459 #[strategy((1..=usize::MAX).prop_map(|x| unsafe { num::NonZeroIsize::new_unchecked(x as isize)} ))]
460 val: num::NonZeroIsize,
461 ) {
462 let compact = val.to_compact_string();
463 prop_assert_eq!(compact.as_str(), val.to_string());
464 }
465}