1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
/**
Derives the [`PanicFmt`](trait@crate::PanicFmt) trait.
This requires the `"derive"` feature, disabled by default.
This generates a generic [`PanicFmt`](trait@crate::PanicFmt) impl,
as well as one or more inherent `to_panicvals` method definitions
[as described in the trait docs](trait@crate::PanicFmt#implementor).
You can also use [`impl_panicfmt`] as an alternative that requires less time to compile
from scratch, but requires repeating the type definition.
[Jump straight to examples](#examples)
# Limitations
### Type parameters
Types with type parameters can't be generically formatted,
to work around this you can use either or both of these attributes:
- `#[pfmt(ignore(T))]`:
if the type parameter(s) are only used in marker types (eg: `PhantomData`).
- `#[pfmt(impl Foo<Bar, Baz>)]`:
to implement panic formatting with concrete type arguments
(this attribute can be used multiple times to add impls).
This limitation is caused by:
- the lack of trait bound support in stable const fns.
- the need to [have a concrete type argument](#concrete-pv-count)
[example below](#type-parameter-example)
### Const parameters
Const parameters must not affect the value of the `PanicFmt::PV_COUNT` of this type,
since the const parameter [must be replaceable with a concrete value](#concrete-pv-count).
<br>Note that arrays have a `PV_COUNT` of `1` for all lengths.
<a id = "concrete-pv-count"></a>
### Concrete `Self` type for `PanicFmt::PV_COUNT`
The `to_panicvals` method that this macro generates roughly returns a
```text
[PanicVal<'_>; <Self as PanicFmt>::PV_COUNT]
```
Because of limitations in stable const generics,
the generic arguments of `Self` in the above code must be replaced with concrete arguments,
requiring:
- Lifetime arguments to be replaced with `'_`
- Type arguments to be replaced with concrete types
(usually `()` or the concrete types used in [`#[pfmt(impl ....)]`](#pfmt-impl-attr) attributes)
- Const arguments to be replaced with concrete values (usually the default value for the type)
# Attributes
### Container attributes
Attributes used above the type definition.
### `#[pfmt(crate = foo::bar)]`
Replaces the path to `const_panic` with `foo::bar`
[example](#crate-example)
### `#[pfmt(debug_print)]`: <br>
For diagnostics, causes the derive macro to panic with the code generated by it.
<a id = "display_fmt-attr"></a>
### `#[pfmt(display_fmt = fn_expression)]`:
Calls the provided function when the deriving type is attempted to be Display-formatted.
[example below](#display_fmt-example)
<a id = "panicvals_lower_bound-attr"></a>
### `#[pfmt(panicvals_lower_bound = usize_expression)]`:
Tells the derive the minimum amount of [`PanicVal`]s that this type needs,
generally only useful in combination with the [`display_fmt`](#display_fmt-attr) attribute.
<a id = "pfmt-ignored-attr"></a>
### `#[pfmt(ignored(T, C))]`
Accepts the names of type and const parameters,
replacing the generic arguments in [`here`](#concrete-pv-count) with a concrete value.
For type parameters, this replaces the type parameter with `()` unless overriden,
and also tells the derive not to require `T: PanicFmt` in
the `PanicFmt` implementation for the deriving type
(since the type parameter is not formatted).
Const parameters are ignored by default,
replacing them with the default value for that type [^1]
The concrete value for each generic parameter can be overriden with `T = value`
examples:
- `#[pfmt(ignored(T))]`
- `#[pfmt(ignored(T = u16))]`
- `#[pfmt(ignored(T = u32, C))]`
- `#[pfmt(ignored(T, C = 100))]`
- `#[pfmt(ignored(U = str, A = false))]`
([more conplete example](#phantom-type-parameter-example))
[^1]: a private trait is used to get the default value for const parameters.
<a id = "pfmt-impl-attr"></a>
### `#[pfmt(impl Foo<Bar, BAZ>)]`
Tells the derive to generate an inherent `to_panicvals` method for the type in the attribute
(it must be the deriving type with concrete enough generic arguments).
examples:
- `#[pfmt(impl Foo<u32, 10>)]`
- `#[pfmt(impl<T> Foo<T, 'A'>)]`:
this also requires a [`#[pfmt(ignored(T))]`](#pfmt-ignored-attr) attribute
([more conplete example](#type-parameter-example))
# Examples
### Basic struct
```rust
use const_panic::{FmtArg, PanicFmt};
assert_eq!(
const_panic::concat_!(Foo { x: 3, y: &[3, 5, 8] }),
"Foo { x: 3, y: [3, 5, 8] }",
);
#[derive(PanicFmt)]
struct Foo<'a> {
x: u32,
y: &'a [u8],
}
```
### Basic enum
```rust
use const_panic::{FmtArg, PanicFmt};
assert_eq!(const_panic::concat_!(Foo::Bar), "Bar");
assert_eq!(
const_panic::concat_!(Foo::Baz("hello", true)),
"Baz(\"hello\", true)",
);
#[derive(PanicFmt)]
enum Foo {
Bar,
Baz(&'static str, bool),
}
```
<a id = "type-parameter-example"></a>
### Type parameters
This example demonstrates support for types with type parameters.
```rust
use const_panic::{FmtArg, PanicFmt};
use std::marker::PhantomData;
{
const WITH_INT: Foo<&str, u8> = Foo {
value: 100u8,
_marker: PhantomData,
};
assert_eq!(
const_panic::concat_!(WITH_INT),
"Foo { value: 100, _marker: PhantomData }",
);
}
{
const WITH_STR: Foo<bool, &str> = Foo {
value: "hello",
_marker: PhantomData,
};
assert_eq!(
const_panic::concat_!(WITH_STR),
r#"Foo { value: "hello", _marker: PhantomData }"#,
);
}
#[derive(Debug, PanicFmt)]
// Tells the derive that the `A` type parameter is not formatted,
// removing the `A: PanicFmt` bound in `impl<A, B> PanicFmt for Foo<A, B>`,
// and using `()` as the `A` type parmeter for
// `<Foo<....> as PanicFmt>::PV_COUNT` in the generated `to_panicvals` method.
#[pfmt(ignore(A))]
// Defines a `to_panicvals` method for `Foo<A, u8>`
#[pfmt(impl<A> Foo<A, u8>)]
// Defines a `to_panicvals` method for `Foo<A, &str>`
#[pfmt(impl<A> Foo<A, &str>)]
pub struct Foo<A, B> {
value: B,
_marker: PhantomData<A>,
}
```
<a id = "phantom-type-parameter-example"></a>
### Phantom Type parameters
This example demonstrates how type parameters can be ignored with
`#[pfmt(ignore(...))]`.
```rust
use const_panic::{FmtArg, PanicFmt};
use std::marker::PhantomData;
{
const WITH_INT: Foo<u8, bool, 100> = Foo{
value: 5,
_marker: PhantomData,
};
assert_eq!(
const_panic::concat_!(WITH_INT),
"Foo { value: 5, _marker: PhantomData }",
);
}
{
const WITH_STR: Foo<str, char, 200> = Foo {
value: 8,
_marker: PhantomData,
};
assert_eq!(
const_panic::concat_!(WITH_STR),
r#"Foo { value: 8, _marker: PhantomData }"#,
);
}
#[derive(Debug, PanicFmt)]
// Tells the derive that the `A` and `B` type parameters are not formatted,
// removing the `A: PanicFmt` and `B: PanicFmt` bounds in the `PanicFmt` impl for `Foo`,
// and using `()` and `u8` as the `A` and `B` type parameters for
// `<Foo<(), u8> as PanicFmt>::PV_COUNT` in the generated `to_panicvals` method.
#[pfmt(ignore(A, B = u8))]
pub struct Foo<A: ?Sized, B, const X: u32> {
value: u32,
_marker: PhantomData<(PhantomData<A>, B)>,
}
```
### Const-generic struct
```rust
use const_panic::{FmtArg, PanicFmt};
assert_eq!(const_panic::concat_!(Foo([])), "Foo([])");
assert_eq!(const_panic::concat_!(Foo([3, 5, 8])), "Foo([3, 5, 8])");
#[derive(PanicFmt)]
struct Foo<const LEN: usize>([u8; LEN]);
```
<a id = "display_fmt-example"></a>
### Display formatting
This example demonstrates the [`display_fmt` attribute](#display_fmt-attr)
```rust
use const_panic::{FmtArg, PanicFmt, PanicVal};
assert_eq!(const_panic::concat_!(debug: Foo([3, 5, 8])), "Foo([3, 5, 8])");
assert_eq!(const_panic::concat_!(display: Foo([3, 5, 8])), "3 5 8");
#[derive(PanicFmt)]
#[pfmt(display_fmt = Self::display_fmt)]
// need this attribute to output more PanicVals in Display formatting than in Debug formatting.
#[pfmt(panicvals_lower_bound = 10)]
struct Foo([u8; 3]);
impl Foo {
const fn display_fmt(&self, fmtarg: FmtArg) -> [PanicVal<'_>; Foo::PV_COUNT] {
let [a, b, c] = self.0;
const_panic::flatten_panicvals!{fmtarg, Foo::PV_COUNT;
a, " ", b, " ", c
}
}
}
```
<a id = "crate-example"></a>
### Crate renaming
This example demonstrates how the `const_panic` crate can be renamed,
passing the new name to the derive macro.
```rust
# extern crate const_panic as cpanic;
# extern crate std as const_panic;
#
use cpanic::{FmtArg, PanicFmt};;
assert_eq!(cpanic::concat_!(Foo(Some(13))), "Foo(Some(13))");
#[derive(PanicFmt)]
#[pfmt(crate = cpanic)]
struct Foo(Option<u32>);
```
*/
pub use PanicFmt;