# Cubob
Some Rust helpers for structural output in display mode.
## Name
This project has autogenerated name, thanks to [This Word Does Not Exist](https://thisworddoesnotexist.com) project.
The word definition can be checked [here](https://l.thisworddoesnotexist.com/SHko).
## Purpose
Rust core library provides some nice output primitives as methods of [core::fmt::Formatter](https://doc.rust-lang.org/core/fmt/struct.Formatter.html): [debug_list](https://doc.rust-lang.org/core/fmt/struct.Formatter.html#method.debug_list), [debug_struct](https://doc.rust-lang.org/core/fmt/struct.Formatter.html#method.debug_struct) and so on.
Moreover, it also provides an amazing derive macro for [core::fmt::Debug](https://doc.rust-lang.org/core/fmt/trait.Debug.html), which perfectly performs all the routine of implementing usage of mentioned primitives.
Unfortunately, there no display-mode analogs of those primitives, and there are no derive macro for [core::fmt::Display](https://doc.rust-lang.org/core/fmt/trait.Display.html) itself.
The last fact is stated in the documentation right with the explanation: it is so to encourage developers to implement core::fmt::Display accordingly to the related type purpose.
I agree with that approach, but I don't believe, however, that this explanation is enough to avoid implementing any suitable primitives at all, whereas existing primitives are too debug-oriented (for example, they always print type name, or always print None values even when it isn't actually needed in display mode).
This library/crate/repo is a little attempt to fullfill mentiond gap: it provides some tiny primitves for discussed cases (see examples).
## Examples
Lets consider the next structure:
```rust
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
```
With standard std::fmt::Debug implementation via derive macro it will have the next output for `{:?}` format:
```console
Point { x: 0, y: 0 }
```
And the next output with prettified (`{:#?}`) format:
```console
Point {
x: 0,
y: 0,
}
```
Continuing the example, one can describe the next structure with related outputs (accordingly):
```rust
#[derive(Debug)]
struct Line {
a: Point,
b: Point,
}
```
```console
Line { a: Point { x: 0, y: 0 }, b: Point { x: 1, y: 1 } }
```
```console
Line {
a: Point {
x: 0,
y: 0,
},
b: Point {
x: 1,
y: 1 ,
}
}
```
The same output will be reproduced if one implements it for display mode (actually, this is the way it is implemented for debug mode "inside" the derive macro):
```rust
impl Display for Point {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.debug_struct("Point")
.field("x", &self.x)
.field("y", &self.y)
.finish()
}
}
impl Display for Line {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.debug_struct("Line")
.field("a", &self.a)
.field("b", &self.b)
.finish()
}
}
```
Such output in display mode can be too detailed (I personally found type information quite annoying). One can do slightly better using some tricks with std::fmt::Formatter methods:
```rust
impl Display for Point {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.debug_map()
.entry(&"x", &self.x)
.entry(&"y", &self.y)
.finish()
}
}
impl Display for Line {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.debug_map()
.entry(&"a", &self.a)
.entry(&"b", &self.b)
.finish()
}
}
```
Here is the result (prettified to simplify reading):
```console
{
"a": Point {
x: 0,
y: 0,
},
"b": Point {
x: 1,
y: 1,
},
}
```
Type name for `Line` has been removed, but stayed for `Point` - because due to call of `debug_map` (*debug_* !) it uses debug mode for outputting all entries, and provided `std::fmt::Display` implementation isn't even used.
Moreover, this approach puts `"` symbols around field names as they are treated like map keys, which is fair, but seems redundant in considered case.
The next attempt will deal with both mentioned effect:
```rust
use std::format_args;
impl Display for Point {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.debug_set()
.entry(&format_args!("x: {:#}", self.x))
.entry(&format_args!("y: {:#}", self.y))
.finish()
}
}
impl Display for Line {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.debug_set()
.entry(&format_args!("a: {:#}", self.a))
.entry(&format_args!("b: {:#}", self.b))
.finish()
}
}
```
This approach reaches our goal for prettified format:
```console
{
a: {
x: 0,
y: 0,
},
b: {
x: 1,
y: 1,
},
}
```
But it has a problem for non-prettified one:
```console
{a: {
x: 0,
y: 0,
}, b: {
x: 1,
y: 1,
}}
```
The reason is in format strings like `"a: {:#}"`. It doesn't matter for `Point` due to scalar values of its fields, but it strikes back for `Line`, because its fields are structs themselves: they are always outputted prettified, because related format string specify that no matter what - even if the `Line` examplar itself is being outputted as non-prettified.
To avoid those problems one should inject some variation in the output code:
```rust
impl Display for Point {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
if f.alternate() {
f.debug_set()
.entry(&format_args!("x: {:#}", self.x))
.entry(&format_args!("y: {:#}", self.y))
.finish()
} else {
f.debug_set()
.entry(&format_args!("x: {}", self.x))
.entry(&format_args!("y: {}", self.y))
.finish()
}
}
}
impl Display for Line {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
if f.alternate() {
f.debug_set()
.entry(&format_args!("a: {:#}", self.a))
.entry(&format_args!("b: {:#}", self.b))
.finish()
} else {
f.debug_set()
.entry(&format_args!("a: {}", self.a))
.entry(&format_args!("b: {}", self.b))
.finish()
}
}
}
```
The result is:
```console
Non-prettified: {a: {x: 0, y: 0}, b: {x: 1, y: 1}}
Prettified: {
a: {
x: 0,
y: 0,
},
b: {
x: 1,
y: 1,
},
}
```
It works! But the code became quite messy, Its no big deal for small structs and programs, but becomes one when a program have a lot of structs with a lot of fields. So here is a Cubob solution: some abstractions which help to achieve the same goals with simplier actions:
```rust
use cubob::display_struct;
impl Display for Point {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
display_struct(
f,
&[
(&"x", &self.x),
(&"y", &self.y),
],
)
}
}
impl Display for Line {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
display_struct(
f,
&[
(&"a", &self.x),
(&"b", &self.y),
],
)
}
}
```
This code produces the same behaviour as the previous, but lets to keep code simplier and clearer.