#![cfg_attr(not(test), no_std)]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
pub use format_args_conditional::branch_on_format_capture;
#[macro_export]
macro_rules! write {
(@lit $w:expr, $lit:literal) => {{
#[allow(unused_imports)]
use $crate::__internal_unstable::IoWriteStr;
$w.write_str($lit)
}};
(@fmt $w:expr, $($fmt:tt)+) => {
$crate::__internal_unstable::core_write!($w, $($fmt)+)
};
($w:expr $(,)?) => { Ok(()) };
($w:expr, $arg:tt $(,)?) => {
$crate::branch_on_format_capture!(
$crate::write {@fmt $w,},
$crate::write {@lit $w,},
$arg
)
};
($w:expr, $($arg:tt)+) => {
$crate::__internal_unstable::core_write!($w, $($arg)+)
};
}
#[macro_export]
macro_rules! writeln {
(@lit $w:expr, $lit:literal) => {{
#[allow(unused_imports)]
use $crate::__internal_unstable::IoWriteStr;
$w.write_str(concat!($lit, "\n"))
}};
(@fmt $w:expr, $($fmt:tt)+) => {
$crate::__internal_unstable::core_writeln!($w, $($fmt)+)
};
($w:expr $(,)?) => {{
#[allow(unused_imports)]
use $crate::__internal_unstable::IoWriteStr;
$w.write_str("\n")
}};
($w:expr, $arg:tt $(,)?) => {
$crate::branch_on_format_capture!(
$crate::writeln {@fmt $w,},
$crate::writeln {@lit $w,},
$arg
)
};
($w:expr, $($arg:tt)+) => {
$crate::__internal_unstable::core_writeln!($w, $($arg)+)
}
}
#[doc(hidden)]
pub mod __internal_unstable {
pub use core::write as core_write;
pub use core::writeln as core_writeln;
#[cfg(feature = "std")]
mod std_only {
extern crate std;
use std::io;
pub trait IoWriteStr {
fn write_str(&mut self, x: &str) -> io::Result<()>;
}
impl<T: io::Write + ?Sized> IoWriteStr for T {
#[inline(always)]
fn write_str(&mut self, x: &str) -> io::Result<()> {
self.write(x.as_bytes()).map(|_| ())
}
}
}
#[cfg(feature = "std")]
pub use std_only::IoWriteStr;
#[cfg(not(feature = "std"))]
pub trait IoWriteStr {}
}
#[cfg(test)]
mod tests {
use core::fmt;
use std::io;
use fmt::Write as _;
#[cfg(feature = "std")]
use io::Write as _;
use super::{write, writeln};
#[derive(Default)]
pub struct FormatTrack<T> {
writer: T,
used_write_fmt: bool,
}
impl<T: fmt::Write> fmt::Write for FormatTrack<T> {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.writer.write_str(s)
}
fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result {
self.used_write_fmt = true;
fmt::write(&mut self.writer, args)
}
}
impl<T: io::Write> io::Write for FormatTrack<T> {
fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> io::Result<()> {
self.used_write_fmt = true;
io::Write::write_fmt(&mut self.writer, args)
}
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.writer.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.writer.flush()
}
}
macro_rules! check {
($macro:ident, $use_write_fmt:ident, $expected_contents:literal, $writer:ident $($args:tt)*) => {
assert!($macro!($writer $($args)*).is_ok());
assert_eq!($writer.used_write_fmt, $use_write_fmt);
$writer.used_write_fmt = false;
assert_eq!(AsRef::<[u8]>::as_ref(&$writer.writer), $expected_contents.as_bytes());
$writer.writer.clear();
};
}
#[test]
fn test_temporary_writer() {
let x = 10;
let _ = write!(String::new(), "Hello");
let _ = write!(String::new(), "Hello {x}");
let _ = write!(String::new(), "Hello {x}", x = "world");
let _ = writeln!(String::new(), "Hello");
let _ = writeln!(String::new(), "Hello {x}");
let _ = writeln!(String::new(), "Hello {x}", x = "world");
}
#[test]
fn test_generic_owned_fmt_writer() {
fn generic_owned_fmt<W: fmt::Write>(mut w: W) -> W {
let x = 10;
let _ = write!(w, "A ");
let _ = write!(w, "B {x} ");
let _ = write!(w, "C {x} ", x = "D");
let _ = writeln!(w, "A");
let _ = writeln!(w, "B {x}");
let _ = writeln!(w, "C {x}", x = "D");
w
}
assert_eq!(
generic_owned_fmt(String::new()),
"A B 10 C D A\nB 10\nC D\n"
);
}
#[test]
fn test_generic_borrow_of_owned_fmt_writer() {
fn generic_borrow_of_owned_fmt<W: fmt::Write>(mut w: W) -> W {
let x = 10;
let _ = write!(&mut w, "A ");
let _ = write!(&mut w, "B {x} ");
let _ = write!(&mut w, "C {x} ", x = "D");
let _ = writeln!(&mut w, "A");
let _ = writeln!(&mut w, "B {x}");
let _ = writeln!(&mut w, "C {x}", x = "D");
w
}
assert_eq!(
generic_borrow_of_owned_fmt(String::new()),
"A B 10 C D A\nB 10\nC D\n"
);
}
#[test]
fn test_generic_mut_fmt_writer() {
fn generic_mut_fmt<W: fmt::Write>(w: &mut W) -> &mut W {
let x = 10;
let _ = write!(w, "A ");
let _ = write!(w, "B {x} ");
let _ = write!(w, "C {x} ", x = "D");
let _ = writeln!(w, "A");
let _ = writeln!(w, "B {x}");
let _ = writeln!(w, "C {x}", x = "D");
w
}
assert_eq!(
generic_mut_fmt(&mut String::new()),
"A B 10 C D A\nB 10\nC D\n"
);
}
#[test]
fn test_fmt_write() {
let mut w = FormatTrack::<String>::default();
let x = 10;
check!(write, true, "Hello world", w, "Hello {x}", x = "world");
check!(
writeln,
true,
"Hello newline\n",
w,
"Hello {x}",
x = "newline"
);
check!(write, true, "Capture x as 10", w, "Capture x as {x}");
check!(writeln, true, "Capture x as 10\n", w, "Capture x as {x}");
check!(write, true, "Positional 10", w, "Positional {}", x);
check!(writeln, true, "Positional 10\n", w, "Positional {}", x);
check!(write, false, "Just a string", w, "Just a string");
check!(write, false, "", w, "");
check!(
writeln,
false,
"Just a string\nwith a newline\n",
w,
"Just a string\nwith a newline"
);
check!(writeln, false, "\n", w, "",);
check!(writeln, false, "\n", w, "");
check!(writeln, false, "\n", w,);
check!(writeln, false, "\n", w);
}
#[cfg(feature = "std")]
#[test]
fn test_io_write() {
let mut w = FormatTrack::<Vec<u8>>::default();
let x = 10;
check!(write, true, "Hello world", w, "Hello {x}", x = "world");
check!(
writeln,
true,
"Hello newline\n",
w,
"Hello {x}",
x = "newline"
);
check!(write, true, "Capture x as 10", w, "Capture x as {x}");
check!(writeln, true, "Capture x as 10\n", w, "Capture x as {x}");
check!(write, true, "Positional 10", w, "Positional {}", x);
check!(writeln, true, "Positional 10\n", w, "Positional {}", x);
check!(write, false, "Just a string", w, "Just a string");
check!(write, false, "", w, "");
check!(
writeln,
false,
"Just a string\nwith a newline\n",
w,
"Just a string\nwith a newline"
);
check!(writeln, false, "\n", w, "",);
check!(writeln, false, "\n", w, "");
check!(writeln, false, "\n", w,);
check!(writeln, false, "\n", w);
}
}