1use core::cell::Cell;
2use core::convert::Infallible;
3use core::fmt::{self, Write};
4use core::ops::Deref;
5use core::pin::Pin;
6
7use super::MAX_LEN;
8use super::escape::FastWritable;
9use crate::{Error, Result};
10
11#[inline]
32pub fn truncate<S: fmt::Display>(
33 source: S,
34 remaining: usize,
35) -> Result<TruncateFilter<S>, Infallible> {
36 Ok(TruncateFilter { source, remaining })
37}
38
39pub struct TruncateFilter<S> {
40 source: S,
41 remaining: usize,
42}
43
44impl<S: fmt::Display> fmt::Display for TruncateFilter<S> {
45 #[inline]
46 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47 write!(TruncateWriter::new(f, self.remaining), "{}", self.source)
48 }
49}
50
51impl<S: FastWritable> FastWritable for TruncateFilter<S> {
52 #[inline]
53 fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
54 self.source
55 .write_into(&mut TruncateWriter::new(dest, self.remaining))
56 }
57}
58
59struct TruncateWriter<W> {
60 dest: Option<W>,
61 remaining: usize,
62}
63
64impl<W> TruncateWriter<W> {
65 fn new(dest: W, remaining: usize) -> Self {
66 TruncateWriter {
67 dest: Some(dest),
68 remaining,
69 }
70 }
71}
72
73impl<W: fmt::Write> fmt::Write for TruncateWriter<W> {
74 fn write_str(&mut self, s: &str) -> fmt::Result {
75 let Some(dest) = &mut self.dest else {
76 return Ok(());
77 };
78 let mut rem = self.remaining;
79 if rem >= s.len() {
80 dest.write_str(s)?;
81 self.remaining -= s.len();
82 } else {
83 if rem > 0 {
84 while !s.is_char_boundary(rem) {
85 rem += 1;
86 }
87 if rem == s.len() {
88 self.remaining = 0;
90 return dest.write_str(s);
91 }
92 dest.write_str(&s[..rem])?;
93 }
94 dest.write_str("...")?;
95 self.dest = None;
96 }
97 Ok(())
98 }
99
100 #[inline]
101 fn write_char(&mut self, c: char) -> fmt::Result {
102 match self.dest.is_some() {
103 true => self.write_str(c.encode_utf8(&mut [0; 4])),
104 false => Ok(()),
105 }
106 }
107
108 #[inline]
109 fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result {
110 match self.dest.is_some() {
111 true => fmt::write(self, args),
112 false => Ok(()),
113 }
114 }
115}
116
117#[inline]
138pub fn join<I, S>(input: I, separator: S) -> Result<JoinFilter<I, S>, Infallible>
139where
140 I: IntoIterator,
141 I::Item: fmt::Display,
142 S: fmt::Display,
143{
144 Ok(JoinFilter(Cell::new(Some((input, separator)))))
145}
146
147pub struct JoinFilter<I, S>(Cell<Option<(I, S)>>);
160
161impl<I, S> fmt::Display for JoinFilter<I, S>
162where
163 I: IntoIterator,
164 I::Item: fmt::Display,
165 S: fmt::Display,
166{
167 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168 let Some((iter, separator)) = self.0.take() else {
169 return Ok(());
170 };
171 for (idx, token) in iter.into_iter().enumerate() {
172 match idx {
173 0 => f.write_fmt(format_args!("{token}"))?,
174 _ => f.write_fmt(format_args!("{separator}{token}"))?,
175 }
176 }
177 Ok(())
178 }
179}
180
181#[inline]
202pub fn center<T: fmt::Display>(src: T, width: usize) -> Result<Center<T>, Infallible> {
203 Ok(Center { src, width })
204}
205
206pub struct Center<T> {
207 src: T,
208 width: usize,
209}
210
211impl<T: fmt::Display> fmt::Display for Center<T> {
212 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213 if self.width < MAX_LEN {
214 write!(f, "{: ^1$}", self.src, self.width)
215 } else {
216 write!(f, "{}", self.src)
217 }
218 }
219}
220
221#[inline]
330pub fn pluralize<C, S, P>(count: C, singular: S, plural: P) -> Result<Pluralize<S, P>, C::Error>
331where
332 C: PluralizeCount,
333{
334 match count.is_singular()? {
335 true => Ok(Pluralize::Singular(singular)),
336 false => Ok(Pluralize::Plural(plural)),
337 }
338}
339
340pub trait PluralizeCount {
342 type Error: Into<Error>;
344
345 fn is_singular(&self) -> Result<bool, Self::Error>;
347}
348
349const _: () = {
350 crate::impl_for_ref! {
351 impl PluralizeCount for T {
352 type Error = T::Error;
353
354 #[inline]
355 fn is_singular(&self) -> Result<bool, Self::Error> {
356 <T>::is_singular(self)
357 }
358 }
359 }
360
361 impl<T> PluralizeCount for Pin<T>
362 where
363 T: Deref,
364 <T as Deref>::Target: PluralizeCount,
365 {
366 type Error = <<T as Deref>::Target as PluralizeCount>::Error;
367
368 #[inline]
369 fn is_singular(&self) -> Result<bool, Self::Error> {
370 self.as_ref().get_ref().is_singular()
371 }
372 }
373
374 macro_rules! impl_pluralize_for_unsigned_int {
376 ($($ty:ty)*) => { $(
377 impl PluralizeCount for $ty {
378 type Error = Infallible;
379
380 #[inline]
381 fn is_singular(&self) -> Result<bool, Self::Error> {
382 Ok(*self == 1)
383 }
384 }
385 )* };
386 }
387
388 impl_pluralize_for_unsigned_int!(u8 u16 u32 u64 u128 usize);
389
390 macro_rules! impl_pluralize_for_signed_int {
392 ($($ty:ty)*) => { $(
393 impl PluralizeCount for $ty {
394 type Error = Infallible;
395
396 #[inline]
397 fn is_singular(&self) -> Result<bool, Self::Error> {
398 Ok(*self == 1 || *self == -1)
399 }
400 }
401 )* };
402 }
403
404 impl_pluralize_for_signed_int!(i8 i16 i32 i64 i128 isize);
405
406 macro_rules! impl_pluralize_for_non_zero {
408 ($($ty:ident)*) => { $(
409 impl PluralizeCount for core::num::$ty {
410 type Error = Infallible;
411
412 #[inline]
413 fn is_singular(&self) -> Result<bool, Self::Error> {
414 self.get().is_singular()
415 }
416 }
417 )* };
418 }
419
420 impl_pluralize_for_non_zero! {
421 NonZeroI8 NonZeroI16 NonZeroI32 NonZeroI64 NonZeroI128 NonZeroIsize
422 NonZeroU8 NonZeroU16 NonZeroU32 NonZeroU64 NonZeroU128 NonZeroUsize
423 }
424};
425
426pub enum Pluralize<S, P> {
427 Singular(S),
428 Plural(P),
429}
430
431impl<S: fmt::Display, P: fmt::Display> fmt::Display for Pluralize<S, P> {
432 #[inline]
433 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
434 match self {
435 Pluralize::Singular(value) => write!(f, "{value}"),
436 Pluralize::Plural(value) => write!(f, "{value}"),
437 }
438 }
439}
440
441impl<S: FastWritable, P: FastWritable> FastWritable for Pluralize<S, P> {
442 #[inline]
443 fn write_into<W: fmt::Write + ?Sized>(&self, dest: &mut W) -> crate::Result<()> {
444 match self {
445 Pluralize::Singular(value) => value.write_into(dest),
446 Pluralize::Plural(value) => value.write_into(dest),
447 }
448 }
449}
450
451#[cfg(all(test, feature = "alloc"))]
452mod tests {
453 use alloc::string::{String, ToString};
454 use alloc::vec::Vec;
455
456 use super::*;
457
458 #[allow(clippy::needless_borrow)]
459 #[test]
460 fn test_join() {
461 assert_eq!(
462 join((&["hello", "world"]).iter(), ", ")
463 .unwrap()
464 .to_string(),
465 "hello, world"
466 );
467 assert_eq!(
468 join((&["hello"]).iter(), ", ").unwrap().to_string(),
469 "hello"
470 );
471
472 let empty: &[&str] = &[];
473 assert_eq!(join(empty.iter(), ", ").unwrap().to_string(), "");
474
475 let input: Vec<String> = alloc::vec!["foo".into(), "bar".into(), "bazz".into()];
476 assert_eq!(join(input.iter(), ":").unwrap().to_string(), "foo:bar:bazz");
477
478 let input: &[String] = &["foo".into(), "bar".into()];
479 assert_eq!(join(input.iter(), ":").unwrap().to_string(), "foo:bar");
480
481 let real: String = "blah".into();
482 let input: Vec<&str> = alloc::vec![&real];
483 assert_eq!(join(input.iter(), ";").unwrap().to_string(), "blah");
484
485 assert_eq!(
486 join((&&&&&["foo", "bar"]).iter(), ", ")
487 .unwrap()
488 .to_string(),
489 "foo, bar"
490 );
491 }
492
493 #[test]
494 fn test_center() {
495 assert_eq!(center("f", 3).unwrap().to_string(), " f ".to_string());
496 assert_eq!(center("f", 4).unwrap().to_string(), " f ".to_string());
497 assert_eq!(center("foo", 1).unwrap().to_string(), "foo".to_string());
498 assert_eq!(
499 center("foo bar", 8).unwrap().to_string(),
500 "foo bar ".to_string()
501 );
502 assert_eq!(
503 center("foo", 111_669_149_696).unwrap().to_string(),
504 "foo".to_string()
505 );
506 }
507}