delegate_display/
lib.rs

1//! Lets you derive [`fmt`](::core::fmt) traits on types wrapping types that already implement them.
2//!
3//! [![master CI badge](https://img.shields.io/github/actions/workflow/status/Alorel/delegate-display-rs/test.yml?label=master%20CI)](https://github.com/Alorel/delegate-display-rs/actions/workflows/test.yml?query=branch%3Amaster)
4//! [![crates.io badge](https://img.shields.io/crates/v/delegate-display)](https://crates.io/crates/delegate-display)
5//! [![Coverage Status](https://coveralls.io/repos/github/Alorel/delegate-display-rs/badge.svg?branch=master)](https://coveralls.io/github/Alorel/delegate-display-rs?branch=master)
6//! [![dependencies badge](https://img.shields.io/librariesio/release/cargo/delegate-display)](https://libraries.io/cargo/delegate-display)
7//!
8//! # Examples
9
10//! <details><summary>Newtype structs</summary>
11//!
12//! ```
13//! # use delegate_display::*;
14//! struct SomeType;
15//! impl core::fmt::Display for SomeType {
16//!   fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
17//!     f.write_str(">foo<")
18//!   }
19//! }
20//!
21//! #[derive(DelegateDisplay)]
22//! struct Foo(SomeType);
23//!
24//! assert_eq!(format!("{}", Foo(SomeType)), ">foo<");
25//! ```
26//!
27//! </details>
28
29//! <details><summary>Structs with 0..=1 fields</summary>
30//!
31//! ```
32//! # use delegate_display::*;
33//! struct SomeType;
34//! impl core::fmt::Debug for SomeType {
35//!   fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
36//!     f.write_str(">foo<")
37//!   }
38//! }
39//!
40//! #[derive(DelegateDebug)]
41//! struct Foo { some_field: SomeType }
42//!
43//! assert_eq!(format!("{:?}", Foo { some_field: SomeType }), ">foo<");
44//! ```
45//!
46//! </details>
47
48//! <details><summary>Enums with 0..=1 variants each</summary>
49//!
50//! ```
51//! # use delegate_display::*;
52//! struct SomeType;
53//! struct AnotherType;
54//!
55//! impl core::fmt::Display for SomeType {
56//!   fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
57//!     f.write_str(">foo<")
58//!   }
59//! }
60//! impl core::fmt::Display for AnotherType {
61//!   fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
62//!     f.write_str(">bar<")
63//!   }
64//! }
65//!
66//! #[derive(DelegateDisplay)]
67//! enum MyEnum {
68//!   Foo,
69//!   Bar(SomeType),
70//!   Qux { baz: AnotherType }
71//! }
72//!
73//! assert_eq!(format!("{}", MyEnum::Bar(SomeType)), ">foo<");
74//! assert_eq!(format!("{}", MyEnum::Qux { baz: AnotherType }), ">bar<");
75//! ```
76//!
77//! </details>
78
79//! <details><summary>Generics</summary>
80//!
81//! Generics are handled automatically for you.
82//!
83//! ```
84//! # use delegate_display::*;
85//! #
86//! #[derive(DelegateDisplay)]
87//! struct MyStruct<T>(T);
88//!
89//! #[derive(DelegateDisplay)]
90//! enum MyEnum<A, B> {
91//!   A(A),
92//!   B { value: B },
93//! }
94//!
95//! assert_eq!(format!("{}", MyStruct(50)), "50");
96//! assert_eq!(format!("{}", MyEnum::<u8, i8>::A(75)), "75");
97//! assert_eq!(format!("{}", MyEnum::<u8, i8>::B { value: -1 }), "-1");
98//! ```
99//!
100//! </details>
101
102//! <details><summary>Structs & enums with 2+ fields</summary>
103//!
104//! The field being delegated to must be marked with the appropriate attribute.
105//!
106//! ```
107//! # use delegate_display::*;
108//!
109//! #[derive(DelegateDisplay)]
110//! struct MyStruct<T> {
111//!   label: String,
112//!   #[ddisplay]
113//!   value: T,
114//! }
115//!
116//! #[derive(DelegateDebug)]
117//! enum MyEnum {
118//!   Foo(#[ddebug] String, u8),
119//!   Bar { baz: u8, #[ddebug] qux: u8 }
120//! }
121//!
122//! let my_struct = MyStruct { label: "foo".into(), value: 42 };
123//! assert_eq!(format!("{}", my_struct), "42");
124//!
125//! let my_enum = MyEnum::Foo(".".into(), 1);
126//! assert_eq!(format!("{:?}", my_enum), "\".\"");
127//!
128//! let my_enum = MyEnum::Bar { baz: 2, qux: 3 };
129//! assert_eq!(format!("{:?}", my_enum), "3");
130//! ```
131//!
132//! </details>
133
134//! <details><summary>Empty structs</summary>
135//!
136//! ```
137//! # use delegate_display::*;
138//! #
139//! #[derive(DelegateDebug, DelegateDisplay)]
140//! struct Foo;
141//!
142//! #[derive(DelegateDebug, DelegateDisplay)]
143//! struct Bar{}
144//!
145//! #[derive(DelegateDebug, DelegateDisplay)]
146//! struct Qux();
147//!
148//! assert_eq!(format!("{}-{:?}", Foo, Foo), "-");
149//! assert_eq!(format!("{}-{:?}", Bar{}, Bar{}), "-");
150//! assert_eq!(format!("{}-{:?}", Qux(), Qux()), "-");
151//! ```
152//!
153//! </details>
154
155//! <details><summary>Typed delegations</summary>
156//!
157//! Can be useful for further prettifying the output.
158//!
159//! ```
160//! # use delegate_display::*;
161//! #
162//! /// Some type that `Deref`s to the type we want to use in our formatting, in this case, `str`.
163//! #[derive(Debug)]
164//! struct Wrapper(&'static str);
165//! impl std::ops::Deref for Wrapper {
166//!   type Target = str;
167//!   fn deref(&self) -> &Self::Target {
168//!     self.0
169//!   }
170//! }
171//!
172//! #[derive(DelegateDebug)]
173//! #[ddebug(delegate_to(str))] // ignore `Wrapper` and debug the `str` it `Deref`s instead
174//! struct Typed(Wrapper);
175//!
176//! #[derive(DelegateDebug)] // Included for comparison
177//! struct Base(Wrapper);
178//!
179//! assert_eq!(format!("{:?}", Typed(Wrapper("foo"))), "\"foo\"");
180//! assert_eq!(format!("{:?}", Base(Wrapper("bar"))), "Wrapper(\"bar\")");
181//! ```
182//!
183//! </details>
184
185//! <details><summary>Custom generic bounds</summary>
186//!
187//! ```
188//! # use delegate_display::*;
189//! # use core::fmt::{Display, Formatter, self};
190//! # use std::ops::Deref;
191//! #
192//! struct CopyDisplayable<T>(T); // Implements Deref
193//! # impl<T> Deref for CopyDisplayable<T> {
194//! #   type Target = T;
195//! #   fn deref(&self) -> &Self::Target {
196//! #     &self.0
197//! #   }
198//! # }
199//!
200//! impl<T: Copy> Display for CopyDisplayable<T> {
201//!   fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
202//!     unimplemented!("Nonsense generic bound - base bounds don't work.");
203//!   }
204//! }
205//!
206//! // Without these options the implementation would have a predicate of `CopyDisplayable<T>: Debug` which would
207//! // effectively mean `T: Copy`; we can transform it to `T: Display` because `CopyDisplayable` derefs to `T`.
208//! #[derive(DelegateDisplay)]
209//! #[ddisplay(bounds(T: Display), delegate_to(T))]
210//! struct Displayable<T>(CopyDisplayable<T>);
211//!
212//! let dbg = Displayable::<String>(CopyDisplayable("cdbg".into()));
213//! assert_eq!(format!("{}", dbg), "cdbg");
214//! ```
215//!
216//! </details>
217
218//! <details><summary>Multiple traits at once</summary>
219//!
220//! Instead of re-parsing your struct/enum multiple times, you can instead derive `DelegateFmt`.
221//! It supports every individual macro's attribute along with `dany` as a catch-all default.
222//!
223//! ```
224//! # use delegate_display::*;
225//! #
226//! struct Wrapper(u8); // implements Deref
227//! # impl ::std::ops::Deref for Wrapper {
228//! #   type Target = u8;
229//! #   fn deref(&self) -> &Self::Target {
230//! #     &self.0
231//! #   }
232//! # }
233//!
234//! #[derive(DelegateFmt)]
235//! #[dfmt(dany(delegate_to(u8)), ddebug, ddisplay, dbinary)]
236//! struct MyStruct(#[dany] Wrapper, #[dbinary] Wrapper);
237//! # impl MyStruct {
238//! #   fn new(a: u8, b: u8) -> Self {
239//! #     Self(Wrapper(a), Wrapper(b))
240//! #   }
241//! # }
242//!
243//! assert_eq!(format!("{:?}", MyStruct::new(1, 2)), "1");
244//! assert_eq!(format!("{}", MyStruct::new(3, 4)), "3");
245//! assert_eq!(format!("{:b}", MyStruct::new(5, 6)), "110");
246//! ```
247//!
248//! </details>
249
250//! <details><summary>Invalid inputs</summary>
251//!
252//! ```compile_fail
253//! #[derive(delegate_display::DelegateDebug)]
254//! struct TooManyFields1 {
255//!   foo: u8,
256//!   bar: u8, // No fields marked with `#[ddebug]` or `#[dany]`
257//! }
258//! ```
259//!
260//! ```compile_fail
261//! #[derive(delegate_display::DelegateDebug)]
262//! struct TooManyFields2(u8, u8); // No fields marked with `#[ddebug]` or `#[dany]`
263//! ```
264//!
265//! ```compile_fail
266//! #[derive(delegate_display::DelegateDebug)]
267//! enum SomeEnum {
268//!   A, // this is ok
269//!   B(u8), // this is ok
270//!   C { foo: u8 }, // this is ok
271//!   D(u8, u8), // ERR: No fields marked with `#[ddebug]` or `#[dany]`
272//!   E { foo: u8, bar: u8 } // ERR: No fields marked with `#[ddebug]` or `#[dany]`
273//! }
274//! ```
275//!
276//! ```compile_fail
277//! #[derive(delegate_display::DelegateDebug)]
278//! union Foo { bar: u8 } // Unions are not supported
279//! ```
280//!
281//! ```compile_fail
282//! # use delegate_display::*;
283//! #
284//! struct NonDebug;
285//!
286//! #[derive(DelegateDebug)]
287//! struct Foo<A, B>(A, B);
288//!
289//! format!("{:?}", Foo(NonDebug, 1)); // NonDebug does not implement Debug
290//! ```
291//!
292//! </details>
293
294#![deny(clippy::correctness, clippy::suspicious)]
295#![warn(clippy::complexity, clippy::perf, clippy::style, clippy::pedantic)]
296#![allow(
297    clippy::wildcard_imports,
298    clippy::default_trait_access,
299    clippy::single_match_else
300)]
301#![warn(missing_docs)]
302
303mod implementation;
304
305const ATTR_ANY: &str = "dany";
306const ATTR_FMT: &str = "dfmt";
307
308macro_rules! alias {
309    ($($delegate_name: ident ($attr_name: ident) => $fmt_trait: literal),+ $(,)?) => {
310        /// Derive multiple [`fmt`](::core::fmt) traits at once without needing to repeatedly parse the struct/enum.
311        ///
312        /// See "Multiple traits at once" example in [crate-level documentation](crate).
313        #[proc_macro_derive(DelegateFmt, attributes(dfmt, dany, $($attr_name),+))]
314        #[inline]
315        pub fn dfmt(input: ::proc_macro::TokenStream) -> ::proc_macro::TokenStream {
316            implementation::Implementation::exec_compound(input)
317        }
318
319       $(
320            #[doc = concat!(" Derive the [`", $fmt_trait, "`](::core::fmt::", $fmt_trait, ") trait.")]
321            #[doc = ""]
322            #[doc = concat!(" Its config attribute is `", stringify!($attr_name) ,"`; alternatively, `dany` can be used")]
323            #[doc = " to configure all derived [`fmt`](::core::fmt) traits."]
324            #[doc = ""]
325            #[doc = " See [crate-level documentation](crate) for config examples."]
326            #[proc_macro_derive($delegate_name, attributes($attr_name, dany))]
327            #[inline]
328            pub fn $attr_name(input: ::proc_macro::TokenStream) -> ::proc_macro::TokenStream {
329                implementation::Implementation::exec(
330                    input,
331                    implementation::Alias::$attr_name.attr_name,
332                    implementation::Alias::$attr_name.trait_name,
333                )
334            }
335        )+
336
337        impl implementation::Alias<'static> {
338            $(
339                #[allow(non_upper_case_globals)]
340                const $attr_name: implementation::Alias<'static> = implementation::Alias {
341                    attr_name: stringify!($attr_name),
342                    trait_name: $fmt_trait,
343                };
344            )+
345        }
346    };
347}
348
349alias! {
350    DelegateBinary(dbinary) => "Binary",
351    DelegateDebug(ddebug) => "Debug",
352    DelegateDisplay(ddisplay) => "Display",
353    DelegateLowerExp(dlexp) => "LowerExp",
354    DelegateLowerHex(dlhex) => "LowerHex",
355    DelegateOctal(doctal) => "Octal",
356    DelegatePointer(dpointer) => "Pointer",
357    DelegateUpperExp(duexp) => "UpperExp",
358    DelegateUpperHex(duhex) => "UpperHex",
359}