printf-compat 0.3.0

printf reimplemented in Rust
Documentation
# printf-compat

[![Crates.io](https://img.shields.io/crates/v/printf-compat.svg)](https://crates.io/crates/printf-compat)
[![Docs.rs](https://docs.rs/printf-compat/badge.svg)](https://docs.rs/printf-compat)

`printf` reimplemented in Rust

This is a complete reimplementation of `printf` in Rust, using the unstable
(i.e. **requires a Nightly compiler**) `c_variadic` feature.

- [Many C][sigrok-log] [libraries][libusb-log] provide a way to provide a
  custom log callback. With this crate, you can provide a pure Rust option,
  and do whatever you want with it. Log it to the console, store it in a
  string, or do anything else.
- If you're writing a Rust-first program for a microcontroller and need to
  interface with a C library, you might not *have* a libc and have to
  reimplement it yourself. If it uses `printf`, use this crate to easily add
  your own output. [`core::fmt`] too big? No problem! Write your own
  formatting code, or use a minimal formatting library like [`ufmt`] or
  [`defmt`]. Don't need *every* single option given by `printf` format
  strings? No problem! Just don't implement it.
- Likewise, if you're using `wasm32-unknown-unknown` instead of emscripten
  (as wasm-bindgen is only compatible with the former), you have no libc. If
  you want to interface with a C library, you'll have to do it all yourself.
  With this crate, that turns into 5 lines instead of hundreds for `printf`.

## Benefits

### ⚒ Modular

printf-compat lets you pick how you want to output a message. Use
pre-written adapters for [`fmt::Write`][output::fmt_write] (like a
[`String`]) or [`io::Write`][output::io_write] (like
[`io::stdout()`][std::io::stdout]), or implement your own.

### 🔬 Small

This crate is `no_std` compatible (with `default-features = false`).
The main machinery doesn't require the use of [`core::fmt`], and it can't panic.

### 🔒 Safe (as can be)

Of course, `printf` is *completely* unsafe, as it requires the use of
`va_list`. However, outside of that, all of the actual string parsing is
written in completely safe Rust. No buffer overflow attacks!

The `n` format specifier, which writes to a user-provided pointer, is
considered a serious security vulnerability if a user-provided string is
ever passed to `printf`. It *is* supported by this crate; however, it
doesn't do anything by default, and you'll have to explicitly do the writing
yourself.

### 🧹 Tested

A wide [test suite] is used to ensure that many different possibilities are
identical to glibc's `printf`. [Differences are
documented][output::fmt_write#differences].

## Getting Started

Start by adding the unstable feature:

```rust
#![feature(c_variadic)]
```

Now, add your function signature:

```rust
use core::ffi::{c_char, c_int};

#[unsafe(no_mangle)]
unsafe extern "C" fn c_library_print(str: *const c_char, args: ...) -> c_int {
    todo!()
}
```

Think about what you're doing:

- If you're implementing `printf` *because you don't have one*, you'll want to
  call it `printf` and add `#[unsafe(no_mangle)]`.
- Likewise, if you're creating a custom log function for a C library and it
  expects to call a globally-defined function, keep `#[unsafe(no_mangle)]` and
  rename the function to what it expects.
- On the other hand, if your C library expects you to call a function to
  register a callback ([example 1][sigrok-log], [example 2][libusb-log]),
  remove `#[unsafe(no_mangle)]`.

Now, add your logic:

```rust
use printf_compat::{format, output};
let mut s = String::new();
let bytes_written = format(str, args, output::fmt_write(&mut s));
println!("{}", s);
bytes_written
```

Of course, replace [`output::fmt_write`] with whatever you like—some are
provided for you in [`output`]. If you'd like to write your own, follow
their function signature: you need to provide a function to [`format()`]
that takes an [`Argument`] and returns the number of bytes written (although
you don't *need* to if your C library doesn't use it) or -1 if there was an
error.

[sigrok-log]: https://sigrok.org/api/libsigrok/unstable/a00074.html#ga4240b8fe79be72ef758f40f9acbd4316
[libusb-log]: http://libusb.sourceforge.net/api-1.0/group__libusb__lib.html#ga2efb66b8f16ffb0851f3907794c06e20
[test suite]: https://github.com/lights0123/printf-compat/blob/master/src/tests.rs
[`ufmt`]: https://docs.rs/ufmt/
[`defmt`]: https://defmt.ferrous-systems.com/

License: MIT OR Apache-2.0

[`core::fmt`]: https://doc.rust-lang.org/core/fmt/index.html
[`String`]: https://doc.rust-lang.org/std/string/struct.String.html
[std::io::stdout]: https://doc.rust-lang.org/std/io/fn.stdout.html
[`std`]: https://doc.rust-lang.org/std/index.html
[`std::os::raw`]: https://doc.rust-lang.org/stable/std/os/raw/index.html
[output::fmt_write]: https://docs.rs/printf-compat/0.1/printf_compat/output/fn.fmt_write.html
[`output::fmt_write`]: https://docs.rs/printf-compat/0.1/printf_compat/output/fn.fmt_write.html
[output::fmt_write#differences]: https://docs.rs/printf-compat/0.1/printf_compat/output/fn.fmt_write.html#differences
[output::io_write]: https://docs.rs/printf-compat/0.1/printf_compat/output/fn.io_write.html
[`output`]: https://docs.rs/printf-compat/0.1/printf_compat/output/index.html
[`format()`]: https://docs.rs/printf-compat/0.1/printf_compat/fn.format.html
[`Argument`]: https://docs.rs/printf-compat/0.1/printf_compat/argument/struct.Argument.html