1#![deny(warnings)]
2#![allow(clippy::needless_doctest_main)]
3
4#![cfg_attr(not(feature = "std"), no_std)]
5
6#![doc=document_features::document_features!()]
14
15#[doc=include_str!("../README.md")]
16type _DocTestReadme = ();
17
18#[cfg(feature = "std")]
19extern crate core;
20
21use core::fmt::{self, Display};
22use core::hint::{unreachable_unchecked};
23
24#[doc(hidden)]
25pub use core::write as std_write;
26
27#[cfg(feature = "std")]
30pub trait AsStrFormatExt: AsRef<str> {
31 fn format<'a, T: Display + ?Sized + 'a>(&self, args: impl IntoIterator<Item=&'a T> + Clone) -> String {
41 format!("{}", Arguments::new(self, args))
42 }
43}
44
45#[cfg(feature = "std")]
46impl<T: AsRef<str>> AsStrFormatExt for T { }
47
48#[macro_export]
69macro_rules! dyn_write {
70 ($dst:expr, $fmt:expr, $args:expr $(,)?) => {
71 $crate::std_write!($dst, "{}", $crate::Arguments::new($fmt, $args))
72 }
73}
74
75#[derive(Clone, Debug)]
78pub struct Arguments<'a, F: AsRef<str>, T: Display + ?Sized + 'a, I: IntoIterator<Item=&'a T> + Clone> {
79 fmt: F,
80 args: I
81}
82
83impl<'a, F: AsRef<str>, T: Display + ?Sized + 'a, I: IntoIterator<Item=&'a T> + Clone> Arguments<'a, F, T, I> {
84 pub fn new(fmt: F, args: I) -> Self { Arguments { fmt, args } }
95}
96
97impl<'a, F: AsRef<str>, T: Display + ?Sized + 'a, I: IntoIterator<Item=&'a T> + Clone> Display for Arguments<'a, F, T, I> {
98 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
99 #[derive(Eq, PartialEq)]
100 enum Brace { Left, Right }
101 fn as_brace(c: u8) -> Option<Brace> {
102 match c {
103 b'{' => Some(Brace::Left),
104 b'}' => Some(Brace::Right),
105 _ => None
106 }
107 }
108 let mut args = self.args.clone().into_iter();
109 let mut fmt = self.fmt.as_ref();
110 let mut piece_end = 0;
111 enum State { Piece, Arg }
112 let mut state = State::Piece;
113 loop {
114 match state {
115 State::Piece => match fmt.as_bytes()[piece_end ..].first() {
116 None => {
117 fmt.fmt(f)?;
118 break;
119 },
120 Some(&b) => match as_brace(b) {
121 Some(b) => {
122 fmt[.. piece_end].fmt(f)?;
123 fmt = &fmt[(piece_end + 1) ..];
124 if fmt.is_empty() { break; }
125 match b {
126 Brace::Left => {
127 piece_end = 0;
128 state = State::Arg;
129 },
130 Brace::Right => {
131 piece_end = 1;
132 state = State::Piece;
133 }
134 };
135 },
136 None => {
137 piece_end += 1;
138 }
139 }
140 },
141 State::Arg => match fmt.as_bytes().first() {
142 None => unsafe { unreachable_unchecked() },
143 Some(&b'}') => {
144 if let Some(arg) = args.next() {
145 arg.fmt(f)?;
146 }
147 fmt = &fmt[1 ..];
148 state = State::Piece;
149 },
150 Some(_) => {
151 piece_end = 1;
152 state = State::Piece;
153 }
154 },
155 }
156 }
157 Ok(())
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use crate as dyn_fmt;
164 #[cfg(feature = "std")]
165 use crate::AsStrFormatExt;
166 use core::fmt::{self, Write, Display};
167 use core::str::{self};
168
169 #[cfg(feature = "std")]
170 #[test]
171 fn test_format() {
172 assert_eq!("{}a{}b{}c".format(&[1, 2, 3]), "1a2b3c");
173 assert_eq!("{}a{}b{}c".format(&[1, 2, 3, 4]), "1a2b3c");
174 assert_eq!("{}a{}b{}c".format(&[1, 2]), "1a2bc");
175 assert_eq!("{{}}{}".format(&[1, 2]), "{}1");
176 }
177
178 #[cfg(feature = "std")]
179 #[test]
180 fn test_format_with_string_format() {
181 let format: String = "{}a{}b{}c".into();
182 assert_eq!(format.format(&[1, 2, 3]), "1a2b3c");
183 assert_eq!(format.format(&[2, 3, 4]), "2a3b4c");
184 }
185
186 struct Writer<'a> {
187 buf: &'a mut str,
188 len: usize,
189 }
190
191 impl<'a> fmt::Write for Writer<'a> {
192 fn write_str(&mut self, s: &str) -> fmt::Result {
193 let buf = &mut self.buf[self.len ..];
194 assert!(buf.len() >= s.len());
195 let buf = &mut buf[.. s.len()];
196 unsafe { buf.as_bytes_mut() }.copy_from_slice(s.as_bytes());
197 self.len += s.len();
198 Ok(())
199 }
200 }
201
202 #[test]
203 fn test_write() {
204 let mut buf = [0u8; 128];
205 let buf = str::from_utf8_mut(&mut buf).unwrap();
206 let mut writer = Writer { buf, len: 0 };
207 dyn_write!(&mut writer, "{}a{}b{}c", &[1, 2, 3]).unwrap();
208 let len = writer.len;
209 assert_eq!("1a2b3c", &buf[.. len]);
210 }
211
212 #[test]
213 fn write_args() {
214 let args_format = dyn_fmt::Arguments::new("{}{}{}", &[1, 2, 3]);
215 let mut buf = [0u8; 128];
216 let buf = str::from_utf8_mut(&mut buf).unwrap();
217 let mut writer = Writer { buf, len: 0 };
218 write!(&mut writer, "{}", args_format).unwrap();
219 let len = writer.len;
220 assert_eq!("123", &buf[.. len]);
221 }
222
223 #[test]
224 fn write_unsized_args() {
225 let args: &'static [&'static dyn Display] = &[&1, &2, &3];
226 let args_format = dyn_fmt::Arguments::new("{}{}{}", args.iter().copied());
227 let mut buf = [0u8; 128];
228 let buf = str::from_utf8_mut(&mut buf).unwrap();
229 let mut writer = Writer { buf, len: 0 };
230 write!(&mut writer, "{}", args_format).unwrap();
231 let len = writer.len;
232 assert_eq!("123", &buf[.. len]);
233 }
234
235 #[cfg(feature = "std")]
236 #[test]
237 fn format_unsized_args() {
238 let args: &'static [&'static dyn Display] = &[&1, &2, &3];
239 let args_format = "{}{}{}".format(args.iter().copied());
240 let mut buf = [0u8; 128];
241 let buf = str::from_utf8_mut(&mut buf).unwrap();
242 let mut writer = Writer { buf, len: 0 };
243 write!(&mut writer, "{}", args_format).unwrap();
244 let len = writer.len;
245 assert_eq!("123", &buf[.. len]);
246 }
247
248 #[test]
249 fn write_str() {
250 let args_format = dyn_fmt::Arguments::new("abcd{}абвгд{}{}", &[1, 2, 3]);
251 let mut buf = [0u8; 128];
252 let buf = str::from_utf8_mut(&mut buf).unwrap();
253 let mut writer = Writer { buf, len: 0 };
254 write!(&mut writer, "{}", args_format).unwrap();
255 let len = writer.len;
256 assert_eq!("abcd1абвгд23", &buf[.. len]);
257 }
258
259 #[test]
260 fn complex_case_1() {
261 let args_format = dyn_fmt::Arguments::new("{{}}x{{}{}}y{", &[1, 2, 3]);
262 let mut buf = [0u8; 128];
263 let buf = str::from_utf8_mut(&mut buf).unwrap();
264 let mut writer = Writer { buf, len: 0 };
265 write!(&mut writer, "{}", args_format).unwrap();
266 let len = writer.len;
267 assert_eq!("{}x{{}y", &buf[.. len]);
268 }
269
270 #[test]
271 fn complex_case_2() {
272 let args_format = dyn_fmt::Arguments::new("{{{}}}x{y}", &[1, 2, 3]);
273 let mut buf = [0u8; 128];
274 let buf = str::from_utf8_mut(&mut buf).unwrap();
275 let mut writer = Writer { buf, len: 0 };
276 write!(&mut writer, "{}", args_format).unwrap();
277 let len = writer.len;
278 assert_eq!("{1}xy", &buf[.. len]);
279 }
280
281 #[test]
282 fn complex_case_3() {
283 let args_format = dyn_fmt::Arguments::new("{{{}}}x{{}", &[1, 2, 3]);
284 let mut buf = [0u8; 128];
285 let buf = str::from_utf8_mut(&mut buf).unwrap();
286 let mut writer = Writer { buf, len: 0 };
287 write!(&mut writer, "{}", args_format).unwrap();
288 let len = writer.len;
289 assert_eq!("{1}x{", &buf[.. len]);
290 }
291
292 #[test]
293 fn fmt_lifetime() {
294 fn display<'a, 'b>(f: &'a str, i: &'a [u8], buf: &'b mut str) -> &'b str {
295 let args_format = dyn_fmt::Arguments::new(f, i);
296 let mut writer = Writer { buf, len: 0 };
297 write!(&mut writer, "{}", args_format).unwrap();
298 let len = writer.len;
299 &buf[.. len]
300 }
301 let mut buf = [0u8; 128];
302 let buf = str::from_utf8_mut(&mut buf).unwrap();
303 let res = display("{}", &[0], buf);
304 assert_eq!("0", res);
305 }
306
307 #[test]
308 fn write_macros() {
309 let mut buf = [0u8; 128];
310 let buf = str::from_utf8_mut(&mut buf).unwrap();
311 let mut writer = Writer { buf, len: 0 };
312 dyn_write!(&mut writer, "abcd{}абвгд{}{}", &[1, 2, 3]).unwrap();
313 let len = writer.len;
314 assert_eq!("abcd1абвгд23", &buf[.. len]);
315 }
316}