use std::io;
#[doc(hidden)]
pub trait WriteStrExt {
type Result;
fn write_str(&mut self, s: &str) -> Self::Result;
}
impl<W: io::Write> WriteStrExt for W {
type Result = io::Result<()>;
fn write_str(&mut self, s: &str) -> Self::Result {
self.write_all(s.as_bytes())
}
}
#[doc(hidden)]
pub trait IdentityMutExt {
fn identity_mut(&mut self) -> &mut Self {
self
}
}
impl<T> IdentityMutExt for T {}
#[macro_export]
macro_rules! wite {
(@one $w:ident, ($e:expr)) => { write!($w, "{}", $e) };
(@one $w:ident, [$e:expr]) => { write!($w, "{:?}", $e) };
(@one $w:ident, {$e:tt : $($fmt:tt)*}) => {
write!($w, concat!("{:", wite!(@stringify-dense $($fmt)*), "}"), $e)
};
(@one $w:ident, {$($arg:tt)*}) => {
write!($w, $($arg)*)
};
(@one $w:ident, $string:tt) => {
{
#[allow(unused_imports)]
use $crate::WriteStrExt;
$w.write_str(concat!($string))
}
};
(@stringify-dense) => { "" };
(@stringify-dense $($tt:tt)+) => { concat!( $(stringify!($tt)),+ ) };
(@expr.. $w:ident {$($before:tt)*} ($($e:tt)*) {$($block:tt)*} $($rest:tt)* ) => {
wite!(@rec $w, $($before)* ($($e)*) {$($block)*} $($rest)*)
};
(@expr.. $w:ident {$($before:tt)*} ($($expr:tt)*) $tt:tt $($rest:tt)* ) => {
wite!(@expr.. $w {$($before)*} ($($expr)* $tt) $($rest)*)
};
(@expr $w:ident {$($before:tt)*} ($($expr:tt)*) $tt:tt $($rest:tt)* ) => {
wite!(@expr.. $w {$($before)*} ($($expr)* $tt) $($rest)*)
};
(@rec $w:ident,
for $p:pat in ($e:expr) { $($body:tt)* }
sep { $($sep:tt)* }
$($rest:tt)*
) => {
{
let mut first_iteration = true;
for $p in $e {
if first_iteration {
first_iteration = false;
} else {
wite!(@rec $w, $($sep)*);
}
wite!(@rec $w, $($body)*);
}
wite!(@rec $w, $($rest)*);
}
};
(@rec $w:ident,
for $p:pat in ($e:expr) { $($body:tt)* }
separated { $($sep:tt)* }
$($rest:tt)*
) => {
wite!(@rec $w, for $p in ($e) { $($body)* } sep { $($sep)* }$($rest)*)
};
(@rec $w:ident, for $p:pat in ($e:expr) { $($body:tt)* } $($rest:tt)*) => {
wite!(@rec $w, for $p in ($e) { $($body)* } sep {} $($rest)*)
};
(@rec $w:ident, for $p:pat in $($tt:tt)* ) => {
wite!(@expr $w { for $p in } () $($tt)*)
};
(@rec $w:ident,
match ($e:expr) {
$( $($p:pat)|+ $(if $g:expr)* => { $($body:tt)* } )*
}
$($rest:tt)*
) => {
{
match $e {
$(
$($p)|+ $(if $g)* => {
wite!(@rec $w, $($body)*)
}
)*
}
wite!(@rec $w, $($rest)*);
}
};
(@rec $w:ident, match $($tt:tt)* ) => {
wite!(@expr $w { match } () $($tt)*)
};
(@rec $w:ident,
if let $p:pat = ($e:expr) { $($then:tt)* }
else { $($els:tt)* }
$($rest:tt)*
) => {
{
if let $p = $e {
wite!(@rec $w, $($then)*);
} else {
wite!(@rec $w, $($els)*);
}
wite!(@rec $w, $($rest)*);
}
};
(@rec $w:ident,
if let $p:pat = ($e:expr) { $($then:tt)* }
else if $($rest:tt)*
) => {
wite!(@ifelseerror)
};
(@rec $w:ident,
if let $p:pat = ($e:expr) { $($then:tt)* }
$($rest:tt)*
) => {
wite!(@rec $w, if let $p = ($e) { $($then)* } else {} $($rest)*);
};
(@rec $w:ident, if let $p:pat = $($tt:tt)* ) => {
wite!(@expr $w { if let $p = } () $($tt)*)
};
(@rec $w:ident,
if ($cond:expr) { $($then:tt)* }
else { $($els:tt)* }
$($rest:tt)*
) => {
{
if $cond {
wite!(@rec $w, $($then)*);
} else {
wite!(@rec $w, $($els)*);
}
wite!(@rec $w, $($rest)*);
}
};
(@rec $w:ident,
if ($cont:expr) { $($then:tt)* }
else if $($rest:tt)*
) => {
wite!(@ifelseerror)
};
(@rec $w:ident, if ($cond:expr) { $($then:tt)* } $($rest:tt)* ) => {
wite!(@rec $w, if ($cond) { $($then)* } else {} $($rest)*);
};
(@rec $w:ident, if $($tt:tt)* ) => {
wite!(@expr $w { if } () $($tt)*)
};
(@rec $w:ident, (= $e:expr) $($rest:tt)*) => {
wite!(@rec $w, (concat!(stringify!($e), " = ")) ($e) $($rest)*)
};
(@rec $w:ident, [= $e:expr] $($rest:tt)*) => {
wite!(@rec $w, (concat!(stringify!($e), " = ")) [$e] $($rest)*)
};
(@rec $w:ident, {= $e:tt : $($fmt:tt)*} $($rest:tt)*) => {
wite!(@rec $w, (concat!(stringify!($e), " = ")) {$e : $($fmt)*} $($rest)*)
};
(@rec $w:ident, $part:tt $($rest:tt)*) => {
{
match wite!(@one $w, $part) {
Ok(_) => (),
error => return error,
}
wite!(@rec $w, $($rest)*);
}
};
(@rec $w:ident, ) => { () };
(@ifelseerror) => {
{
let ERROR: () = "`else if` is not supported";
let NOTE: () = "use `match` or `else { if ... }` instead";
}
};
($writer:expr, $($part:tt)*) => {
{
use $crate::IdentityMutExt;
match $writer.identity_mut() {
_w => (||{
if false {
let _ = write!(_w, "");
}
wite!(@rec _w, $($part)*);
Ok(())
})()
}
}
};
}
#[macro_export]
macro_rules! witeln {
($writer:expr, $($arg:tt)*) => { wite!($writer, $($arg)* "\n") };
($writer:expr) => { wite!($writer, "\n") };
}
#[macro_export]
macro_rules! pint {
($($arg:tt)*) => {
{
{
#[cfg(not(test))] {
use ::std::io::Write;
let o = ::std::io::stdout();
wite!(o.lock(), $($arg)*).unwrap();
}
#[cfg(test)] {
print!("{}", fomat!($($arg)*))
}
}
}
}
}
#[macro_export]
macro_rules! pintln {
($($arg:tt)*) => {
{
#[cfg(not(test))] {
pint!($($arg)* "\n")
}
#[cfg(test)] {
print!("{}", fomat!($($arg)* "\n"))
}
}
}
}
#[macro_export]
macro_rules! epint {
($($arg:tt)*) => {
{
use ::std::io::Write;
let o = ::std::io::stderr();
wite!(o.lock(), $($arg)*).unwrap();
}
}
}
#[macro_export]
#[deprecated(since="0.2.1", note="use `epint` instead")]
macro_rules! perr { ($($arg:tt)*) => { epint!($($arg)*) } }
#[macro_export]
macro_rules! epintln {
($($arg:tt)*) => { epint!($($arg)* "\n") }
}
#[macro_export]
#[deprecated(since="0.2.1", note="use `epint` instead")]
macro_rules! perrln { ($($arg:tt)*) => { epintln!($($arg)*) } }
#[macro_export]
macro_rules! fomat {
(@cap ($len:expr, $multiplier:expr)) => {
($len, $multiplier)
};
(@cap ($($lm:tt)*) for $p:pat in $($tt:tt)*) => {
fomat!(@cap-ignore ($($lm)*) $($tt)*)
};
(@cap ($($lm:tt)*) sep $($tt:tt)*) => {
fomat!(@cap-ignore ($($lm)*) $($tt)*)
};
(@cap ($($lm:tt)*) separated $($tt:tt)*) => {
fomat!(@cap-ignore ($($lm)*) $($tt)*)
};
(@cap ($($lm:tt)*) if let $p:pat = $($tt:tt)*) => {
fomat!(@cap-ignore ($($lm)*) $($tt)*)
};
(@cap ($($lm:tt)*) if $($tt:tt)*) => {
fomat!(@cap-ignore ($($lm)*) $($tt)*)
};
(@cap ($($lm:tt)*) else $($tt:tt)*) => {
fomat!(@cap-ignore ($($lm)*) $($tt)*)
};
(@cap ($($lm:tt)*) match $($tt:tt)*) => {
fomat!(@cap-ignore ($($lm)*) $($tt)*)
};
(@cap ($len:expr, $mul:expr) ($($x:tt)*) $($rest:tt)*) => {
fomat!(@cap ($len, 2) $($rest)*)
};
(@cap ($len:expr, $mul:expr) [$($x:tt)*] $($rest:tt)*) => {
fomat!(@cap ($len, 2) $($rest)*)
};
(@cap ($len:expr, $mul:expr) {$($x:tt)*} $($rest:tt)*) => {
fomat!(@cap ($len, 2) $($rest)*)
};
(@cap ($len:expr, $mul:expr) $string:tt $($rest:tt)*) => {
fomat!(@cap ($len + concat!($string).len(), $mul) $($rest)*)
};
(@cap-ignore ($($lm:tt)*) { $($block:tt)* } $($rest:tt)*) => {
fomat!(@cap ($($lm)*) $($rest)*)
};
(@cap-ignore ($($lm:tt)*) $tt:tt $($rest:tt)*) => {
fomat!(@cap-ignore ($($lm)*) $($rest)*)
};
() => { String::new() };
($($arg:tt)*) => {
{
use ::std::fmt::Write;
let (len, mul) = fomat!(@cap (0, 1) $($arg)*);
let mut _s = String::with_capacity(len * mul);
wite!(_s, $($arg)*).ok();
_s
}
}
}
#[test]
fn basics() {
let world = "World";
assert_eq!(fomat!("Hello, "(world)"!"), "Hello, World!");
let x = 3;
assert_eq!(fomat!((x)" * 2 = "(x * 2)), "3 * 2 = 6");
}
#[test]
fn empty() {
assert_eq!(fomat!(), "");
}
#[test]
fn debug() {
let v = [1,2,3];
assert_eq!(fomat!([v] "."), "[1, 2, 3].");
}
#[test]
fn test_if() {
let s = fomat!(
if true { "A" "A" } else { "X" }
if false { "X" } else { "D" "D" }
if true { "T" "T" }
if false { "X" }
if let Some(x) = Some(5) { (x) (x) } else { "E" "E" }
if let None = Some(5) { "X" } else { "F" "F" }
if let Some(x) = Some(5) { (x) }
if let None = Some(5) { "X" }
if {let t = true; t} { "K" }
"."
);
assert_eq!(s, "AADDTT55FF5K.");
}
#[test]
fn format() {
assert_eq!( fomat!({5:02}), "05" );
assert_eq!( fomat!({"{}-{}", 4, 2}), "4-2" );
}
#[test]
fn separator() {
let v = [1, 2, 3];
let s1 = fomat!( for x in &v { (x) } separated { "-" "-" } "." );
let s2 = fomat!( for x in &v { (x) } sep { "--" } "." );
assert_eq!(s1, "1--2--3.");
assert_eq!(s2, "1--2--3.");
}
#[test]
fn test_match() {
let s = fomat!(
match Some(5) {
Some(x) if x > 3 => { (x) "!" }
Some(2) | None => {}
_ => {}
}
"."
);
assert_eq!(s, "5!.");
}
#[test]
fn capacity() {
assert_eq!(fomat!("Hello, " "world!").capacity(), 13);
assert_eq!(fomat!("Hello, "[40+2]).capacity(), 14);
let s = fomat!(
"Hello"
for x in [1][1..].iter() { (x) "a" }
if let Some(()) = None { "b" }
if false { "c" } else {}
match 1 { 2 => { "e" } _ => {} }
"!"
);
assert_eq!(s.capacity(), 6);
}
#[test]
fn fmt_write() {
use std::fmt;
struct Foo;
impl fmt::Display for Foo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
wite!(f, "foo"(42))
}
}
assert_eq!(format!("{}", Foo), "foo42");
}
#[test]
fn equal_sign() {
let x = 5;
let v = [10];
assert_eq!(fomat!((=x) "."), "x = 5.");
assert_eq!(fomat!([=&v] "."), "&v = [10].");
assert_eq!(fomat!({=13:05b} "."), "13 = 01101.");
}
#[test]
fn depth() {
let _ = fomat!(
"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" "2" "3" "4" "5" "6" "7" "8" "9" "0"
"1" "2" "3" "4" "5" "6" "7" "8" "9" "0"
);
}
#[test]
fn non_static_writer() {
use std::io::Write;
use std::io::Result;
use std::fmt::Arguments;
use WriteStrExt;
struct Prepender<'a, T: Write> {
prefix: &'a str,
writer: T,
}
impl<'a, T: Write> Write for Prepender<'a, T> {
fn write(&mut self, buf: &[u8]) -> Result<usize> {
self.writer.write(buf)
}
fn flush(&mut self) -> Result<()> {
self.writer.flush()
}
fn write_fmt(&mut self, fmt: Arguments) -> Result<()> {
self.writer.write_str(self.prefix)?;
self.writer.write_fmt(fmt)
}
}
let mut buf = vec![];
witeln!(
Prepender { prefix: &"foo ".to_owned(), writer: &mut buf },
(2+2)
).unwrap();
assert_eq!(buf, "foo 4\n".as_bytes());
}
#[test]
fn no_semicolon() {
if true { pint!("foo") } else { epint!("bar") }
pintln!("foo" "bar")
}