aube_codes/lib.rs
1//! Stable identifiers for every error and warning that aube emits.
2//!
3//! The crate is dependency-free on purpose: every other aube crate may
4//! depend on it. Codes are exposed as `pub const &str` so they can be
5//! used unmodified in `tracing::warn!(code = aube_codes::warnings::X, ...)`,
6//! `#[diagnostic(code = aube_codes::errors::Y)]`, and ndjson-emitting
7//! reporters without needing to call `.as_str()` or do any conversion.
8//!
9//! Naming convention:
10//! - `ERR_AUBE_*` for errors (anything that returns `Err` to the caller
11//! or aborts with a non-zero exit).
12//! - `WARN_AUBE_*` for warnings (`tracing::warn!`) and non-fatal
13//! `tracing::error!` sites that don't change exit status.
14//!
15//! aube does not emit `ERR_PNPM_*` codes itself. Where a code maps
16//! cleanly onto a pnpm concept (lockfile, peer-deps, tarball, etc.) we
17//! reuse pnpm's *suffix* under the `ERR_AUBE_` prefix so the code reads
18//! the same to anyone familiar with pnpm — but the published code is
19//! always `ERR_AUBE_*`.
20//!
21//! Codes are stable: once published, a code's identifier and meaning
22//! must not change. Adding new codes is fine; removing or repurposing
23//! one is a breaking change.
24
25#![forbid(unsafe_code)]
26
27pub mod errors;
28pub mod exit;
29pub mod warnings;
30
31/// Metadata for a single error or warning code.
32///
33/// `name` doubles as the emitted string value — every code is
34/// declared as `pub const X: &str = "X"` so this field references the
35/// same constant the call site uses. Keeping the const + the
36/// `CodeMeta` entry pointing at the same identifier lets a rename
37/// flow through both with no drift.
38///
39/// `description` and `category` feed the generated docs page
40/// (`docs/error-codes.data.json`, consumed by `<ErrorCodesTable>`).
41/// `exit_code` is `Some(_)` only for errors that have a bespoke
42/// entry — warnings always set `None` because they don't change
43/// exit status.
44///
45/// `Serialize` is derived so the generator binary can emit each
46/// entry verbatim via `serde_json`. Every consuming crate already
47/// has `serde` in its dep tree; adding it here doesn't grow the
48/// compile graph.
49#[derive(Debug, serde::Serialize)]
50pub struct CodeMeta {
51 pub name: &'static str,
52 pub category: &'static str,
53 pub description: &'static str,
54 pub exit_code: Option<i32>,
55}
56
57#[cfg(test)]
58mod tests {
59 use super::*;
60
61 #[test]
62 fn every_error_const_value_matches_its_name() {
63 // The `pub const ERR_AUBE_X: &str = "ERR_AUBE_X"` shape is
64 // load-bearing — typos between the const name and the value
65 // would silently emit the wrong code. CodeMeta::name is the
66 // const value, so checking the prefix on every entry catches
67 // any rogue addition that didn't follow the convention.
68 for meta in errors::ALL {
69 assert!(
70 meta.name.starts_with("ERR_AUBE_"),
71 "error codes must use the ERR_AUBE_ prefix: {}",
72 meta.name
73 );
74 assert!(
75 !meta.description.is_empty(),
76 "error code {} is missing a description",
77 meta.name
78 );
79 assert!(
80 !meta.category.is_empty(),
81 "error code {} is missing a category",
82 meta.name
83 );
84 }
85 }
86
87 #[test]
88 fn every_warning_const_value_matches_its_name() {
89 for meta in warnings::ALL {
90 assert!(
91 meta.name.starts_with("WARN_AUBE_"),
92 "warning codes must use the WARN_AUBE_ prefix: {}",
93 meta.name
94 );
95 assert!(
96 !meta.description.is_empty(),
97 "warning code {} is missing a description",
98 meta.name
99 );
100 assert!(
101 !meta.category.is_empty(),
102 "warning code {} is missing a category",
103 meta.name
104 );
105 assert!(
106 meta.exit_code.is_none(),
107 "warning {} has an exit_code; warnings don't change exit status",
108 meta.name
109 );
110 }
111 }
112
113 #[test]
114 fn no_duplicate_codes() {
115 use std::collections::HashSet;
116 let all: Vec<&str> = errors::ALL
117 .iter()
118 .chain(warnings::ALL.iter())
119 .map(|m| m.name)
120 .collect();
121 let unique: HashSet<&str> = all.iter().copied().collect();
122 assert_eq!(
123 all.len(),
124 unique.len(),
125 "duplicate code identifier across errors/warnings"
126 );
127 }
128}