#dbg #pretty-print #macro #file-line #line-column #programming

pretty_dbg

pretty_dbg! is a copy of dbg!, just modified :#? to :# for pretty print. format_dbg! works like eprintln! with added file, line and column

4 stable releases

1.0.54 Feb 22, 2024
1.0.49 Feb 2, 2024

#129 in Debugging

Download history 2/week @ 2024-01-29 190/week @ 2024-02-19 216/week @ 2024-02-26 41/week @ 2024-03-04 9/week @ 2024-03-11 6/week @ 2024-03-18 32/week @ 2024-03-25 10/week @ 2024-04-01 47/week @ 2024-04-15

90 downloads per month

MIT license

16KB

Rust macro pretty_dbg!()

pretty_dbg! is a copy of dbg!, just modified :#? to :# for pretty print. format_dbg! works like eprintln! with added file, line and column
version: 1.0.54 date: 2024-02-22 author: bestia.dev repository: GitHub

maintained ready_for_use

crates.io Documentation crev reviews RustActions

latest doc License pretty_dbg

Lines in Rust code Lines in Doc comments Lines in Comments Lines in examples Lines in tests

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 debugging.
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 pretty_dbg! macro

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!(), std::column!())
    };
    ($val:expr $(,)?) => {
        match $val {
            tmp => {
                std::eprintln!("[{}:{}:{}] {} = {:#}",
                    std::file!(), std::line!(), std::column!(), 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!(), std::column!())
    };
    ($val:expr $(,)?) => {
        match $val {
            tmp => {
                std::eprintln!("[{}:{}:{}] {} = {:#}",
                    std::file!(), std::line!(), std::column!(), 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 format_dbg! macro

Sometimes when debugging I want to write some string to the output and not only a variable.
The macro dbg! and consequently pretty_dbg! are not the right tools for that.

Again I could use simply the eprintln!, but then it is not easy to find and remove this debugging code.
Let's make another macro format_dbg!. It is just a simpler dbg! with another name.

Run this code in the rust playground:

fn main() {
/// copy of the macro dbg!, just modified :#? to :# for pretty print
#[macro_export]
macro_rules! pretty_dbg {
    () => {
        std::eprintln!("[{}:{}:{}]", std::file!(), std::line!(), std::column!())
    };
    ($val:expr $(,)?) => {
        match $val {
            tmp => {
                std::eprintln!("[{}:{}:{}] {} = {:#}",
                    std::file!(), std::line!(), std::column!(), std::stringify!($val), &tmp);
                tmp
            }
        }
    };
    ($($val:expr),+ $(,)?) => {
        ($(std::pretty_dbg!($val)),+,)
    };
}

/// format_dbg! is a version of dbg! that uses the formatting rules from the macro eprintln!
/// Just like dbg!, it prefixes the stderr output with file!, line! and column!
#[macro_export]
macro_rules! format_dbg {
    ($($arg:tt)*) => {{
        std::eprint!("[{}:{}:{}] ", std::file!(), std::line!(), std::column!());
        std::eprintln!($($arg)*);
    }};
}
    let val="123456789";

    dbg!("using the dbg! macro : {val}");
    pretty_dbg!("using the pretty_dbg! macro : {val}");
    format_dbg!("using the format_dbg! macro : {val}");
}

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.

Development details

Read the development details in a separate md file:
DEVELOPMENT.md

Releases changelog

Read the changelog in a separate md file:
RELEASES.md

TODO

Nothing big in the near future.

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

No runtime deps