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
//! Error-kind numbering for the `un*` family: the values that are already shared, and
//! the bands that keep future ones from colliding.
//!
//! # What is here, and what deliberately is not
//!
//! Each library owns its own `ErrorKind` enum, because the reasons a PDF fails to parse
//! are not the reasons a spreadsheet does. What they share is the *numbering* those enums
//! live in, since the discriminants cross the C ABI as plain integers.
//!
//! This module therefore exports **only values that are already identical across the
//! family**. It does not offer a broad set of "common kinds", and that restraint is the
//! whole design: a library adopting a constant whose value differs from what it currently
//! ships would be renumbering its public ABI. Above [`UNKNOWN_FORMAT`] the family already
//! disagrees — the low band was assigned independently before anyone thought to align it —
//! so any value published here would be a renumbering request aimed at somebody.
//!
//! For everything else, this module offers **bands** rather than values: a range each
//! library allocates from, so that a kind added next year does not land on a number that
//! already means something elsewhere.
//!
//! # Bands
//!
//! | Range | Meaning |
//! |---|---|
//! | `0` | Success. Never a valid kind — see [`NONE`] |
//! | `1..=17` | Assigned before the convention existed. Frozen where shipped; see below |
//! | [`COMMON_BAND`] | New reasons that genuinely apply to more than one library |
//! | [`BOUNDARY_BAND`] | Failures of the ABI call itself, with no library-side counterpart |
//! | `200..` | One band per library, [`LIBRARY_BAND_SIZE`] wide. See [`library_band`] |
//!
//! The `1..=17` range is history, not design. Numbers in it are frozen for any library
//! that has shipped them, and a library that has *not* shipped a given number should not
//! assume it is free to mean something new — a consumer reading two of these libraries
//! sees one integer space. Add new reasons from [`COMMON_BAND`] or a library band.
//!
//! # Where a failure is attributed
//!
//! Two libraries classified the same failure differently — serialising a rendered result
//! to JSON — because nothing said where it belonged. A shared numbering is only useful if
//! the same failure lands on the same number, so the rule is stated here rather than
//! rediscovered:
//!
//! **Failing to serialise a rendered result is a rendering failure.** Producing output is
//! rendering, and it stays rendering when the last step of producing it is serialisation.
//! It is not a generic failure, and it is not a boundary failure — nothing is wrong with
//! how the call was made.
//!
//! A library with no rendering reason of its own takes one from its own band rather than
//! reaching for [`OTHER`], which means "this failure carries no classification" and is
//! worth keeping true.
//!
//! # The contract consumers are promised
//!
//! Every library in the family documents the same four rules. They are restated here
//! because a shared numbering is only worth having if the rules are shared too:
//!
//! 1. A new reason takes a new number. Existing numbers are never reused or renumbered.
//! 2. An unrecognised value is not an error — treat it as a generic failure and keep the
//! number. A newer library stays usable by an older caller precisely because of this.
//! 3. `0` means success, never a reason.
//! 4. Reasons are `#[non_exhaustive]`: match with a `_ =>` arm.
use c_int;
use RangeInclusive;
/// No error is recorded: the last call on this thread succeeded.
///
/// Not a valid kind. Zero is reserved for this so that a zeroed struct or an
/// uninitialised `int` cannot be mistaken for a classified failure.
pub const NONE: c_int = 0;
/// A failure with no more specific classification.
///
/// Exists so that bindings have a value for a failure that carries no classification —
/// one raised by the binding itself, say. It must never be confused with success.
///
/// Shared across the family; adopting it is a no-op for libraries that already use `1`.
pub const OTHER: c_int = 1;
/// An I/O failure: a missing file, an unreadable one, a short read.
///
/// Shared across the family.
pub const IO: c_int = 2;
/// The input is not a document this library recognises at all.
///
/// Distinct from "recognised but unsupported", which is a library-specific reason.
///
/// Shared across the family.
pub const UNKNOWN_FORMAT: c_int = 3;
/// Reasons that apply to more than one library and were assigned after the convention.
///
/// A reason belongs here only once **two or more** libraries expose it. One library's
/// need is served by its own band — a shared numbering earns its keep by describing what
/// is actually shared, not by anticipating.
pub const COMMON_BAND: = 18..=99;
/// Reasons that describe a failure of the ABI call itself.
///
/// These have no library-side counterpart: nothing went wrong with the document, the
/// call was made wrongly or could not deliver its result. The three values in this band
/// are the ones the family already agrees on.
pub const BOUNDARY_BAND: = 100..=199;
/// An argument was null, or a string argument was not valid UTF-8.
pub const INVALID_ARGUMENT: c_int = 100;
/// A panic was caught at the ABI boundary.
///
/// Unwinding across `extern "C"` is undefined behaviour, so every entry point catches.
/// Reaching this value means a bug in the library, not in the caller's input.
pub const PANIC: c_int = 101;
/// The result could not cross the ABI: a string holding an interior NUL byte.
///
/// The output exists and is correct as far as the library is concerned; it simply cannot
/// be represented as a C string. Reported rather than truncated, because silently
/// returning the text up to the NUL loses the rest without saying so.
pub const INVALID_OUTPUT: c_int = 102;
/// Width of each library's private band.
pub const LIBRARY_BAND_SIZE: c_int = 100;
/// First number available to library bands.
pub const FIRST_LIBRARY_BAND: c_int = 200;
/// The band belonging to library `index`, counting from zero.
///
/// Bands are handed out in a fixed order so that a number identifies which library
/// produced it. The current allocation:
///
/// | Index | Library | Band |
/// |---|---|---|
/// | 0 | `unpdf` | `200..=299` |
/// | 1 | `undoc` | `300..=399` |
/// | 2 | `unhwp` | `400..=499` |
///
/// ```
/// # use uncore::kind;
/// assert_eq!(kind::library_band(2), 400..=499);
/// ```
pub const
/// Whether `kind` names a failure at the ABI boundary rather than in the document.
///
/// Useful to a binding that wants to report "you called this wrongly" differently from
/// "your document is broken".
///
/// ```
/// # use uncore::kind;
/// assert!(kind::is_boundary(kind::PANIC));
/// assert!(!kind::is_boundary(kind::IO));
/// assert!(!kind::is_boundary(kind::NONE));
/// ```