Skip to main content

prebindgen_registry/unfold/
error.rs

1//! Errors produced while resolving output-deconstruction declarations.
2
3/// Errors surfaced while resolving
4/// [`Deconstructors`](super::Deconstructors).
5#[derive(Debug)]
6pub enum UnfoldError {
7    UnknownFunction(syn::Ident),
8    UnknownAccessor(syn::Ident),
9    NoDeconstructor {
10        func: syn::Ident,
11        target: String,
12    },
13    AccessorTargetMismatch {
14        accessor: String,
15        takes: String,
16        expected: String,
17    },
18    MultipleIdentity {
19        target: String,
20    },
21    /// A nested deconstructor recurses back into a type already on the nesting
22    /// chain (`A → … → A`).
23    Cycle {
24        target: String,
25    },
26    /// A single-value (`Return`) delivery on a decomposition that does not
27    /// flatten to exactly one leaf, or whose shape is `Iterable`.
28    ConvertNotSingle {
29        func: syn::Ident,
30        reason: &'static str,
31    },
32    /// A decomposer record references a function that was not declared via
33    /// `.accessor`.
34    RecordNotAccessor {
35        func: syn::Ident,
36    },
37    /// A shape / record kind not yet implemented.
38    Unsupported {
39        func: syn::Ident,
40        reason: &'static str,
41    },
42    /// Two leaves of one deconstructor resolved to the same (literal) name.
43    /// Author leaf names are explicit and emitted verbatim, so a collision is a
44    /// declaration bug — never auto-resolved.
45    DuplicateLeafName {
46        target: String,
47        name: String,
48    },
49    /// An author-supplied leaf name contains the reserved `"__"` chain
50    /// separator (used internally to join nested deconstructor segments).
51    ReservedSeparator {
52        name: String,
53    },
54    /// An owned decomposition declared `.field_self()` (the root identity,
55    /// which MOVES the value) before a field that splices a nested identity
56    /// (which borrows it) — the generated Rust would not compile.
57    RootIdentityBeforeNested {
58        target: String,
59    },
60    /// A per-fn `.expand_return(expand_return!(T)…)` decl whose `T` does not
61    /// match the function's peeled return type.
62    ReturnTypeMismatch {
63        func: syn::Ident,
64        declared: String,
65        actual: String,
66    },
67    /// Structurally invalid declaration records — empty record lists or
68    /// duplicate targets. All offenders are collected before failing
69    /// (mirrors `ScanError::DeclaredNotFound`).
70    InvalidDeclarations {
71        entries: Vec<UnfoldDeclError>,
72    },
73}
74
75/// One structurally invalid output-expansion declaration (see
76/// [`UnfoldError::InvalidDeclarations`]). Note that EMPTY record lists are
77/// deliberately not diagnosed here — an empty inline list is the valid
78/// whole-element (`Vec<T>` per-element) delivery form.
79#[derive(Debug)]
80pub enum UnfoldDeclError {
81    /// Two deconstructor declarations for the same target type.
82    DuplicateDeconstructor { target: String },
83    /// Two per-fn output expansions for the same fn and position.
84    DuplicateOutput {
85        func: syn::Ident,
86        target: super::DeconTarget,
87    },
88}
89
90impl std::fmt::Display for UnfoldDeclError {
91    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92        match self {
93            UnfoldDeclError::DuplicateDeconstructor { target } => {
94                write!(f, "duplicate deconstructor declaration for `{target}`")
95            }
96            UnfoldDeclError::DuplicateOutput { func, target } => write!(
97                f,
98                "duplicate output expansion for `{func}` ({target:?} position)"
99            ),
100        }
101    }
102}
103
104impl std::fmt::Display for UnfoldError {
105    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106        match self {
107            UnfoldError::UnknownFunction(name) => write!(
108                f,
109                "output expansion: function `{}` is not a #[prebindgen] item",
110                name
111            ),
112            UnfoldError::UnknownAccessor(name) => write!(
113                f,
114                "output expansion: accessor `{}` is not a #[prebindgen] item",
115                name
116            ),
117            UnfoldError::ReturnTypeMismatch {
118                func,
119                declared,
120                actual,
121            } => write!(
122                f,
123                "output expansion: `{}`.expand_return(expand_return!({declared})): the \
124                 function's return type is `{actual}`, not `{declared}` — declare the decl \
125                 for the actual return type",
126                func
127            ),
128            UnfoldError::NoDeconstructor { func, target } => write!(
129                f,
130                "output expansion: no deconstructor registered for `{}` (return of `{}`)",
131                target, func
132            ),
133            UnfoldError::AccessorTargetMismatch {
134                accessor,
135                takes,
136                expected,
137            } => write!(
138                f,
139                "output expansion: accessor `{}` takes `{}` but the deconstructor decomposes `{}`",
140                accessor, takes, expected
141            ),
142            UnfoldError::MultipleIdentity { target } => write!(
143                f,
144                "output expansion: deconstructor for `{}` has more than one identity record",
145                target
146            ),
147            UnfoldError::Cycle { target } => write!(
148                f,
149                "output expansion: nested deconstructors form a cycle through `{}`",
150                target
151            ),
152            UnfoldError::ConvertNotSingle { func, reason } => write!(
153                f,
154                "convert_output: `{}` is not a single-value deconstructor: {}",
155                func, reason
156            ),
157            UnfoldError::RecordNotAccessor { func } => write!(
158                f,
159                "deconstructor record `{}` is not a `.fun_accessor` — decomposer records may only \
160                 reference functions declared via `.fun_accessor(...)`",
161                func
162            ),
163            UnfoldError::Unsupported { func, reason } => write!(
164                f,
165                "output expansion: `{}` not yet supported: {}",
166                func, reason
167            ),
168            UnfoldError::DuplicateLeafName { target, name } => write!(
169                f,
170                "deconstructor for `{}` has two output records named `{}` — leaf names must be \
171                 unique (they are emitted literally)",
172                target, name
173            ),
174            UnfoldError::ReservedSeparator { name } => write!(
175                f,
176                "output record name `{}` contains the reserved `__` separator (used to join \
177                 nested deconstructor segments)",
178                name
179            ),
180            UnfoldError::RootIdentityBeforeNested { target } => write!(
181                f,
182                "return-field list of `{}`: `.field_self()` (the root identity) must be \
183                 declared AFTER fields that splice a nested identity — the root identity moves the owned \
184                 value while nested identities borrow it, so this order would generate \
185                 non-compiling Rust. Declare the `_self` field last.",
186                target
187            ),
188            UnfoldError::InvalidDeclarations { entries } => {
189                writeln!(f, "output expansion: invalid declarations:")?;
190                for e in entries {
191                    writeln!(f, "  - {e}")?;
192                }
193                Ok(())
194            }
195        }
196    }
197}
198
199impl std::error::Error for UnfoldError {}