1use std::{
2 cell::{Cell, RefCell},
3 fmt,
4};
5
6pub use fmt::*;
7
8pub fn from_fn<F>(f: F) -> FromFn<F>
10where
11 F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result,
12{
13 FromFn(f)
14}
15
16pub struct FromFn<F>(F);
18
19impl<F> fmt::Display for FromFn<F>
20where
21 F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result,
22{
23 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24 (self.0)(f)
25 }
26}
27
28impl<F> fmt::Debug for FromFn<F>
29where
30 F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result,
31{
32 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33 (self.0)(f)
34 }
35}
36
37pub trait FmtIteratorExt: Iterator + Sized {
39 fn format<'a>(self, separator: &'a str) -> Format<'a, Self>
41 where
42 Self::Item: fmt::Display,
43 {
44 Format { iter: Cell::new(Some(self)), separator }
45 }
46
47 fn format_with<'a, F>(self, separator: &'a str, format: F) -> FormatWith<'a, Self, F>
49 where
50 F: FnMut(&mut fmt::Formatter<'_>, Self::Item) -> fmt::Result,
51 {
52 FormatWith { inner: RefCell::new(Some((self, format))), separator }
53 }
54}
55
56impl<I: Iterator> FmtIteratorExt for I {}
57
58pub struct Format<'a, I> {
60 iter: Cell<Option<I>>,
61 separator: &'a str,
62}
63
64impl<I> fmt::Display for Format<'_, I>
65where
66 I: Iterator,
67 I::Item: fmt::Display,
68{
69 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70 let iter = self.iter.take().expect("format called twice");
71 for (i, item) in iter.enumerate() {
72 if i != 0 {
73 f.write_str(self.separator)?;
74 }
75 write!(f, "{item}")?;
76 }
77 Ok(())
78 }
79}
80
81pub struct FormatWith<'a, I, F> {
83 inner: RefCell<Option<(I, F)>>,
84 separator: &'a str,
85}
86
87impl<I, F> fmt::Display for FormatWith<'_, I, F>
88where
89 I: Iterator,
90 F: FnMut(&mut fmt::Formatter<'_>, I::Item) -> fmt::Result,
91{
92 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93 let (iter, mut format) = self.inner.borrow_mut().take().expect("format_with called twice");
94 for (i, item) in iter.enumerate() {
95 if i != 0 {
96 f.write_str(self.separator)?;
97 }
98 format(f, item)?;
99 }
100 Ok(())
101 }
102}
103
104pub fn or_list<I>(list: I) -> impl fmt::Display
106where
107 I: IntoIterator<IntoIter: ExactSizeIterator, Item: fmt::Display>,
108{
109 let list = Cell::new(Some(list.into_iter()));
110 from_fn(move |f| {
111 let list = list.take().expect("or_list called twice");
112 let len = list.len();
113 for (i, t) in list.enumerate() {
114 if i > 0 {
115 let is_last = i == len - 1;
116 f.write_str(if len > 2 && is_last {
117 ", or "
118 } else if len == 2 && is_last {
119 " or "
120 } else {
121 ", "
122 })?;
123 }
124 write!(f, "{t}")?;
125 }
126 Ok(())
127 })
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133
134 #[test]
135 fn test_or_list() {
136 let tests: &[(&[&str], &str)] = &[
137 (&[], ""),
138 (&["`<eof>`"], "`<eof>`"),
139 (&["integer", "identifier"], "integer or identifier"),
140 (&["path", "string literal", "`&&`"], "path, string literal, or `&&`"),
141 (&["`&&`", "`||`", "`&&`", "`||`"], "`&&`, `||`, `&&`, or `||`"),
142 ];
143 for &(tokens, expected) in tests {
144 assert_eq!(or_list(tokens).to_string(), expected, "{tokens:?}");
145 }
146 }
147
148 #[test]
149 fn test_format() {
150 assert_eq!([1, 2, 3].iter().format(", ").to_string(), "1, 2, 3");
151 }
152
153 #[test]
154 fn test_format_with() {
155 let values = [1, 2, 3];
156 let formatted = values.iter().format_with(" | ", |f, value| write!(f, "#{value}"));
157 assert_eq!(formatted.to_string(), "#1 | #2 | #3");
158 }
159}