const_format/const_debug_derive.rs
1/// Derives const debug formatting for a type.
2///
3/// Derives the [`FormatMarker`] trait, and defines an `const_debug_fmt` inherent
4/// method to format a type at compile-time.
5///
6/// # Features
7///
8/// This derive macro is only available with the "derive" feature,
9/// and Rust 1.83.0, because is uses mutable references in const.
10///
11/// # Limitations
12///
13/// Compile-time formatting currently imposes these limitations on users,
14/// this derive macro has some mitigations for some of them.
15///
16/// ### Generic impls
17///
18/// Because the formatting of custom types is implemented with duck typing,
19/// it's not possible to format generic types, instead you must do either of these:
20///
21/// - Provide all the implementations ahead of time, what the [`impls attribute`] is for.
22///
23/// - Provide a macro that formats the type.
24/// The `call_debug_fmt` macro is a version of this that formats generic std types,
25/// then it can be used to format fields of the type with the
26/// [`#[cdeb(with_macro = "....")]`](#cdeb_with_macro) attribute.
27///
28/// These are the things that this macro does to mitigate the limitations:
29///
30/// - Allows users to provide a function/macro/wrapper to format a field.
31///
32/// - Automatically detect some builtin/standard library types that are generic.
33///
34/// - Allow users to ignore a field.
35///
36/// # Container Attributes
37///
38/// These attributes go on the type itself, rather than the fields.
39///
40/// ### `#[cdeb(debug_print)]`
41///
42/// Panics with the output of the expanded derive.
43///
44/// ### `#[cdeb(impls(....))]`
45///
46/// Allows users to implement const debug formatting for multiple different
47/// concrete instances of the type.
48///
49/// When this attribute is used it disables the default implementation
50/// that uses the type parameters generically.
51///
52/// Example:
53///
54/// ```rust
55/// #[derive(const_format::ConstDebug)]
56/// #[cdeb(impls(
57/// "Foo<u8, u64>",
58/// "<T> Foo<u16, T>",
59/// "<T> Foo<u32, T> where T: 'static",
60/// ))]
61/// struct Foo<A, B>(A, *const B);
62/// ```
63///
64/// In this example, there's exactly three impls of
65/// the `const_debug_fmt` method and [`FormatMarker`] trait.
66///
67/// ### `#[cdeb(crate = "foo::bar")]`
68///
69/// The path to the `const_format` crate, useful if you want to reexport the ConstDebug macro,
70/// or rename the `const_format` crate in the Cargo.toml .
71///
72/// Example of renaming the `const_format` crate in the Cargo.toml file:
73/// ```toml
74/// cfmt = {version = "0.*", package = "const_format"}
75/// ```
76///
77/// # Field attributes
78///
79/// ### `#[cdeb(ignore)]`
80///
81/// Ignores the field, pretending that it doesn't exist.
82///
83/// ### `#[cdeb(with = "module::function")]`
84///
85/// Uses the function at the passed-in path to format the field.
86///
87/// The function is expected to have this signature:
88/// ```ignored
89/// const fn(&FieldType, &mut const_format::Formatter<'_>) -> Result<(), const_format::Error>
90/// ```
91///
92/// <span id = "cdeb_with_macro"> </span>
93///
94/// ### `#[cdeb(with_macro = "module::the_macro")]`
95///
96/// Uses the macro at the passed-in path to format the field.
97///
98/// The macro is expected to be callable like a function with this signature:
99/// ```ignored
100/// const fn(&FieldType, &mut const_format::Formatter<'_>) -> Result<(), const_format::Error>
101/// ```
102///
103/// ### `#[cdeb(with_wrapper = "module::Wrapper")]`
104///
105/// Uses the wrapper type to print the field.
106///
107/// The wrapper is expected to wrap a reference to the field type,
108/// to have an implementation of the [`FormatMarker`] trait,
109/// and have a method with this signature:
110/// ```ignored
111/// const fn const_debug_fmt(
112/// self,
113/// &mut const_format::Formatter<'_>,
114/// ) -> Result<(), const_format::Error>
115/// ```
116/// (`self` can be taken by reference or by value)
117///
118/// ### `#[cdeb(is_a(....))]`
119///
120/// Gives the derive macro a hint of what the type is.
121///
122/// For standard library types,
123/// this is necessary if you're using a type alias, since the derive macro detects
124/// those types syntactically.
125///
126/// These are the valid ways to use this attribute:
127///
128/// - `#[cdeb(is_a(array))]`/`#[cdeb(is_a(slice))]`:
129/// Treats the field as being a slice/array,
130/// printing the elements of std or user-defined type with const debug formatting.
131///
132/// - `#[cdeb(is_a(Option))]`/`#[cdeb(is_a(option))]`:
133/// Treats the field as being an Option,
134/// printing the contents of std or user-defined type with const debug formatting.
135///
136/// - `#[cdeb(is_a(newtype))]`:
137/// Treats the field as being being a single field tuple struct,
138/// using the identifier of the field type as the name of the struct,
139/// then printing the single field of std or user-defined type with const debug formatting.
140///
141/// - `#[cdeb(is_a(non_std))]`/`#[cdeb(is_a(not_std))]`:
142/// This acts as an opt-out for the automatic detection of std types,
143/// most likely needed for types named `Option`.
144///
145/// # Examples
146///
147/// ### Basic
148///
149/// This example demonstrates using the derive without using any helper attributes.
150///
151/// ```rust
152///
153/// use const_format::{ConstDebug, formatc};
154///
155/// use std::cmp::Ordering;
156///
157/// const E_FOO: &str = formatc!("{:?}", Enum::Foo);
158/// const E_BAR: &str = formatc!("{:?}", Enum::Bar(10));
159/// const E_BAZ: &str = formatc!("{:?}", Enum::Baz{order: Ordering::Less});
160///
161/// const S_UNIT: &str = formatc!("{:?}", Unit);
162/// const S_BRACED: &str = formatc!("{:?}", Braced{is_true: false, optional: Some(Unit)});
163///
164/// assert_eq!(E_FOO, "Foo");
165/// assert_eq!(E_BAR, "Bar(10)");
166/// assert_eq!(E_BAZ, "Baz { order: Less }");
167///
168/// assert_eq!(S_UNIT, "Unit");
169/// assert_eq!(S_BRACED, "Braced { is_true: false, optional: Some(Unit) }");
170///
171///
172/// #[derive(ConstDebug)]
173/// enum Enum {
174/// Foo,
175/// Bar(u32),
176/// Baz{
177/// order: Ordering,
178/// },
179/// }
180///
181/// #[derive(ConstDebug)]
182/// struct Unit;
183///
184/// #[derive(ConstDebug)]
185/// struct Braced {
186/// is_true: bool,
187/// optional: Option<Unit>,
188/// }
189///
190/// ```
191///
192/// ### Generic type
193///
194/// This example demonstrates the `#[cdeb(impls)]` attribute,
195/// a workaround for deriving this trait for generic types,
196/// specifying a list of impls of types that unconditionally implement const debug formatting
197///
198/// ```rust
199///
200/// use const_format::{ConstDebug, formatc};
201///
202/// use std::marker::PhantomData;
203///
204///
205/// const S_U32: &str = formatc!("{:?}", Foo(10));
206///
207/// const S_STR: &str = formatc!("{:?}", Foo("hello"));
208///
209/// const S_PHANTOM: &str = formatc!("{:?}", Foo(PhantomData::<()>));
210///
211/// assert_eq!(S_U32, r#"Foo(10)"#);
212/// assert_eq!(S_STR, r#"Foo("hello")"#);
213/// assert_eq!(S_PHANTOM, r#"Foo(PhantomData)"#);
214///
215///
216/// // This type implements const debug formatting three times:
217/// // - `Foo<u32>`
218/// // - `Foo<&str>`
219/// // - `Foo<PhantomData<T>>`: with a generic `T`
220/// #[derive(ConstDebug)]
221/// #[cdeb(impls(
222/// "Foo<u32>",
223/// "Foo<&str>",
224/// "<T> Foo<PhantomData<T>>",
225/// ))]
226/// struct Foo<T>(T);
227///
228/// ```
229///
230/// ### `is_a` attributes
231///
232/// This example demonstrates when you would use the `is_a` attributes.
233///
234/// ```rust
235///
236/// use const_format::{ConstDebug, formatc};
237///
238/// use std::{
239/// cmp::Ordering,
240/// marker::PhantomData,
241/// num::Wrapping,
242/// };
243///
244/// const STRUCT: &Struct = &Struct {
245/// arr: [Ordering::Less, Ordering::Equal, Ordering::Greater, Ordering::Less],
246/// opt: Some(Unit),
247/// wrap: Wrapping(21),
248/// not_option: Option(PhantomData), // This is not the standard library `Option`
249/// };
250///
251/// const S_STRUCT: &str = formatc!("{STRUCT:#?}");
252///
253/// const EXPECTED: &str = "\
254/// Struct {
255/// arr: [
256/// Less,
257/// Equal,
258/// Greater,
259/// Less,
260/// ],
261/// opt: Some(
262/// Unit,
263/// ),
264/// wrap: Wrapping(
265/// 21,
266/// ),
267/// not_option: Option(
268/// PhantomData,
269/// ),
270/// }";
271///
272/// fn main(){
273/// assert_eq!(S_STRUCT, EXPECTED);
274/// }
275///
276/// #[derive(ConstDebug)]
277/// struct Struct {
278/// // `Ordering` implements const debug formatting,
279/// // but `[Ordering; 4]` does not, so this attribute is required for the
280/// // derive macro to generate code to format this array field.
281/// #[cdeb(is_a(array))]
282/// arr: Array,
283///
284/// // Attribute is required to tell the derive macro that this is an
285/// // `Option` wrapping a user-defined type,
286/// // since `Option<Unit>` doesn't implement const debug formatting.
287/// #[cdeb(is_a(option))]
288/// opt: Opt,
289///
290/// // Attribute is required because `Wrapping<usize>` is a newtype struct
291/// // that doesn't implement const debug formatting,
292/// // so the derive generates code to format it.
293/// #[cdeb(is_a(newtype))]
294/// wrap: Wrapping<usize>,
295///
296/// // Attribute is required for the field to be treated as a user-defined type,
297/// // otherwise it'd be assumed to be `Option` from the standard library.
298/// #[cdeb(is_a(not_std))]
299/// not_option: Option<u32>,
300///
301/// }
302///
303/// type Array = [Ordering; 4];
304///
305/// type Opt = std::option::Option<Unit>;
306///
307/// #[derive(ConstDebug)]
308/// struct Unit;
309///
310/// #[derive(ConstDebug)]
311/// struct Option<T>(PhantomData<T>);
312///
313/// ```
314///
315/// [`FormatMarker`]: ./marker_traits/trait.FormatMarker.html
316/// [`impls attribute`]: #cdebimpls
317///
318///
319///
320///
321/// ### Renamed import
322///
323/// This example demonstrates that you can use all the macros when the `const_format`
324/// crate is renamed.
325///
326/// ```rust
327/// # extern crate self as const_format;
328/// # extern crate const_format as cfmt;
329/// # fn main() {
330/// use cfmt::{
331/// for_examples::Unit,
332/// ConstDebug, formatc,
333/// };
334///
335/// #[derive(ConstDebug)]
336/// #[cdeb(crate = "cfmt")]
337/// struct Foo {
338/// bar: &'static str,
339/// baz: Unit
340/// }
341///
342/// const TEXT: &str = formatc!("{:?}", Foo{ bar: "hello", baz: Unit });
343///
344/// assert_eq!(TEXT, r#"Foo { bar: "hello", baz: Unit }"#);
345///
346/// # }
347/// ```
348///
349#[cfg_attr(feature = "__docsrs", doc(cfg(feature = "derive")))]
350#[cfg(feature = "derive")]
351pub use const_format_proc_macros::ConstDebug;