2 releases
0.1.1 | Dec 27, 2024 |
---|---|
0.1.0 | Dec 26, 2024 |
#1307 in Rust patterns
241 downloads per month
22KB
481 lines
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 theBinary
traitdebug
, implementing theDebug
traitdisplay
, implementing theDisplay
traitlower_exp
, implementing theLowerExp
traitlower_hex
, implementing theLowerHex
traitoctal
, implementing theOctal
traitpointer
, implementing thePointer
traitupper_exp
, implementing theUpperExp
traitupper_hex
, implementing theUpperHex
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 thef{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:
# 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);
# 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);
# 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
# 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!");
# }
# 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",
}), "");
# }
Dependencies
~215–660KB
~16K SLoC