prebindgen_registry/registry/view.rs
1//! What a conversion is built against — the partial view during the fill, and
2//! the total one after it.
3
4use std::collections::HashMap;
5
6use prebindgen_flat::flat::{Flat, TypeRef};
7
8use super::*;
9use crate::unfold::{DeconId, DeconSpec, UnfoldPlan};
10
11/// One `(direction, type)` pair that crosses the boundary.
12///
13/// Direction is part of the identity, not a separate axis: `&str` inbound
14/// decodes a `jstring` and outbound allocates one, and one may be convertible
15/// while the other is not.
16pub type Crossing = (Direction, TypeKey);
17
18/// What a conversion is built against: the model, and the conversions already
19/// available.
20///
21/// Two implementors, and the reason there are two is the fill phase.
22/// [`Building`] is the partial view a generator sees while it is still
23/// producing conversions; [`Registry`] is the total one everything else sees.
24/// A helper that serves both — reading a signature off the model, say — takes
25/// `&impl Conversions<M>` and works either side of the boundary.
26pub trait Conversions<M> {
27 /// The model.
28 fn flat(&self) -> &Flat;
29
30 /// The reading for `ty` — what the frontend made of it.
31 ///
32 /// On the trait because it is needed on **both** sides of the fill: a
33 /// converter is chosen for a type while the registry is still being built, and
34 /// an emitter asks about the same type afterwards. Both views answer from the
35 /// cell the scan filled, so the answer does not change across that line.
36 ///
37 /// This is what lets a generator take a crossing and reason about it without
38 /// rebuilding a spelling from the key and classifying that — the round trip
39 /// `api/core` removed from itself in #263, which is the same defect one layer
40 /// out.
41 ///
42 /// **Keyed**, because that is the one thing a caller has before it has a
43 /// reading. This is the door FROM identity TO the model's answer, and the
44 /// only lookup on this trait that does not already take a `TypeRef` — the
45 /// rest take one precisely because this exists to hand them one (#284).
46 fn reading(&self, key: &TypeKey) -> Option<TypeRef>;
47
48 /// The conversion for `reading` in `dir`, if there is one.
49 ///
50 /// Takes the **reading**, not a spelling. A caller that has to ask what a
51 /// type converts to has already established what the type *is*; asking with
52 /// tokens instead let a spelling nobody classified reach the table, and cost
53 /// a `TypeKey::from_type` on every call for an identity the reading already
54 /// carries.
55 fn conversion(&self, dir: Direction, reading: &TypeRef) -> Option<&TypeEntry<M>>;
56
57 /// The reading for a **spelling** — identify, then look up.
58 ///
59 /// The door for a caller holding tokens it peeled or composed itself, which
60 /// is a real position: an adapter may strip a `&` or name a wire type, and
61 /// #280 sealed minting so it cannot make a reading for the result. It asks
62 /// instead, and `None` means the registry never saw that type.
63 ///
64 /// Kept separate from the entry lookups on purpose. Those take a `TypeRef`,
65 /// so they cannot be called about a type the registry does not know — which
66 /// is the guarantee, and it survives only while getting a reading from
67 /// tokens is a visible step with a `None` to handle.
68 fn reading_of(&self, ty: &syn::Type) -> Option<TypeRef> {
69 self.reading(&TypeKey::from_type(ty))
70 }
71
72 /// Wire → rust.
73 fn input_entry(&self, reading: &TypeRef) -> Option<&TypeEntry<M>> {
74 self.conversion(Direction::Input, reading)
75 }
76
77 /// Rust → wire.
78 fn output_entry(&self, reading: &TypeRef) -> Option<&TypeEntry<M>> {
79 self.conversion(Direction::Output, reading)
80 }
81
82 /// The decomposition of a callback argument type, if it has one.
83 ///
84 /// On the trait because a callback converter needs it while being built,
85 /// and the emitter needs it again afterwards. Plans are applied by
86 /// `prepare`, so they are complete either side of that line.
87 fn callback_arg_plan(&self, key: &TypeKey) -> Option<&UnfoldPlan>;
88
89 /// Every callback-argument decomposition, for the emitters that enumerate
90 /// them rather than look one up.
91 fn callback_arg_plans(&self) -> &HashMap<TypeKey, UnfoldPlan>;
92
93 /// The return decomposition of a function, if it has one.
94 fn unfold_plans(&self) -> &HashMap<syn::Ident, UnfoldPlan>;
95
96 /// The error-position decomposition of a fallible function.
97 fn error_plans(&self) -> &HashMap<syn::Ident, UnfoldPlan>;
98
99 /// The declaration-default decomposition behind each deconstructor.
100 fn decon_plans(&self) -> &HashMap<DeconId, DeconSpec>;
101
102 /// Every type key that crosses in `dir`.
103 ///
104 /// The niche allocator needs the whole population, not one lookup: it picks
105 /// sentinel values no sibling conversion can produce.
106 fn crossing_keys(&self, dir: Direction) -> Vec<TypeKey>;
107
108 /// The origin crate's module path for an item, or `None` when unknown.
109 fn origin_module(&self, ident: &syn::Ident) -> Option<syn::Path> {
110 origin_module_of(self.flat(), ident)
111 }
112
113 /// The default module for references with no recorded origin.
114 fn default_module(&self) -> Option<syn::Path> {
115 default_module_of(self.flat())
116 }
117}
118
119impl<M> Conversions<M> for Building<'_, M> {
120 fn flat(&self) -> &Flat {
121 &self.registry.flat
122 }
123 fn reading(&self, key: &TypeKey) -> Option<TypeRef> {
124 self.registry.reading(key)
125 }
126 fn conversion(&self, dir: Direction, reading: &TypeRef) -> Option<&TypeEntry<M>> {
127 self.built.get(&(dir, reading.key()))
128 }
129 fn callback_arg_plan(&self, key: &TypeKey) -> Option<&UnfoldPlan> {
130 self.registry.callback_arg_plans.get(key)
131 }
132 fn callback_arg_plans(&self) -> &HashMap<TypeKey, UnfoldPlan> {
133 &self.registry.callback_arg_plans
134 }
135 fn unfold_plans(&self) -> &HashMap<syn::Ident, UnfoldPlan> {
136 &self.registry.unfold_plans
137 }
138 fn error_plans(&self) -> &HashMap<syn::Ident, UnfoldPlan> {
139 &self.registry.error_plans
140 }
141 fn decon_plans(&self) -> &HashMap<DeconId, DeconSpec> {
142 &self.registry.decon_plans
143 }
144 fn crossing_keys(&self, dir: Direction) -> Vec<TypeKey> {
145 self.all_keys
146 .iter()
147 .filter(|(d, _)| *d == dir)
148 .map(|(_, k)| k.clone())
149 .collect()
150 }
151}
152
153impl<M> Conversions<M> for Registry<M> {
154 fn flat(&self) -> &Flat {
155 &self.flat
156 }
157 fn reading(&self, key: &TypeKey) -> Option<TypeRef> {
158 Registry::reading(self, key)
159 }
160 fn conversion(&self, dir: Direction, reading: &TypeRef) -> Option<&TypeEntry<M>> {
161 self.type_table(dir).get(&reading.key())?.entry.as_ref()
162 }
163 fn callback_arg_plan(&self, key: &TypeKey) -> Option<&UnfoldPlan> {
164 self.callback_arg_plans.get(key)
165 }
166 fn callback_arg_plans(&self) -> &HashMap<TypeKey, UnfoldPlan> {
167 &self.callback_arg_plans
168 }
169 fn unfold_plans(&self) -> &HashMap<syn::Ident, UnfoldPlan> {
170 &self.unfold_plans
171 }
172 fn error_plans(&self) -> &HashMap<syn::Ident, UnfoldPlan> {
173 &self.error_plans
174 }
175 fn decon_plans(&self) -> &HashMap<DeconId, DeconSpec> {
176 &self.decon_plans
177 }
178 fn crossing_keys(&self, dir: Direction) -> Vec<TypeKey> {
179 self.type_table(dir).keys().cloned().collect()
180 }
181}
182
183/// The registry mid-fill: the model, plus the conversions supplied so far.
184///
185/// What a generator builds a conversion *against*. It sees every crossing it
186/// can compose from — `RegistryBuilder::crossings` hands them out inner-first, so by
187/// the time `Option<Handle>` is asked for, `Handle` is already in here.
188///
189/// It exposes exactly the reads a conversion needs, which is what keeps the
190/// half-filled state from leaking anywhere else: the resolved [`Registry`] is
191/// what the emitters get, and it is total.
192pub struct Building<'a, M> {
193 /// The prepared registry: model, decompositions and the full crossing
194 /// population. Its conversion cells are still empty — [`Self::conversion`]
195 /// deliberately reads [`Self::built`] instead, so a generator can only see
196 /// what it has actually produced.
197 registry: &'a Registry<M>,
198 built: &'a HashMap<Crossing, TypeEntry<M>>,
199 /// Every crossing in the binding, resolved or not — the niche allocator
200 /// reads the population, not just what is built so far.
201 all_keys: &'a [Crossing],
202}
203
204impl<'a, M> Building<'a, M> {
205 pub(crate) fn new(
206 registry: &'a Registry<M>,
207 built: &'a HashMap<Crossing, TypeEntry<M>>,
208 all_keys: &'a [Crossing],
209 ) -> Self {
210 Self {
211 registry,
212 built,
213 all_keys,
214 }
215 }
216}
217
218/// Shared by [`Registry::origin_module`] and [`Building::origin_module`], so the
219/// two cannot answer differently.
220pub(super) fn origin_module_of(flat: &Flat, ident: &syn::Ident) -> Option<syn::Path> {
221 let crate_name = flat.element(ident)?.location().crate_name.as_ref()?;
222 syn::parse_str(&crate_name.replace('-', "_")).ok()
223}
224
225pub(super) fn default_module_of(flat: &Flat) -> Option<syn::Path> {
226 flat.source_modules()
227 .first()
228 .and_then(|m| syn::parse_str(m).ok())
229}