use format_bytes::{
ArgName, DisplayBytes, Message, Sink, Visit, message, write_bytes,
};
use std::io;
#[test]
fn writers_are_sinks() {
let mut out = vec![];
out.literal("a").unwrap();
out.arg(ArgName::Index(0), &1).unwrap();
let writer: &mut dyn io::Write = &mut out;
writer.arg(ArgName::Name("b"), b"b").unwrap();
assert_eq!(out, b"a1b");
}
#[derive(Default)]
struct Recorder(Vec<(Option<ArgName>, Vec<u8>)>);
impl Sink for Recorder {
fn literal(&mut self, text: &str) -> io::Result<()> {
self.0.push((None, text.as_bytes().to_vec()));
Ok(())
}
fn arg(
&mut self,
name: ArgName,
value: &dyn DisplayBytes,
) -> io::Result<()> {
let mut bytes = vec![];
value.display_bytes(&mut bytes)?;
self.0.push((Some(name), bytes));
Ok(())
}
}
#[test]
fn literals_and_args_are_apart() {
let rev = 42;
let mut sink = Recorder::default();
write_bytes!(&mut sink, "{} {path} {rev}{}", b"a", 1, path = b"p")
.unwrap();
assert_eq!(
sink.0,
[
(Some(ArgName::Index(0)), b"a".to_vec()),
(None, b" ".to_vec()),
(Some(ArgName::Name("path")), b"p".to_vec()),
(None, b" ".to_vec()),
(Some(ArgName::Name("rev")), b"42".to_vec()),
(Some(ArgName::Index(1)), b"1".to_vec()),
]
);
}
struct Brackets(Vec<u8>);
impl Sink for Brackets {
fn literal(&mut self, text: &str) -> io::Result<()> {
self.0.literal(text)
}
fn arg(
&mut self,
_name: ArgName,
value: &dyn DisplayBytes,
) -> io::Result<()> {
value.visit(&mut BracketsValue(self))
}
}
struct BracketsValue<'a>(&'a mut Brackets);
impl Visit for BracketsValue<'_> {
fn bytes(&mut self, value: &[u8]) -> io::Result<()> {
write_bytes!(&mut self.0.0, "[{}]", value)
}
fn str(&mut self, value: &str) -> io::Result<()> {
self.bytes(value.as_bytes())
}
fn message(&mut self, message: &Message) -> io::Result<()> {
message.write_to(self.0)
}
}
#[test]
fn nested_message() {
let cause = message!("no such file {path}", path = b"p".to_vec());
let msg = message!("cannot load {config}: {cause}", config = 1);
let mut sink = Brackets(vec![]);
msg.write_to(&mut sink).unwrap();
assert_eq!(sink.0, b"cannot load [1]: no such file [p]");
}