# printf-compat
[](https://crates.io/crates/printf-compat)
[](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