#[macro_export]
macro_rules! fmt {
(move $($tt:tt)*) => {
$crate::fmt(move |_f| {
$crate::__fmt!{_f $($tt)*}
Ok(())
})
};
($($tt:tt)*) => {
$crate::fmt(|_f| {
$crate::__fmt!{_f $($tt)*}
Ok(())
})
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! __fmt {
($f:ident $text1:literal $text2:literal $($tail:tt)*) => {
$crate::__fmt!{$f @concat($text1, $text2) $($tail)*}
};
($f:ident $text:literal $($tail:tt)*) => {
$f.write_str($crate::obfstr!(concat!($text)))?;
$crate::__fmt!{$f $($tail)*}
};
($f:ident @concat($($texts:literal),+) $text:literal $($tail:tt)*) => {
$crate::__fmt!{$f @concat($($texts,)+ $text) $($tail)*}
};
($f:ident @concat($($texts:literal),+) $($tail:tt)*) => {
$f.write_str($crate::obfstr!(concat!($($texts),+)))?;
$crate::__fmt!{$f $($tail)*}
};
($f:ident {$($e:tt)*} $($tail:tt)*) => {
$f.write_fmt($crate::__fmt_format!([] $($e)*))?;
$crate::__fmt!{$f $($tail)*}
};
($f:ident |$ff:pat_param| $block:block $($tail:tt)*) => {
let $ff = &mut *$f;
$block
$crate::__fmt!{$f $($tail)*}
};
($f:ident |$ff:pat_param| $stmt:stmt; $($tail:tt)*) => {
let $ff = &mut *$f;
$stmt
$crate::__fmt!{$f $($tail)*}
};
($f:ident let $p:pat = $e:expr; $($tail:tt)*) => {
let $p = $e;
$crate::__fmt!{$f $($tail)*}
};
($f:ident if $($tail:tt)*) => {
$crate::__fmt_if!{$f [] if $($tail)*}
};
($f:ident match ($e:expr) { $($body:tt)* } $($tail:tt)*) => {
$crate::__fmt_match!{$f match ($e) {} $($body)*}
$crate::__fmt!{$f $($tail)*}
};
($f:ident match $($tail:tt)*) => {
$crate::__with_parens!{__fmt! [$f match] () $($tail)*}
};
($f:ident for $p:pat in ($e:expr) { $($body:tt)* } $($tail:tt)*) => {
for $p in $e {
$crate::__fmt!{$f $($body)*}
}
$crate::__fmt!{$f $($tail)*}
};
($f:ident for $p:pat in $($tail:tt)*) => {
$crate::__with_parens!{__fmt! [$f for $p in] () $($tail)*}
};
($f:ident ($($tt:tt)*) $($tail:tt)*) => {
$crate::__fmt!{$f $($tt)*}
$crate::__fmt!{$f $($tail)*}
};
($f:ident) => {};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __fmt_format {
([$($e:tt)*] : $($tail:tt)*) => {
$crate::__fmt_expr!([$($e)*] : $($tail)*)
};
([$($e:tt)*] ; $($tail:tt)*) => {
$crate::__fmt_expr!([$($e)*] : $($tail)*)
};
([$($e:tt)*] $nom:tt $($tail:tt)*) => {
$crate::__fmt_format!([$($e)* $nom] $($tail)*)
};
([$($e:tt)*]) => {
$crate::__fmt_expr!([$($e)*])
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __fmt_expr {
([$e:expr]) => {
::core::format_args!("{}", $e)
};
([$e:expr $(, $w:expr)?] $($s:tt)*) => {
::core::format_args!(concat!("{", $(::core::stringify!($s),)* "}"), $e $(,$w)?)
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __fmt_if {
($f:ident [$($c:tt)*] if let $p:pat = ($e:expr) { $($body:tt)* } $($tail:tt)*) => {
$crate::__fmt_if!{$f [$($c)* if let $p = $e { $crate::__fmt!{$f $($body)*} }] $($tail)*}
};
($f:ident [$($c:tt)*] if let $p:pat = $($tail:tt)*) => {
$crate::__with_parens!{__fmt_if! [$f [$($c)*] if let $p =] () $($tail)*}
};
($f:ident [$($c:tt)*] if ($e:expr) { $($body:tt)* } $($tail:tt)*) => {
$crate::__fmt_if!{$f [$($c)* if $e { $crate::__fmt!{$f $($body)*} }] $($tail)*}
};
($f:ident [$($c:tt)*] if $($tail:tt)*) => {
$crate::__with_parens!{__fmt_if! [$f [$($c)*] if] () $($tail)*}
};
($f:ident [$($c:tt)*] else if let $p:pat = ($e:expr) { $($body:tt)* } $($tail:tt)*) => {
$crate::__fmt_if!{$f [$($c)* else if let $p = $e { $crate::__fmt!{$f $($body)*} }] $($tail)*}
};
($f:ident [$($c:tt)*] else if let $p:pat = $($tail:tt)*) => {
$crate::__with_parens!{__fmt_if! [$f [$($c)*] else if let $p =] () $($tail)*}
};
($f:ident [$($c:tt)*] else if ($e:expr) { $($body:tt)* } $($tail:tt)*) => {
$crate::__fmt_if!{$f [$($c)* else if $e { $crate::__fmt!{$f $($body)*} }] $($tail)*}
};
($f:ident [$($c:tt)*] else if $($tail:tt)*) => {
$crate::__with_parens!{__fmt_if! [$f [$($c)*] else if] () $($tail)*}
};
($f:ident [$($c:tt)*] else { $($body:tt)* } $($tail:tt)*) => {
$($c)*
else {
$crate::__fmt!{$f $($body)*}
}
$crate::__fmt!{$f $($tail)*}
};
($f:ident [$($c:tt)*] $($tail:tt)*) => {
$($c)*
$crate::__fmt!{$f $($tail)*}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __with_parens {
($next:ident! [$($prefix:tt)*] ($($tt:tt)*) { $($body:tt)* } $($tail:tt)*) => {
$crate::$next!{$($prefix)* ($($tt)*) { $($body)* } $($tail)*}
};
($next:ident! [$($prefix:tt)*] ($($tt:tt)*) $nom:tt $($tail:tt)*) => {
$crate::__with_parens!{$next! [$($prefix)*] ($($tt)* $nom) $($tail)*}
};
($next:ident! [$($prefix:tt)*] ($($tt:tt)*)) => {
$crate::$next!{$($prefix)* ($($tt)*) {}}
compile_error!(concat!("missing block after expression: ", stringify!($($tt)*)));
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __fmt_match {
($f:ident match ($e:expr) {$($arms:tt)*} $pat:pat $(if $guard:expr)? => { $($body:tt)* }, $($tail:tt)*) => {
$crate::__fmt_match!{$f match ($e) {$($arms)* $pat $(if $guard)? => { $crate::__fmt!{$f $($body)*} }} $($tail)*}
};
($f:ident match ($e:expr) {$($arms:tt)*} $pat:pat $(if $guard:expr)? => { $($body:tt)* } $($tail:tt)*) => {
$crate::__fmt_match!{$f match ($e) {$($arms)* $pat $(if $guard)? => { $crate::__fmt!{$f $($body)*} }} $($tail)*}
};
($f:ident match ($e:expr) {$($arms:tt)*} $pat:pat $(if $guard:expr)? => $($tail:tt)*) => {
$crate::__until_comma!{__fmt_match! [$f match ($e) {$($arms)*} $pat $(if $guard)? =>] {} $($tail)*}
};
($f:ident match ($e:expr) {$($pat:pat $(if $guard:expr)? => $block:block)*}) => {
match $e {
$($pat $(if $guard)? => $block)*
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __until_comma {
($next:ident! [$($prefix:tt)*] {$($tt:tt)*} , $($tail:tt)*) => {
$crate::$next!{$($prefix)* {$($tt)*}, $($tail)*}
};
($next:ident! [$($prefix:tt)*] {$($tt:tt)*} $nom:tt $($tail:tt)*) => {
$crate::__until_comma!{$next! [$($prefix)*] {$($tt)* $nom} $($tail)*}
};
($next:ident! [$($prefix:tt)*] {$($tt:tt)*}) => {
$crate::$next!{$($prefix)* {$($tt)*}}
};
}
#[test]
fn tests() {
#[track_caller]
fn check(t: impl std::fmt::Display, s: &str) {
assert_eq!(t.to_string(), s);
}
check(fmt!("abc"), "abc");
check(fmt!("abc" "def"), "abcdef");
check(fmt!("abc" 123 "def" '#' 4_2_0_f32), "abc123def#420");
check(fmt!(0 {0}), "00");
check(fmt!(let value = 42; "hex("{value}") = "{value:#x}), "hex(42) = 0x2a");
check(fmt!(let width = 5; "Hello "{"x",width:1$}"!"), "Hello x !");
check(fmt!(|_| let name = "world"; |f| f.write_str("Hello ")?; |f| f.write_str(name)?;), "Hello world");
check(fmt!(|_| let name = "world"; |f| { f.write_str("Hello ")?; f.write_str(name)?; }), "Hello world");
check(fmt!("{"{fmt!("a = "{42})}"}"), "{a = 42}");
check(fmt!("{"{{let a = 42; fmt!(move "a = "{a})}}"}"), "{a = 42}");
let _ = fmt!(if false {});
let _ = fmt!(if false {} if true {});
let _ = fmt!(if false {} else {});
let _ = fmt!(if false {} else if true {});
let _ = fmt!(if false {} else if false {} else {});
let _ = fmt!(if let Some(_) = Some(42) {});
let _ = fmt!(if let Some(_) = Some(42) {} else {});
let _ = fmt!(if let Some(_) = Some(42) {} else if let Some(_) = Some(13) {});
let _ = fmt!(if let Some(_) = Some(42) {} else if let Some(_) = Some(13) {} else {});
let _ = fmt!(match false { false => {}, true => {}});
let _ = fmt!(match 0i32 { 0 => "0", 1 => {} _ => {},});
let _ = fmt!(match false { _ if false => {} _ => {}});
let _ = fmt!(for _ in 0..4 {});
let _ = fmt!(for _ in &[1, 2, 3, 4] {});
check(fmt!(
(0 {1} 2 3 {4} 5 6 {7} 8 9 {0} 1 2 {3} 4 5 6 {7} 8 9 {0} 1 2 {3} 4 5 {6} 7 8 {9} 0 1)
(0 {1} 2 3 {4} 5 6 {7} 8 9 {0} 1 2 {3} 4 5 6 {7} 8 9 {0} 1 2 {3} 4 5 {6} 7 8 {9} 0 1)
(0 {1} 2 3 {4} 5 6 {7} 8 9 {0} 1 2 {3} 4 5 6 {7} 8 9 {0} 1 2 {3} 4 5 {6} 7 8 {9} 0 1)
(0 {1} 2 3 {4} 5 6 {7} 8 9 {0} 1 2 {3} 4 5 6 {7} 8 9 {0} 1 2 {3} 4 5 {6} 7 8 {9} 0 1)
), concat!("01234567890123456789012345678901", "01234567890123456789012345678901",
"01234567890123456789012345678901", "01234567890123456789012345678901"));
}