Skip to main content

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}