hyperlight_component_util/emit.rs
1/*
2Copyright 2025 The Hyperlight Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15 */
16
17//! A bunch of utilities used by the actual code emit functions
18use std::collections::{BTreeMap, BTreeSet, VecDeque};
19use std::vec::Vec;
20
21use proc_macro2::TokenStream;
22use quote::{format_ident, quote};
23use syn::Ident;
24
25use crate::etypes::{BoundedTyvar, Defined, Handleable, ImportExport, TypeBound, Tyvar};
26
27/// A representation of a trait definition that we will eventually
28/// emit. This is used to allow easily adding onto the trait each time
29/// we see an extern decl.
30#[derive(Debug, Default)]
31pub struct Trait {
32 /// A set of supertrait constraints, each associated with a
33 /// bindings module path
34 pub supertraits: BTreeMap<Vec<Ident>, TokenStream>,
35 /// Keep track for each type variable of:
36 /// - The identifier that we use for it in the generated source
37 /// - Whether it comes from a component type variable, and if so,
38 /// which one. (Most do; the I: Imports on the main component
39 /// trait is the main one that doesn't).
40 /// - Whether there are any bounds on it
41 pub tvs: BTreeMap<Ident, (Option<u32>, TokenStream)>,
42 /// Raw tokens of the contents of the trait
43 pub items: TokenStream,
44}
45impl Trait {
46 pub fn new() -> Self {
47 Self {
48 supertraits: BTreeMap::new(),
49 tvs: BTreeMap::new(),
50 items: TokenStream::new(),
51 }
52 }
53 /// Collect the component tyvar indices that correspond to the
54 /// type variables on this trait.
55 ///
56 /// Precondition: all of the type
57 /// variables on this trait do correspond to component variables.
58 pub fn tv_idxs(&self) -> Vec<u32> {
59 self.tvs.iter().map(|(_, (n, _))| n.unwrap()).collect()
60 }
61 /// See [`State::adjust_vars`].
62 pub fn adjust_vars(&mut self, n: u32) {
63 for (_, (v, _)) in self.tvs.iter_mut() {
64 if let Some(v) = v.as_mut() {
65 *v += n;
66 }
67 }
68 }
69 /// Build a token stream of all type variables and trait bounds on
70 /// them, e.g. what you would put "inside" the <> in trait T<...>.
71 pub fn tv_toks_inner(&mut self) -> TokenStream {
72 let tvs = self
73 .tvs
74 .iter()
75 .map(|(k, (_, v))| {
76 let colon = if v.is_empty() {
77 quote! {}
78 } else {
79 quote! { : }
80 };
81 quote! { #k #colon #v }
82 })
83 .collect::<Vec<_>>();
84 quote! { #(#tvs),* }
85 }
86 /// Build a token stream for the type variable part of the trait
87 /// declaration
88 pub fn tv_toks(&mut self) -> TokenStream {
89 if !self.tvs.is_empty() {
90 let toks = self.tv_toks_inner();
91 quote! { <#toks> }
92 } else {
93 quote! {}
94 }
95 }
96 /// Build a token stream for this entire trait definition
97 pub fn into_tokens(&mut self, n: Ident) -> TokenStream {
98 let trait_colon = if !self.supertraits.is_empty() {
99 quote! { : }
100 } else {
101 quote! {}
102 };
103 let supertraits = self
104 .supertraits
105 .iter()
106 .map(|(is, ts)| {
107 quote! { #(#is)::*#ts }
108 })
109 .collect::<Vec<_>>();
110 let tvs = self.tv_toks();
111 let items = &self.items;
112 quote! {
113 pub trait #n #tvs #trait_colon #(#supertraits)+* { #items }
114 }
115 }
116}
117
118/// A representation of a module definition that we will eventually
119/// emit. This is used to allow easily adding onto the module each time
120/// we see a relevant decl.
121#[derive(Debug, Default)]
122pub struct Mod {
123 pub submods: BTreeMap<Ident, Mod>,
124 pub items: TokenStream,
125 pub traits: BTreeMap<Ident, Trait>,
126 pub impls: BTreeMap<(Vec<Ident>, Ident), TokenStream>,
127}
128impl Mod {
129 pub fn empty() -> Self {
130 Self {
131 submods: BTreeMap::new(),
132 items: TokenStream::new(),
133 traits: BTreeMap::new(),
134 impls: BTreeMap::new(),
135 }
136 }
137 /// Get a reference to a sub-module, creating it if necessary
138 pub fn submod<'a>(&'a mut self, i: Ident) -> &'a mut Self {
139 self.submods.entry(i).or_insert(Self::empty())
140 }
141 /// Get an immutable reference to a sub-module
142 ///
143 /// Precondition: the named submodule must already exist
144 pub fn submod_immut<'a>(&'a self, i: Ident) -> &'a Self {
145 &self.submods[&i]
146 }
147 /// Get a reference to a trait definition in this module, creating
148 /// it if necessary
149 pub fn r#trait<'a>(&'a mut self, i: Ident) -> &'a mut Trait {
150 self.traits.entry(i).or_default()
151 }
152 /// Get an immutable reference to a trait definition in this module
153 ///
154 /// Precondition: the named trait must already exist
155 pub fn trait_immut<'a>(&'a self, i: Ident) -> &'a Trait {
156 &self.traits[&i]
157 }
158 /// Get a reference to an impl block that is in this module,
159 /// creating it if necessary.
160 ///
161 /// Currently, we don't track much information about these, so
162 /// it's just a mutable token stream.
163 pub fn r#impl<'a>(&'a mut self, t: Vec<Ident>, i: Ident) -> &'a mut TokenStream {
164 self.impls.entry((t, i)).or_default()
165 }
166 /// See [`State::adjust_vars`].
167 pub fn adjust_vars(&mut self, n: u32) {
168 self.submods
169 .iter_mut()
170 .map(|(_, m)| m.adjust_vars(n))
171 .for_each(drop);
172 self.traits
173 .iter_mut()
174 .map(|(_, t)| t.adjust_vars(n))
175 .for_each(drop);
176 }
177 /// Build a token stream for this entire module
178 pub fn into_tokens(self) -> TokenStream {
179 let mut tt = TokenStream::new();
180 for (k, v) in self.submods {
181 let vt = v.into_tokens();
182 tt.extend(quote! {
183 pub mod #k { #vt }
184 });
185 }
186 for (n, mut t) in self.traits {
187 tt.extend(t.into_tokens(n));
188 }
189 tt.extend(self.items);
190 for ((ns, i), t) in self.impls {
191 tt.extend(quote! {
192 impl #(#ns)::* for #i { #t }
193 })
194 }
195 tt
196 }
197}
198
199/// Unlike [`tv::ResolvedTyvar`], which is mostly concerned with free
200/// variables and leaves bound variables alone, this tells us the most
201/// information that we have at codegen time for a top level bound
202/// variable.
203pub enum ResolvedBoundVar<'a> {
204 Definite {
205 /// The final variable offset (relative to s.var_offset) that
206 /// we followed to get to this definite type, used
207 /// occasionally to name things.
208 final_bound_var: u32,
209 /// The actual definite type that this resolved to
210 ty: Defined<'a>,
211 },
212 Resource {
213 /// A resource-type index. Currently a resource-type index is
214 /// the same as the de Bruijn index of the tyvar that
215 /// introduced the resource type, but is never affected by
216 /// e.g. s.var_offset.
217 rtidx: u32,
218 },
219}
220
221/// A whole grab-bag of useful state to have while emitting Rust
222#[derive(Debug)]
223pub struct State<'a, 'b> {
224 /// A pointer to a [`Mod`] that everything we emit will end up in
225 pub root_mod: &'a mut Mod,
226 /// A cursor to the current submodule (under [`State::root_mod`]),
227 /// where decls that we are looking at right now should end up
228 pub mod_cursor: Vec<Ident>,
229 /// If we are currently processing decls that should end up inside
230 /// a trait (representing an instance or a resource), this names
231 /// the trait where they should end up.
232 pub cur_trait: Option<Ident>,
233 /// We use a "helper module" for auxiliary definitions: for
234 /// example, an instance represented by `InstanceTrait` would end
235 /// up with nominal definitions for its nontrivial types in
236 /// `instance_trait::Type`. This keeps track of the name of that
237 /// module, if it presently exists.
238 pub cur_helper_mod: Option<Ident>,
239 /// Whether the trait/type definition that we are currently
240 /// emitting is in the helper module or the main module
241 /// corresponding directly to the wit package. This is important
242 /// to get references to other types correct.
243 pub is_helper: bool,
244 /// All the bound variables in the component type that we are
245 /// currently processing
246 pub bound_vars: &'a mut VecDeque<BoundedTyvar<'b>>,
247 /// An offset into bound_vars from which any variable indices we
248 /// see in the source component type will be resolved; used to
249 /// deal with the fact that when we recurse down into a type in
250 /// the Eq bound of a type variable, its variables are offset from
251 /// ours (since we use de Bruijn indices).
252 pub var_offset: usize,
253 /// A path through instance import/export names from the root
254 /// component type to the type we are currently processing. This
255 /// is used with [`crate::etypes::TyvarOrigin`] to decide whether
256 /// a type variable we encounter is "locally defined", i.e. should
257 /// have a type definition emitted for it in this module.
258 pub origin: Vec<ImportExport<'b>>,
259 /// A set of type variables that we encountered while emitting the
260 /// type bound for a type variable.
261 pub cur_needs_vars: Option<&'a mut BTreeSet<u32>>,
262 /// A map from type variables to the type variables used in their
263 /// bounds, used to ensure that we are parametrized over the
264 /// things we need to be
265 pub vars_needs_vars: &'a mut VecDeque<BTreeSet<u32>>,
266 /// The Rust type parameter used to represent the type that
267 /// implements the imports of a component
268 pub import_param_var: Option<Ident>,
269 /// The Rust type parameter used to represent the current Rust
270 /// state type
271 pub self_param_var: Option<Ident>,
272 /// Whether we are emitting an implementation of the component
273 /// interfaces, or just the types of the interface
274 pub is_impl: bool,
275 /// A namespace path and a name representing the Rust trait
276 /// generated for the root component that we started codegen from
277 pub root_component_name: Option<(TokenStream, &'a str)>,
278 /// Whether we are generating code for the Hyperlight host or the
279 /// Hyperlight guest
280 pub is_guest: bool,
281 /// A temporary hack to enable some special cases used by the
282 /// wasmtime guest emit. When that is refactored to use the host
283 /// guest emit, this can go away.
284 pub is_wasmtime_guest: bool,
285 /// Are we working on an export or an import of the component type?
286 pub is_export: bool,
287}
288
289/// Create a State with all of its &mut references pointing to
290/// sensible things, run a function that emits code into the state,
291/// and then generate a token stream representing everything emitted
292pub fn run_state<'b, F: for<'a> FnMut(&mut State<'a, 'b>)>(
293 is_guest: bool,
294 is_wasmtime_guest: bool,
295 mut f: F,
296) -> TokenStream {
297 let mut root_mod = Mod::empty();
298 let mut bound_vars = std::collections::VecDeque::new();
299 let mut vars_needs_vars = std::collections::VecDeque::new();
300 {
301 let mut state = State::new(
302 &mut root_mod,
303 &mut bound_vars,
304 &mut vars_needs_vars,
305 is_guest,
306 is_wasmtime_guest,
307 );
308 f(&mut state);
309 }
310 root_mod.into_tokens()
311}
312
313impl<'a, 'b> State<'a, 'b> {
314 pub fn new(
315 root_mod: &'a mut Mod,
316 bound_vars: &'a mut VecDeque<BoundedTyvar<'b>>,
317 vars_needs_vars: &'a mut VecDeque<BTreeSet<u32>>,
318 is_guest: bool,
319 is_wasmtime_guest: bool,
320 ) -> Self {
321 Self {
322 root_mod,
323 mod_cursor: Vec::new(),
324 cur_trait: None,
325 cur_helper_mod: None,
326 is_helper: false,
327 bound_vars,
328 var_offset: 0,
329 origin: Vec::new(),
330 cur_needs_vars: None,
331 vars_needs_vars,
332 import_param_var: None,
333 self_param_var: None,
334 is_impl: false,
335 root_component_name: None,
336 is_guest,
337 is_wasmtime_guest,
338 is_export: false,
339 }
340 }
341 pub fn clone<'c>(&'c mut self) -> State<'c, 'b> {
342 State {
343 root_mod: self.root_mod,
344 mod_cursor: self.mod_cursor.clone(),
345 cur_trait: self.cur_trait.clone(),
346 cur_helper_mod: self.cur_helper_mod.clone(),
347 is_helper: self.is_helper,
348 bound_vars: self.bound_vars,
349 var_offset: self.var_offset,
350 origin: self.origin.clone(),
351 cur_needs_vars: self.cur_needs_vars.as_deref_mut(),
352 vars_needs_vars: self.vars_needs_vars,
353 import_param_var: self.import_param_var.clone(),
354 self_param_var: self.self_param_var.clone(),
355 is_impl: self.is_impl,
356 root_component_name: self.root_component_name.clone(),
357 is_guest: self.is_guest,
358 is_wasmtime_guest: self.is_wasmtime_guest,
359 is_export: self.is_export,
360 }
361 }
362 /// Obtain a reference to the [`Mod`] that we are currently
363 /// generating code in, creating it if necessary
364 pub fn cur_mod<'c>(&'c mut self) -> &'c mut Mod {
365 let mut m: &'c mut Mod = self.root_mod;
366 for i in &self.mod_cursor {
367 m = m.submod(i.clone());
368 }
369 if self.is_helper {
370 m = m.submod(self.cur_helper_mod.clone().unwrap());
371 }
372 m
373 }
374 /// Obtain an immutable reference to the [`Mod`] that we are
375 /// currently generating code in.
376 ///
377 /// Precondition: the module must already exist
378 pub fn cur_mod_immut<'c>(&'c self) -> &'c Mod {
379 let mut m: &'c Mod = self.root_mod;
380 for i in &self.mod_cursor {
381 m = m.submod_immut(i.clone());
382 }
383 if self.is_helper {
384 m = m.submod_immut(self.cur_helper_mod.clone().unwrap());
385 }
386 m
387 }
388 /// Copy the state, changing its module cursor to emit code into a
389 /// different module
390 pub fn with_cursor<'c>(&'c mut self, cursor: Vec<Ident>) -> State<'c, 'b> {
391 let mut s = self.clone();
392 s.mod_cursor = cursor;
393 s
394 }
395 /// Copy the state, replacing its [`State::cur_needs_vars`] reference,
396 /// allowing a caller to capture the vars referenced by any emit
397 /// run with the resultant state
398 pub fn with_needs_vars<'c>(&'c mut self, needs_vars: &'c mut BTreeSet<u32>) -> State<'c, 'b> {
399 let mut s = self.clone();
400 s.cur_needs_vars = Some(needs_vars);
401 s
402 }
403 /// Record that an emit sequence needed a var, given an absolute
404 /// index for the var (i.e. ignoring [`State::var_offset`])
405 pub fn need_noff_var(&mut self, n: u32) {
406 self.cur_needs_vars.as_mut().map(|vs| vs.insert(n));
407 }
408 /// Use the [`State::cur_needs_vars`] map to populate
409 /// [`State::vars_needs_vars`] for a var that we presumably just
410 /// finished emitting a bound for
411 pub fn record_needs_vars(&mut self, n: u32) {
412 let un = n as usize;
413 if self.vars_needs_vars.len() < un + 1 {
414 self.vars_needs_vars.resize(un + 1, BTreeSet::new());
415 }
416 let Some(ref mut cnvs) = self.cur_needs_vars else {
417 return;
418 };
419 log::debug!("debug varref: recording {:?} for var {:?}", cnvs.iter(), un);
420 self.vars_needs_vars[un].extend(cnvs.iter());
421 }
422 /// Get a list of all the variables needed by a var, given its absolute
423 /// index (i.e. ignoring [`State::var_offset`])
424 pub fn get_noff_var_refs(&mut self, n: u32) -> BTreeSet<u32> {
425 let un = n as usize;
426 if self.vars_needs_vars.len() < un + 1 {
427 return BTreeSet::new();
428 };
429 log::debug!(
430 "debug varref: looking up {:?} for var {:?}",
431 self.vars_needs_vars[un].iter(),
432 un
433 );
434 self.vars_needs_vars[un].clone()
435 }
436 /// Find the exported name which gave rise to a component type
437 /// variable, given its absolute index (i.e. ignoring
438 /// [`State::var_offset`])
439 pub fn noff_var_id(&self, n: u32) -> Ident {
440 let Some(n) = self.bound_vars[n as usize].origin.last_name() else {
441 panic!("missing origin on tyvar in rust emit")
442 };
443 kebab_to_type(n)
444 }
445 /// Copy the state, changing it to emit into the helper module of
446 /// the current trait
447 pub fn helper<'c>(&'c mut self) -> State<'c, 'b> {
448 let mut s = self.clone();
449 s.is_helper = true;
450 s
451 }
452 /// Construct a namespace token stream that can be emitted in the
453 /// current module to refer to a name in the root module
454 pub fn root_path(&self) -> TokenStream {
455 if self.is_impl {
456 return TokenStream::new();
457 }
458 let mut s = self
459 .mod_cursor
460 .iter()
461 .map(|_| quote! { super })
462 .collect::<Vec<_>>();
463 if self.is_helper {
464 s.push(quote! { super });
465 }
466 quote! { #(#s::)* }
467 }
468 /// Construct a namespace token stream that can be emitted in the
469 /// current module to refer to a name in the helper module
470 pub fn helper_path(&self) -> TokenStream {
471 if self.is_impl {
472 let c = &self.mod_cursor;
473 let helper = self.cur_helper_mod.clone().unwrap();
474 let h = if !self.is_helper {
475 quote! { #helper:: }
476 } else {
477 TokenStream::new()
478 };
479 quote! { #(#c::)*#h }
480 } else if self.is_helper {
481 quote! { self:: }
482 } else {
483 let helper = self.cur_helper_mod.clone().unwrap();
484 quote! { #helper:: }
485 }
486 }
487 /// Emit a namespace token stream that can be emitted in the root
488 /// module to refer to the current trait
489 pub fn cur_trait_path(&self) -> TokenStream {
490 let tns = &self.mod_cursor;
491 let tid = self.cur_trait.clone().unwrap();
492 quote! { #(#tns::)* #tid }
493 }
494 /// Add a supertrait constraint referring to a trait in the helper
495 /// module; primarily used to add a constraint for the trait
496 /// representing a resource type.
497 pub fn add_helper_supertrait(&mut self, r: Ident) {
498 let (Some(t), Some(hm)) = (self.cur_trait.clone(), &self.cur_helper_mod.clone()) else {
499 panic!("invariant violation")
500 };
501 self.cur_mod()
502 .r#trait(t)
503 .supertraits
504 .insert(vec![hm.clone(), r], TokenStream::new());
505 }
506 /// Obtain a reference to the [`Trait`] that we are currently
507 /// generating code in, creating it if necessary.
508 ///
509 /// Precondition: we are currently generating code in a trait
510 /// (i.e. [`State::cur_trait`] is not [`None`])
511 pub fn cur_trait<'c>(&'c mut self) -> &'c mut Trait {
512 let n = self.cur_trait.as_ref().unwrap().clone();
513 self.cur_mod().r#trait(n)
514 }
515 /// Obtain an immutable reference to the [`Trait`] that we are
516 /// currently generating code in.
517 ///
518 /// Precondition: we are currently generating code in a trait
519 /// (i.e. [`State::cur_trait`] is not [`None`]), and that trait has
520 /// already been created
521 pub fn cur_trait_immut<'c>(&'c self) -> &'c Trait {
522 let n = self.cur_trait.as_ref().unwrap().clone();
523 self.cur_mod_immut().trait_immut(n)
524 }
525 /// Obtain a reference to the trait at the given module path and
526 /// name from the root module, creating it and any named modules
527 /// if necessary
528 pub fn r#trait<'c>(&'c mut self, namespace: &'c [Ident], name: Ident) -> &'c mut Trait {
529 let mut m: &'c mut Mod = self.root_mod;
530 for i in namespace {
531 m = m.submod(i.clone());
532 }
533 m.r#trait(name)
534 }
535 /// Add an import/export to [`State::origin`], reflecting that we are now
536 /// looking at code underneath it
537 ///
538 /// origin_was_export differs from s.is_export in that s.is_export
539 /// keeps track of whether the item overall was imported or exported
540 /// from the root component (taking into account positivity), whereas
541 /// origin_was_export just checks if this particular extern_decl was
542 /// imported or exported from its parent instance (and so e.g. an
543 /// export of an instance that is imported by the root component has
544 /// !s.is_export && origin_was_export)
545 pub fn push_origin<'c>(&'c mut self, origin_was_export: bool, name: &'b str) -> State<'c, 'b> {
546 let mut s = self.clone();
547 s.origin.push(if origin_was_export {
548 ImportExport::Export(name)
549 } else {
550 ImportExport::Import(name)
551 });
552 s
553 }
554 /// Find out if a [`Defined`] type is actually a reference to a
555 /// locally defined type variable, returning its index and bound
556 /// if it is
557 pub fn is_var_defn(&self, t: &Defined<'b>) -> Option<(u32, TypeBound<'b>)> {
558 match t {
559 Defined::Handleable(Handleable::Var(tv)) => match tv {
560 Tyvar::Bound(n) => {
561 let bv = &self.bound_vars[self.var_offset + (*n as usize)];
562 log::debug!("checking an origin {:?} {:?}", bv.origin, self.origin);
563 if bv.origin.matches(self.origin.iter()) {
564 Some((*n, bv.bound.clone()))
565 } else {
566 None
567 }
568 }
569 Tyvar::Free(_) => panic!("free tyvar in finished type"),
570 },
571 _ => None,
572 }
573 }
574 /// Find out if a variable is locally-defined given its absolute
575 /// index, returning its origin and bound if it is
576 pub fn is_noff_var_local<'c>(
577 &'c self,
578 n: u32,
579 ) -> Option<(Vec<ImportExport<'c>>, TypeBound<'a>)> {
580 let bv = &self.bound_vars[n as usize];
581 bv.origin
582 .is_local(self.origin.iter())
583 .map(|path| (path, bv.bound.clone()))
584 }
585 /// Obtain an immutable reference to the trait at the specified
586 /// namespace path, either from the root module (if `absolute`)
587 /// is true, or from the current module
588 ///
589 /// Precondition: all named traits/modules must exist
590 pub fn resolve_trait_immut(&self, absolute: bool, path: &[Ident]) -> &Trait {
591 log::debug!("resolving trait {:?} {:?}", absolute, path);
592 let mut m = if absolute {
593 &*self.root_mod
594 } else {
595 self.cur_mod_immut()
596 };
597 for x in &path[0..path.len() - 1] {
598 m = &m.submods[x];
599 }
600 &m.traits[&path[path.len() - 1]]
601 }
602 /// Shift all of the type variable indices over, because we have
603 /// gone under some binders. Used when we switch from looking at
604 /// a component's import types (where type idxs are de Bruijn into
605 /// the component's uvar list) to a component's export types
606 /// (where type idx are de Bruijn first into the evar list and
607 /// then the uvar list, as we go under the existential binders).
608 pub fn adjust_vars(&mut self, n: u32) {
609 self.vars_needs_vars
610 .iter_mut()
611 .enumerate()
612 .for_each(|(i, vs)| {
613 *vs = vs.iter().map(|v| v + n).collect();
614 log::debug!("updated {:?} to {:?}", i, *vs);
615 });
616 for _ in 0..n {
617 self.vars_needs_vars.push_front(BTreeSet::new());
618 }
619 self.root_mod.adjust_vars(n);
620 }
621 /// Resolve a type variable as far as possible: either this ends
622 /// up with a definition, in which case, let's get that, or it
623 /// ends up with a resource type, in which case we return the
624 /// resource index
625 ///
626 /// Distinct from [`Ctx::resolve_tv`], which is mostly concerned
627 /// with free variables, because this is concerned entirely with
628 /// bound variables.
629 pub fn resolve_bound_var(&self, n: u32) -> ResolvedBoundVar<'b> {
630 let noff = self.var_offset as u32 + n;
631 match &self.bound_vars[noff as usize].bound {
632 TypeBound::Eq(Defined::Handleable(Handleable::Var(Tyvar::Bound(nn)))) => {
633 self.resolve_bound_var(n + 1 + nn)
634 }
635 TypeBound::Eq(t) => ResolvedBoundVar::Definite {
636 final_bound_var: n,
637 ty: t.clone(),
638 },
639 TypeBound::SubResource => ResolvedBoundVar::Resource { rtidx: noff },
640 }
641 }
642
643 /// Construct a namespace path referring to the resource trait for
644 /// a resource with the given name
645 pub fn resource_trait_path(&self, r: Ident) -> Vec<Ident> {
646 let mut path = self.mod_cursor.clone();
647 let helper = self
648 .cur_helper_mod
649 .as_ref()
650 .expect("There should always be a helper mod to hold a resource trait")
651 .clone();
652 path.push(helper);
653 path.push(r);
654 path
655 }
656}
657
658/// A parsed representation of a WIT name, containing package
659/// namespaces, an actual name, and possibly a SemVer version
660#[derive(Debug, Clone)]
661pub struct WitName<'a> {
662 pub namespaces: Vec<&'a str>,
663 pub name: &'a str,
664 pub _version: Vec<&'a str>,
665}
666impl<'a> WitName<'a> {
667 /// Extract a list of Rust module names corresponding to the WIT
668 /// namespace/package
669 pub fn namespace_idents(&self) -> Vec<Ident> {
670 self.namespaces
671 .iter()
672 .map(|x| kebab_to_namespace(x))
673 .collect::<Vec<_>>()
674 }
675 /// Extract a token stream representing the Rust namespace path
676 /// corresponding to the WIT namespace/package
677 pub fn namespace_path(&self) -> TokenStream {
678 let ns = self.namespace_idents();
679 quote! { #(#ns)::* }
680 }
681}
682/// Parse a kebab-name as a WIT name
683pub fn split_wit_name(n: &str) -> WitName<'_> {
684 let mut namespaces = Vec::new();
685 let mut colon_components = n.split(':').rev();
686 let last = colon_components.next().unwrap();
687 namespaces.extend(colon_components.rev());
688 let mut slash_components = last.split('/').rev();
689 let mut versioned_name = slash_components.next().unwrap().split('@');
690 let name = versioned_name.next().unwrap();
691 namespaces.extend(slash_components.rev());
692 WitName {
693 namespaces,
694 name,
695 _version: versioned_name.collect(),
696 }
697}
698
699fn kebab_to_snake(n: &str) -> Ident {
700 if n == "self" {
701 return format_ident!("self_");
702 }
703 let mut ret = String::new();
704 for c in n.chars() {
705 if c == '-' {
706 ret.push('_');
707 continue;
708 }
709 ret.push(c);
710 }
711 format_ident!("r#{}", ret)
712}
713
714fn kebab_to_camel(n: &str) -> Ident {
715 let mut word_start = true;
716 let mut ret = String::new();
717 for c in n.chars() {
718 if c == '-' {
719 word_start = true;
720 continue;
721 }
722 if word_start {
723 ret.extend(c.to_uppercase())
724 } else {
725 ret.push(c)
726 };
727 word_start = false;
728 }
729 format_ident!("{}", ret)
730}
731
732/// Convert a kebab name to something suitable for use as a
733/// (value-level) variable
734pub fn kebab_to_var(n: &str) -> Ident {
735 kebab_to_snake(n)
736}
737/// Convert a kebab name to something suitable for use as a
738/// type constructor
739pub fn kebab_to_cons(n: &str) -> Ident {
740 kebab_to_camel(n)
741}
742/// Convert a kebab name to something suitable for use as a getter
743/// function name
744pub fn kebab_to_getter(n: &str) -> Ident {
745 kebab_to_snake(n)
746}
747/// Convert a kebab name to something suitable for use as a type name
748pub fn kebab_to_type(n: &str) -> Ident {
749 kebab_to_camel(n)
750}
751/// Convert a kebab name to something suitable for use as a module
752/// name/namespace path entry
753pub fn kebab_to_namespace(n: &str) -> Ident {
754 kebab_to_snake(n)
755}
756/// From a kebab name for a Component, derive something suitable for
757/// use as the name of the imports trait for that component
758pub fn kebab_to_imports_name(trait_name: &str) -> Ident {
759 format_ident!("{}Imports", kebab_to_type(trait_name))
760}
761/// From a kebab name for a Component, derive something suitable for
762/// use as the name of the imports trait for that component
763pub fn kebab_to_exports_name(trait_name: &str) -> Ident {
764 format_ident!("{}Exports", kebab_to_type(trait_name))
765}
766
767/// The kinds of names that a function associated with a resource in
768/// WIT can have
769pub enum ResourceItemName {
770 Constructor,
771 Method(Ident),
772 Static(Ident),
773}
774
775/// The kinds of names that a function in WIT can have
776pub enum FnName {
777 Associated(Ident, ResourceItemName),
778 Plain(Ident),
779}
780/// Parse a kebab-name as a WIT function name, figuring out if it is
781/// associated with a resource
782pub fn kebab_to_fn(n: &str) -> FnName {
783 if let Some(n) = n.strip_prefix("[constructor]") {
784 return FnName::Associated(kebab_to_type(n), ResourceItemName::Constructor);
785 }
786 if let Some(n) = n.strip_prefix("[method]") {
787 let mut i = n.split('.');
788 let r = i.next().unwrap();
789 let n = i.next().unwrap();
790 return FnName::Associated(
791 kebab_to_type(r),
792 ResourceItemName::Method(kebab_to_snake(n)),
793 );
794 }
795 if let Some(n) = n.strip_prefix("[static]") {
796 let mut i = n.split('.');
797 let r = i.next().unwrap();
798 let n = i.next().unwrap();
799 return FnName::Associated(
800 kebab_to_type(r),
801 ResourceItemName::Static(kebab_to_snake(n)),
802 );
803 }
804 FnName::Plain(kebab_to_snake(n))
805}