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
//! Make your Rust types readable in GDB and LLDB with one derive.
//!
//! ```
//! use debuggable::Debuggable;
//!
//! #[derive(Debuggable)]
//! #[debuggable(summary = "{len}/{cap}")]
//! pub struct Buffer {
//! #[debuggable(items, len = "len")]
//! data: Vec<u32>,
//! len: usize,
//! cap: usize,
//! }
//! ```
//!
//! In a debugger, a `Buffer` then reads `3/8 = {len = 3, cap = 8, [0] = 1, [1] = 2, [2] = 3}`
//! instead of its raw representation.
//!
//! To see it, set up your debugger once with
//! [`cargo-debuggable`](https://crates.io/crates/cargo-debuggable):
//! `cargo install cargo-debuggable && cargo debuggable setup`. Nothing runs inside your
//! program: the derive only embeds a description of the type for the debugger to read.
//!
//! # Attributes
//!
//! All attributes are optional. A bare `#[derive(Debuggable)]` hides `PhantomData` fields
//! and, for enums, shows just the variant name when the variant has no fields.
//!
//! ## `summary = "..."`: one-line summary
//!
//! On a struct, or on an enum variant. `{name}` inserts a field, `{0}` a tuple field, `{#}`
//! the number of elements (on types with `items`), and `{{` and `}}` are literal braces.
//! Summaries may use hidden fields. Each inserted field
//! shows the debugger's own one-line rendering of it, so a field that is itself
//! `Debuggable` shows its summary.
//!
//! ```
//! # use debuggable::Debuggable;
//! #[derive(Debuggable)]
//! #[debuggable(summary = "({x}, {y})")]
//! pub struct Point { pub x: i32, pub y: i32 }
//!
//! #[derive(Debuggable)]
//! #[debuggable(summary = "{0} m")]
//! pub struct Meters(pub f64);
//!
//! #[derive(Debuggable)]
//! pub enum Shape {
//! #[debuggable(summary = "circle r={radius} at {center}")]
//! Circle { center: Point, radius: Meters },
//! Empty,
//! }
//! ```
//!
//! A circle then shows as `circle r=2.5 m at (1, 2)`. On an enum, put `summary` on each
//! variant; variants without one show their name.
//!
//! ## `hide`: leave a field out
//!
//! ```
//! # use debuggable::Debuggable;
//! #[derive(Debuggable)]
//! pub struct Cache {
//! pub hits: u64,
//! #[debuggable(hide)]
//! generation: u32,
//! _marker: std::marker::PhantomData<*const ()>, // hidden automatically
//! }
//! ```
//!
//! ## `rename = "..."`: show a field under another name
//!
//! ```
//! # use debuggable::Debuggable;
//! #[derive(Debuggable)]
//! pub struct Account {
//! #[debuggable(rename = "balance_pence")]
//! balance: i64,
//! }
//! ```
//!
//! ## `items` and `len = "field"`: show a collection's elements
//!
//! On one field per struct. The field's elements are shown as `[0]`, `[1]`, ... after the
//! other fields, in place of the field itself.
//!
//! - On a `Vec<T>`: shows its elements. With `len`, at most that many.
//! - On an array `[T; N]` stored in the struct (an inline buffer): shows its elements. With
//! `len`, at most that many.
//! - On a `*const T`, `*mut T` or `NonNull<T>`: `len` is required and gives the count.
//!
//! Elements of type `MaybeUninit<T>` or `ManuallyDrop<T>` are shown as `T`. Only the first
//! `len` of them are read, so give `len` whenever some slots may be uninitialized.
//!
//! ```
//! # use debuggable::Debuggable;
//! # use std::ptr::NonNull;
//! #[derive(Debuggable)]
//! #[debuggable(summary = "{len} items")]
//! pub struct Stack<T> {
//! #[debuggable(items, len = "len")]
//! slots: Vec<Option<T>>, // only the first `len` slots are in use
//! #[debuggable(hide)]
//! len: usize,
//! }
//!
//! #[derive(Debuggable)]
//! pub struct RawParts {
//! #[debuggable(items, len = "len")]
//! ptr: NonNull<u16>,
//! len: usize,
//! }
//!
//! #[derive(Debuggable)]
//! pub struct Inline<T, const N: usize> {
//! #[debuggable(items, len = "len")]
//! buf: [std::mem::MaybeUninit<T>; N],
//! len: usize,
//! }
//! ```
//!
//! At most 10,000 elements are shown.
//!
//! ## `only` and `value`: slot collections
//!
//! Options for `items` on collections whose slots may be vacant, such as slab, arena or slot
//! map types. `only` keeps the elements that match, and `value` shows a field of each kept
//! element instead of the whole slot. Kept elements keep their index as their label, so
//! `[3]` is still slot 3.
//!
//! - `only = "Variant"`: elements that are enums, in that variant.
//! - `only = "field"` or `only = "field & mask"`: elements whose integer or `bool` field is
//! non-zero, or has a bit of `mask` set. The path may go through fields: `"meta.flags & 0x4"`.
//! - `value = "path"`: a field of the element, or of the variant chosen by `only`; through
//! unions too. `MaybeUninit` and `ManuallyDrop` are unwrapped.
//!
//! ```
//! # use debuggable::Debuggable;
//! # use std::mem::ManuallyDrop;
//! enum Entry<T> { Vacant(usize), Occupied(T) }
//!
//! #[derive(Debuggable)]
//! #[debuggable(summary = "{len} items")]
//! pub struct Slab<T> {
//! #[debuggable(items, only = "Occupied", value = "0")]
//! entries: Vec<Entry<T>>,
//! #[debuggable(hide)]
//! len: usize,
//! }
//!
//! union SlotUnion<T> { value: ManuallyDrop<T>, next_free: u32 }
//! struct Slot<T> { u: SlotUnion<T>, version: u32 } // odd version = occupied
//!
//! #[derive(Debuggable)]
//! #[debuggable(summary = "{num_elems} items")]
//! pub struct SlotMap<V> {
//! #[debuggable(items, only = "version & 1", value = "u.value")]
//! slots: Vec<Slot<V>>,
//! #[debuggable(hide)]
//! num_elems: u32,
//! }
//! ```
//!
//! A slab holding "a" in slot 1 and "d" in slot 3 then shows as
//! `2 items = {[1] = "a", [3] = "d"}`. Names inside the element type can't be checked at
//! compile time; a misspelled variant shows `<only: no variant ...>` in the debugger.
//!
//! ## `items = "path"` on the type: inline-or-heap collections
//!
//! For types whose elements live in one of several places, such as small-vector types that
//! move from an inline buffer to the heap. On the struct or enum, each
//! `#[debuggable(items = "path", len = "path")]` names one place the elements can be. The
//! first one whose paths exist in the current value is shown. A path segment can name an enum
//! variant, and then only leads somewhere while that variant is active. Paths start at the
//! type: on an enum, at a variant.
//!
//! ```
//! # use debuggable::Debuggable;
//! # use std::{marker::PhantomData, mem::MaybeUninit, ptr::NonNull};
//! # pub trait Array { type Item; }
//! # impl<T, const N: usize> Array for [T; N] { type Item = T; }
//! enum SmallVecData<A: Array> {
//! Inline(MaybeUninit<A>),
//! Heap { ptr: NonNull<A::Item>, len: usize },
//! }
//!
//! #[derive(Debuggable)]
//! #[debuggable(summary = "{#} items")]
//! #[debuggable(items = "data.Inline.0", len = "capacity")] // inline: `capacity` is the length
//! #[debuggable(items = "data.Heap.ptr", len = "data.Heap.len")]
//! pub struct SmallVec<A: Array> {
//! #[debuggable(hide)]
//! capacity: usize,
//! #[debuggable(hide)]
//! data: SmallVecData<A>,
//! _marker: PhantomData<A::Item>,
//! }
//!
//! #[derive(Debuggable)]
//! #[debuggable(items = "Inline.0", len = "Inline.1")]
//! #[debuggable(items = "Heap.0")]
//! pub enum TinyVec<T> {
//! #[debuggable(summary = "{#} items (inline)")]
//! Inline(#[debuggable(hide)] [T; 4], #[debuggable(hide)] u16),
//! #[debuggable(summary = "{#} items")]
//! Heap(#[debuggable(hide)] Vec<T>),
//! }
//! ```
//!
//! Either way, three elements show as `3 items = {[0] = 1, [1] = 2, [2] = 3}`. The first
//! segment of each path is checked at compile time; deeper ones can't be, and if no place
//! matches, the debugger shows `<items: no alternative matched>`.
//!
//! ## `text`: show bytes as a string
//!
//! On any number of fields. The field's bytes are shown as a string literal, as UTF-8:
//! `"hello"`. Escapes follow Rust's `{:?}` for strings (`\n`, `\"`, `\0`), and bytes that
//! aren't valid UTF-8 show as `\xNN`, so nothing is lost. Takes the same sources as `items`,
//! with elements of one byte: `[u8; N]`, `[MaybeUninit<u8>; N]`, `Vec<u8>`, or a pointer with
//! `len`. With `hide`, the field gets no row of its own but a summary can still show it.
//!
//! ```
//! # use debuggable::Debuggable;
//! # use std::mem::MaybeUninit;
//! #[derive(Debuggable)]
//! #[debuggable(summary = "{buf}")]
//! pub struct InlineString<const N: usize> {
//! #[debuggable(text, len = "len", hide)]
//! buf: [MaybeUninit<u8>; N],
//! #[debuggable(hide)]
//! len: u8,
//! }
//! ```
//!
//! An `InlineString` holding "hello" then shows as just `"hello"`, like a `String`. At most
//! 1,024 bytes are read; LLDB leaves out trailing NUL bytes.
//!
//! # Compile errors
//!
//! Every mistake is a compile error pointing at the attribute, with suggestions for typos:
//!
//! ```compile_fail
//! # use debuggable::Debuggable;
//! #[derive(Debuggable)]
//! #[debuggable(summary = "{lenght} items")] // did you mean `length`?
//! pub struct Stack { length: usize }
//! ```
//!
//! # Generics, enums, and dependencies
//!
//! Generic types work for every instantiation. Enums work with any layout, including
//! niche-optimised ones (for example, an enum whose data variant holds a `String`). Types from
//! dependency crates work in the final binary like your own.
//!
//! # Cost, and opting out
//!
//! About 300 bytes per derived type in the final binary, plus a 5 KB GDB runtime once per
//! Linux binary: data in a section that only debuggers read. On Windows nothing is emitted
//! (not supported yet). Build with `RUSTFLAGS="--cfg debuggable_disable"` to emit nothing
//! anywhere.
//!
//! # Limitations
//!
//! - Types defined inside function bodies are not matched by the debugger
//! (`cargo debuggable doctor <binary>` points them out).
//! - Summaries refer to fields by name only: no expressions or format specs.
//! - Windows (Natvis) is not supported yet.
//!
//! See the [repository](https://github.com/tommantonclery/debuggable) for the compatibility table and
//! troubleshooting.
/// Derives debugger visualizers for a struct or an enum. See the [crate docs](crate).
pub use Debuggable;