# ty(pe)f(ormat)ling
easier implementations of the formatting traits, supporting structs, enums
```
# use {::tyfling::{display, lower_hex}, ::core::fmt::{Display, LowerHex}};
#[display("foo: {f0}")]
#[lower_hex("foo: {f0:x}")]
struct Foo<#[display] #[lower_hex] T>(T);
println!("{}", Foo(4)); // foo: 4
println!("{:x}", Foo(0x57e11a)); // foo: 57e11a
```
## provides
9 attribute proc macros
- `binary`, implementing the `Binary` trait
- `debug`, implementing the `Debug` trait
- `display`, implementing the `Display` trait
- `lower_exp`, implementing the `LowerExp` trait
- `lower_hex`, implementing the `LowerHex` trait
- `octal`, implementing the `Octal` trait
- `pointer`, implementing the `Pointer` trait
- `upper_exp`, implementing the `UpperExp` trait
- `upper_hex`, implementing the `UpperHex` trait
## usage
### struct
attach the attribute to the struct, and pass in formatting info
### enum
attach the attribute to the enum and each variant, the enum's attribute should be empty, while the variants should have their individual formatting info
### syntax
`display` is used as an example, but the functionality is identical for all of the attributes
- `#[display("formatting string")]` formats without any variation, just a plain format string
- `#[display("fmt string that references {f0} tuple fields")]` for tuple structs and tuple enum variants, their fields can be accessed as the `f{n}` local variables (eg: `f0`, `f1`, etc)
- `#[display("fmt string that references {n} struct fields")]` for named field structs and named field enum variants, their fields are available verbatim
- `#[display("fmt string doing {} calculations {how_often}", how_much = if 42 == ~42 { "a few" } else { "at least 1" }, how_often = "uhh, once?")]` format strings can have arguments like normal format strings
- `#[display(.1)]` delegate to a tuple field of the [struct,enum variant] (equivalent to `"{1}"` (or whatever format specifier is used for a given trait) but more readable)
- `#[display(.some_field)]` delegate to a named field of the [struct,enum variant] (equivalent to `"{some_field}"`)
if any of the format strings for a type formats a generic parameter that isnt already constrained to the given trait, apply the attribute to the generic type to constrain just the impl:
```rust
# use {::tyfling::display, ::core::fmt::Display};
// ok, because the parameter is constrained to Display
#[display("foo: {f0}")]
struct Foo<T: Display>(T);
// ok, because the parameter is formatted
#[display("bar: ignoring the field")]
struct Bar<T>(T);
```
```compile_fail
# use {::tyfling::display, ::core::fmt::Display};
// NOT ok, because there's no bound/attribute, and the parameter is used
#[display("bar: {f0}")]
struct Baz<T>(T);
```
```rust
# use {::tyfling::display, ::core::fmt::Display};
// ok, because attribute constrains the impl
// does not constrain the struct, only the impl
#[display("qux: {f0}")]
struct Qux<#[display] T>(T);
```
## example
```rust
# use ::tyfling::display;
// set a format string and implement Display
#[display("diagnostic (from {source_file_name}): {kind}")]
struct Diagnostic</* since the format string uses a generic type, ensure that the impl is constrained to T: Display */ #[display] O> {
kind: DiagnosticKind<O>,
source_file_name: &'static str,
}
#[display]
enum DiagnosticKind<#[display] O> {
#[display("forgot macro bang (at bytes {} to {}): {code}", span.start, span.end)]
ForgotMacro {
code: &'static str,
span: ::core::ops::Range<usize>,
},
// no additional context to add, delegate to the indexed field
#[display(.0)]
Message(String),
// unknown context, just delegate to the error fields
#[display(.error)]
OtherError {
error: O,
fatal: bool,
},
}
#[display("displayable!")]
struct Displayable;
# fn main() {
assert_eq!(format!("{}", Diagnostic::<Displayable> {
kind: DiagnosticKind::ForgotMacro {
code: "fn main() { println(\"hello world!\"); }",
span: 12..20,
},
source_file_name: "src/lib.rs",
}), "diagnostic (from src/lib.rs): forgot macro bang (at bytes 12 to 20): fn main() { println(\"hello world!\"); }");
assert_eq!(format!("{}", Diagnostic::<Displayable> {
kind: DiagnosticKind::OtherError {
error: Displayable,
fatal: false,
},
source_file_name: "src/lib.rs",
}), "diagnostic (from src/lib.rs): displayable!");
# }
```
```compile_fail
# use ::tyfling::display;
#
# // set a format string and implement Display
# #[display("diagnostic (from {source_file_name}): {kind}")]
# struct Diagnostic</* since the format string uses a generic type, ensure that the impl is constrained to T: Display */ #[display] O> {
# kind: DiagnosticKind<O>,
# source_file_name: &'static str,
# }
#
# #[display]
# enum DiagnosticKind<#[display] O> {
# #[display("forgot macro bang (at bytes {} to {}): {code}", span.start, span.end)]
# ForgotMacro {
# code: &'static str,
# span: ::core::ops::Range<usize>,
# },
# // no additional context to add, delegate to the indexed field
# #[display(.0)]
# Message(String),
# // unknown context, just delegate to the error fields
# #[display(.error)]
# OtherError {
# error: O,
# fatal: bool,
# },
# }
struct NotDisplayable;
# fn main() {
//! compile error, cannot Display, because NotDisplayable is not displayable
assert_eq!(format!("{}", Diagnostic::<NotDisplayable> {
kind: DiagnosticKind::OtherError {
error: NotDisplayable,
fatal: true,
},
source_file_name: "src/lib.rs",
}), "");
# }
```