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
//! Runtime glue for `#[derive(DjogiEnum)]`.
//! Most of the codec logic (ToSql / FromSql impls) is generated per-enum by the proc macro.
//! This module holds shared error types and re-exports that complete the runtime surface.
//! # Design
//! A Postgres enum column round-trips as a string on the wire. `#[derive(DjogiEnum)]`
//! generates:
//! 1. `ToSql` — encodes `self` as the mapped string label.
//! 2. `FromSql` — decodes a wire string, matches against known variants, returns
//! `Err(EnumDecodeError::UnknownVariant { ... })` for unrecognised labels.
//! 3. `inventory::submit!(EnumDescriptor { ... })` — registers the enum's metadata so
//! the migration differ can emit `CREATE TYPE ... AS ENUM (...)`.
//! 4. A `variants()` convenience fn returning the mapped string slice.
/// Decode failed: the Postgres wire string did not match any known variant.
/// Returned (boxed) from `FromSql::from_sql` when the wire bytes decode to a string that
/// is not in the enum's variant map. The `postgres_type` field names the Postgres enum
/// type (e.g. `"vehicle_status"`) so error messages identify the column clearly.
#[derive(Debug)]
pub struct EnumDecodeError {
/// Postgres type name — matches `EnumDescriptor::postgres_type`.
pub postgres_type: &'static str,
/// The wire string that did not match any variant.
pub received: String,
/// The variants the decoder expected — for human-readable error messages.
pub expected: &'static [&'static str],
}
impl std::fmt::Display for EnumDecodeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"unknown variant `{}` for Postgres enum `{}`; expected one of: {}",
self.received,
self.postgres_type,
self.expected.join(", ")
)
}
}
impl std::error::Error for EnumDecodeError {}
#[cfg(test)]
mod tests {
use super::EnumDecodeError;
#[test]
fn decode_error_display() {
let err = EnumDecodeError {
postgres_type: "vehicle_status",
received: "unknown_val".to_owned(),
expected: &["active", "in_maintenance", "decommissioned"],
};
let msg = err.to_string();
assert!(
msg.contains("unknown_val"),
"error message must include the received value"
);
assert!(
msg.contains("vehicle_status"),
"error message must include the postgres type name"
);
assert!(
msg.contains("active"),
"error message must include expected variants"
);
}
}