delegate_display/lib.rs
1//! Lets you derive [`fmt`](::core::fmt) traits on types wrapping types that already implement them.
2//!
3//! [](https://github.com/Alorel/delegate-display-rs/actions/workflows/test.yml?query=branch%3Amaster)
4//! [](https://crates.io/crates/delegate-display)
5//! [](https://coveralls.io/github/Alorel/delegate-display-rs?branch=master)
6//! [](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}