Skip to main content

Crate format_bytes

Crate format_bytes 

Source
Expand description

This crate exposes procedural macros that allow you to format bytestrings. For more background on why you would want to do that, read this article.

§Usage

Add this to your Cargo.toml:

[dependencies]
format-bytes = "0.4"

Requires Rust 1.85 or later. See the changelog.

Then use the macro like so:

use format_bytes::format_bytes;

fn main() {
    assert_eq!(
        format_bytes!(b"look at those {} bytes", &[0u8, 1, 2]),
        b"look at those \x00\x01\x02 bytes"
    );

    let path = b"caf\xe9";
    assert_eq!(
        format_bytes!("{{{path}}} is {len} bytes long", len = path.len()),
        b"{caf\xe9} is 4 bytes long"
    );
}

See more examples of how it works on the documentation of format_bytes! itself.

Arguments can be of any type that implements DisplayBytes: bytestrings, integers and floats, Utf8 for std::fmt::Display types, join() for sequences, and references and smart pointers to those.

§Writing to a stream: write_bytes!

write_bytes! takes a &mut to the stream to write to, followed by the same arguments as format_bytes!:

use format_bytes::write_bytes;
use std::io;

fn write_entry(out: &mut dyn io::Write, path: &[u8]) -> io::Result<()> {
    write_bytes!(out, b"{}: {} bytes\n", path, path.len())
}

fn main() {
    let mut out = vec![];
    write_entry(&mut out, b"caf\xe9").unwrap();
    assert_eq!(out, b"caf\xe9: 4 bytes\n");
}

The stream can be any std::io::Write, or any Sink.

§Formatting later: message!

message! takes the same arguments as format_bytes!, but returns a Message that keeps the format string and the arguments, to be written later, for example by an error handler. Arguments are moved into the message, so they must be 'static, Send and Sync.

use format_bytes::{Message, format_bytes, message};

fn unknown_revision(rev: u32) -> Message {
    message!("unknown revision {rev}")
}

fn main() {
    let msg = unknown_revision(42);
    assert_eq!(format_bytes!("abort: {msg}"), b"abort: unknown revision 42");
    assert_eq!(msg.template().source, "unknown revision {rev}");
}

A Message is itself a DisplayBytes argument. Message::write_to writes it to a Sink, and Message::template gives the format string, parsed at compile time.

§Custom output: Sink

A Sink receives the format string separately from each argument and its name (ArgName). Every std::io::Write is a Sink that writes both as-is. Other sinks can, for example, tell user data apart from the message around it:

use format_bytes::{ArgName, DisplayBytes, Sink, write_bytes};
use std::io;

/// Quotes arguments
struct Quoting(Vec<u8>);

impl Sink for Quoting {
    fn literal(&mut self, text: &str) -> io::Result<()> {
        self.0.literal(text)
    }

    fn arg(&mut self, _: ArgName, value: &dyn DisplayBytes) -> io::Result<()> {
        write_bytes!(&mut self.0, "'{}'", value)
    }
}

fn main() {
    let mut sink = Quoting(vec![]);
    write_bytes!(&mut sink, "cannot open {path}", path = b"caf\xe9").unwrap();
    assert_eq!(sink.0, b"cannot open 'caf\xe9'");
}

A sink that cares about types for structured output, can call DisplayBytes::visit with a Visit, which implements methods for integers, UTF-8 text, bytes, a join() sequence or a nested Message.

§Missing features

  • format!-like explicit indices ({0}) and format specs ({:?}, padding): if the need manifests itself, they might appear
  • #[no_std] support

§Why not 1.0?

Not until the macro gets a bit of mileage (it will be used in Mercurial).

Macros§

format_bytes
Creates a Vec<u8> using interpolation of runtime expressions.
message
Creates a Message: like format_bytes!, but the arguments are kept, for a Sink to write later.
write_bytes
Like format_bytes!, but writes to a stream given as an additional first argument.

Structs§

Message
A format string with its arguments, made by message!.
Template
A format string, parsed at compile time.
Utf8
Adaptor for types that implement std::fmt::Display. The Unicode output is encoded as UTF-8.

Enums§

ArgName
How a format string refers to an argument.
Piece
A part of a Template.

Traits§

DisplayBytes
Let types decide how to format themselves for presentation to users in a byte-stream output.
Sink
Destination of formatting, which keeps the format string’s text apart from its arguments.
Visit
Receives a value described by DisplayBytes::visit.

Functions§

join
Format a sequence of values with the given separator repeated between any two consecutive values, but not at the start or end of the sequence.