delegate-display

derive(Display, Debug) for structs/enums with one member


Keywords
debug, derive, display, enum, macro
License
MIT

Documentation

Lets you derive fmt traits on types wrapping types that already implement them.

master CI badge crates.io badge Coverage Status dependencies badge

Examples

Newtype structs
struct SomeType;
impl core::fmt::Display for SomeType {
  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
    f.write_str(">foo<")
  }
}

#[derive(DelegateDisplay)]
struct Foo(SomeType);

assert_eq!(format!("{}", Foo(SomeType)), ">foo<");
Structs with 0..=1 fields
struct SomeType;
impl core::fmt::Debug for SomeType {
  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
    f.write_str(">foo<")
  }
}

#[derive(DelegateDebug)]
struct Foo { some_field: SomeType }

assert_eq!(format!("{:?}", Foo { some_field: SomeType }), ">foo<");
Enums with 0..=1 variants each
struct SomeType;
struct AnotherType;

impl core::fmt::Display for SomeType {
  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
    f.write_str(">foo<")
  }
}
impl core::fmt::Display for AnotherType {
  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
    f.write_str(">bar<")
  }
}

#[derive(DelegateDisplay)]
enum MyEnum {
  Foo,
  Bar(SomeType),
  Qux { baz: AnotherType }
}

assert_eq!(format!("{}", MyEnum::Bar(SomeType)), ">foo<");
assert_eq!(format!("{}", MyEnum::Qux { baz: AnotherType }), ">bar<");
Generics

Generics are handled automatically for you.

#[derive(DelegateDisplay)]
struct MyStruct<T>(T);

#[derive(DelegateDisplay)]
enum MyEnum<A, B> {
  A(A),
  B { value: B },
}

assert_eq!(format!("{}", MyStruct(50)), "50");
assert_eq!(format!("{}", MyEnum::<u8, i8>::A(75)), "75");
assert_eq!(format!("{}", MyEnum::<u8, i8>::B { value: -1 }), "-1");
Structs & enums with 2+ fields

The field being delegated to must be marked with the appropriate attribute.

#[derive(DelegateDisplay)]
struct MyStruct<T> {
  label: String,
  #[ddisplay]
  value: T,
}

#[derive(DelegateDebug)]
enum MyEnum {
  Foo(#[ddebug] String, u8),
  Bar { baz: u8, #[ddebug] qux: u8 }
}

let my_struct = MyStruct { label: "foo".into(), value: 42 };
assert_eq!(format!("{}", my_struct), "42");

let my_enum = MyEnum::Foo(".".into(), 1);
assert_eq!(format!("{:?}", my_enum), "\".\"");

let my_enum = MyEnum::Bar { baz: 2, qux: 3 };
assert_eq!(format!("{:?}", my_enum), "3");
Empty structs
#[derive(DelegateDebug, DelegateDisplay)]
struct Foo;

#[derive(DelegateDebug, DelegateDisplay)]
struct Bar{}

#[derive(DelegateDebug, DelegateDisplay)]
struct Qux();

assert_eq!(format!("{}-{:?}", Foo, Foo), "-");
assert_eq!(format!("{}-{:?}", Bar{}, Bar{}), "-");
assert_eq!(format!("{}-{:?}", Qux(), Qux()), "-");
Typed delegations

Can be useful for further prettifying the output.

/// Some type that `Deref`s to the type we want to use in our formatting, in this case, `str`.
#[derive(Debug)]
struct Wrapper(&'static str);
impl std::ops::Deref for Wrapper {
  type Target = str;
  fn deref(&self) -> &Self::Target {
    self.0
  }
}

#[derive(DelegateDebug)]
#[ddebug(delegate_to(str))] // ignore `Wrapper` and debug the `str` it `Deref`s instead
struct Typed(Wrapper);

#[derive(DelegateDebug)] // Included for comparison
struct Base(Wrapper);

assert_eq!(format!("{:?}", Typed(Wrapper("foo"))), "\"foo\"");
assert_eq!(format!("{:?}", Base(Wrapper("bar"))), "Wrapper(\"bar\")");
Custom generic bounds
struct CopyDisplayable<T>(T); // Implements Deref

impl<T: Copy> Display for CopyDisplayable<T> {
  fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
    unimplemented!("Nonsense generic bound - base bounds don't work.");
  }
}

// Without these options the implementation would have a predicate of `CopyDisplayable<T>: Debug` which would
// effectively mean `T: Copy`; we can transform it to `T: Display` because `CopyDisplayable` derefs to `T`.
#[derive(DelegateDisplay)]
#[ddisplay(bounds(T: Display), delegate_to(T))]
struct Displayable<T>(CopyDisplayable<T>);

let dbg = Displayable::<String>(CopyDisplayable("cdbg".into()));
assert_eq!(format!("{}", dbg), "cdbg");
Multiple traits at once

Instead of re-parsing your struct/enum multiple times, you can instead derive DelegateFmt. It supports every individual macro's attribute along with dany as a catch-all default.

struct Wrapper(u8); // implements Deref

#[derive(DelegateFmt)]
#[dfmt(dany(delegate_to(u8)), ddebug, ddisplay, dbinary)]
struct MyStruct(#[dany] Wrapper, #[dbinary] Wrapper);

assert_eq!(format!("{:?}", MyStruct::new(1, 2)), "1");
assert_eq!(format!("{}", MyStruct::new(3, 4)), "3");
assert_eq!(format!("{:b}", MyStruct::new(5, 6)), "110");
Invalid inputs
#[derive(delegate_display::DelegateDebug)]
struct TooManyFields1 {
  foo: u8,
  bar: u8, // No fields marked with `#[ddebug]` or `#[dany]`
}
#[derive(delegate_display::DelegateDebug)]
struct TooManyFields2(u8, u8); // No fields marked with `#[ddebug]` or `#[dany]`
#[derive(delegate_display::DelegateDebug)]
enum SomeEnum {
  A, // this is ok
  B(u8), // this is ok
  C { foo: u8 }, // this is ok
  D(u8, u8), // ERR: No fields marked with `#[ddebug]` or `#[dany]`
  E { foo: u8, bar: u8 } // ERR: No fields marked with `#[ddebug]` or `#[dany]`
}
#[derive(delegate_display::DelegateDebug)]
union Foo { bar: u8 } // Unions are not supported
struct NonDebug;

#[derive(DelegateDebug)]
struct Foo<A, B>(A, B);

format!("{:?}", Foo(NonDebug, 1)); // NonDebug does not implement Debug