Rust macro pretty_dbg!()
copy of the macro dbg!, just modified :#? to :# for pretty print
version: 1.0.49 date: 2024-02-02 author: bestia.dev repository: Github
Hashtags: #rustlang #tutorial
My projects on GitHub are more like a tutorial than a finished product: bestia-dev tutorials.
Motivation
I love using the macro dbg!()
in Rust. It is an easy way to temporarily print a value on the screen while programming. And when not needed anymore it is easy to search for all dbg!
and erase or comment them.
In my last project, I had some JSON data. The macro from the standard library printed a humanly unreadable long string. This is not pretty! Even multiline strings are printed in one single line making it unreadable.
Run this code in the rust playground:
fn main() {
let json_str = r#"
{
owner: 'bestia-dev',
repository_details: {
general: {
description: 'testing the creation of a github repo',
},
},
}
"#;
dbg!(json_str);
}
This is the unreadable output for a string:
[src/main.rs:12] json_str = "\n {\n owner: 'bestia-dev',\n repository_details: {\n general: {\n description: 'testing the creation of a github repo',\n },\n },\n }\n"
Now let's try with the serde_json::Value:
Run this code in the rust playground:
use serde_json;
use anyhow::Result;
fn main() -> Result<(), anyhow::Error>{
let response_text =
r#"{
"id": 1296269,
"homepage": "https://github.com"
}"#;
let parsed_json_value: serde_json::Value = serde_json::from_str(response_text)?;
dbg!(&parsed_json_value);
Ok(())
}
This returns also an unreadable output:
[src/main.rs:11] &parsed_json_value = Object {
"homepage": String("https://github.com"),
"id": Number(1296269),
}
I know that dbg!
under the hood is just a simple eprintln!("{:#?}, json")
.
And I know that I can print pretty JSON using eprintln!("{:#}, json")
but then I don't express neatly my intent to dbg!
. And I lose the possibility to search for dbg!
.
I found a crate that colors the dbg!
output and is really pretty: dbg-pls. That is maybe too much for my little project.
new macro pretty_dbg!
So I decided to copy the original macro dbg!
, modify it a little bit, and give it the name pretty_dbg!
.
Run this code in the rust playground:
/// copy of the macro dbg!, just modified :#? to :# for pretty print
#[macro_export]
macro_rules! pretty_dbg {
() => {
std::eprintln!("[{}:{}]", std::file!(), std::line!())
};
($val:expr $(,)?) => {
match $val {
tmp => {
std::eprintln!("[{}:{}] {} = {:#}",
std::file!(), std::line!(), std::stringify!($val), &tmp);
tmp
}
}
};
($($val:expr),+ $(,)?) => {
($(std::pretty_dbg!($val)),+,)
};
}
fn main() {
let json_str = r#"
{
owner: 'bestia-dev',
repository_details: {
general: {
description: 'testing the creation of a github repo',
},
},
}
"#;
pretty_dbg!(json_str);
}
The output is now very pretty:
[src/main.rs:32] json_str =
{
owner: 'bestia-dev',
repository_details: {
general: {
description: 'testing the creation of a github repo',
},
},
}
Now let's try with the serde_json::Value:
Run this code in the rust playground:
use serde_json;
use anyhow::Result;
/// copy of the macro dbg!, just modified :#? to :# for pretty print
#[macro_export]
macro_rules! pretty_dbg {
() => {
std::eprintln!("[{}:{}]", std::file!(), std::line!())
};
($val:expr $(,)?) => {
match $val {
tmp => {
std::eprintln!("[{}:{}] {} = {:#}",
std::file!(), std::line!(), std::stringify!($val), &tmp);
tmp
}
}
};
($($val:expr),+ $(,)?) => {
($(std::pretty_dbg!($val)),+,)
};
}
fn main() -> Result<(), anyhow::Error>{
let response_text =
r#"{
"id": 1296269,
"homepage": "https://github.com"
}"#;
let parsed_json_value: serde_json::Value = serde_json::from_str(response_text)?;
pretty_dbg!(&parsed_json_value);
Ok(())
}
The output is now pretty:
[src/main.rs:31] &parsed_json_value = {
"homepage": "https://github.com",
"id": 1296269
}
New crate in crates.io or not
I think this is maybe too small to be made in a new crate.
It is just a small macro.
For now, I am just adding the code for this macro in my projects where I need it.
I changed my mind. I will publish a micro crate and I will make it exemplary because it is so small.
I will add tests, examples, playground code, documentation,... as well as I could.
Playing with the Rust Playground
Trying short Rust code in the Rust playground is great! It is fast and easy. It works just with a browser. Fantastic to show examples of real code, not just hypothetical code.
I first created Github Gists for my code examples. Every example must be in a separate Gist. Then I copy the Gist identification number into the playground link like this: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=d5d5e264b9143f4fde16594eaea1fa09 and it just works. Great!
I want to avoid using unwrap!
in my examples. Unwrap is a bad, bad habit. Instead, I will use the crate anyhow
and its types Result
and Error
directly in the main() function. So I can use the error propagation symbol ?
in the code like a pro.
Integration tests for err/std output
Integration tests by default capture the std output and run in parallel. In my case, this is exactly what I don't want.
I need to capture the err/std output because this is the whole point of how the pretty_dbg! macro works. When working with the std/err output, code must not run in parallel because it would mix the output from different code and make it like scrambled eggs.
It took a while to modify the code in my automation tasks and the calling parameters to run the tests in this non-standard way.
I found the crate gag
that can capture err/std output and I used a macro for my integration tests. This will come in handy for a lot of tests for CLI apps.
Open-source and free as a beer
My open-source projects are free as a beer (MIT license).
I just love programming.
But I need also to drink. If you find my projects and tutorials helpful, please buy me a beer by donating to my PayPal.
You know the price of a beer in your local bar ;-)
So I can drink a free beer for your health :-)
Na zdravje! Alla salute! Prost! Nazdravlje! 🍻
//bestia.dev
//github.com/bestia-dev
//bestiadev.substack.com
//youtube.com/@bestia-dev-tutorials