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/// A whole grab-bag of useful state to have while emitting Rust
200#[derive(Debug)]
201pub struct State<'a, 'b> {
202    /// A pointer to a [`Mod`] that everything we emit will end up in
203    pub root_mod: &'a mut Mod,
204    /// A cursor to the current submodule (under [`State::root_mod`]),
205    /// where decls that we are looking at right now should end up
206    pub mod_cursor: Vec<Ident>,
207    /// If we are currently processing decls that should end up inside
208    /// a trait (representing an instance or a resource), this names
209    /// the trait where they should end up.
210    pub cur_trait: Option<Ident>,
211    /// We use a "helper module" for auxiliary definitions: for
212    /// example, an instance represented by `InstanceTrait` would end
213    /// up with nominal definitions for its nontrivial types in
214    /// `instance_trait::Type`.  This keeps track of the name of that
215    /// module, if it presently exists.
216    pub cur_helper_mod: Option<Ident>,
217    /// Whether the trait/type definition that we are currently
218    /// emitting is in the helper module or the main module
219    /// corresponding directly to the wit package. This is important
220    /// to get references to other types correct.
221    pub is_helper: bool,
222    /// All the bound variables in the component type that we are
223    /// currently processing
224    pub bound_vars: &'a mut VecDeque<BoundedTyvar<'b>>,
225    /// An offset into bound_vars from which any variable indices we
226    /// see in the source component type will be resolved; used to
227    /// deal with the fact that when we recurse down into a type in
228    /// the Eq bound of a type variable, its variables are offset from
229    /// ours (since we use de Bruijn indices).
230    pub var_offset: usize,
231    /// A path through instance import/export names from the root
232    /// component type to the type we are currently processing. This
233    /// is used with [`crate::etypes::TyvarOrigin`] to decide whether
234    /// a type variable we encounter is "locally defined", i.e. should
235    /// have a type definition emitted for it in this module.
236    pub origin: Vec<ImportExport<'b>>,
237    /// A set of type variables that we encountered while emitting the
238    /// type bound for a type variable.
239    pub cur_needs_vars: Option<&'a mut BTreeSet<u32>>,
240    /// A map from type variables to the type variables used in their
241    /// bounds, used to ensure that we are parametrized over the
242    /// things we need to be
243    pub vars_needs_vars: &'a mut VecDeque<BTreeSet<u32>>,
244    /// The Rust type parameter used to represent the type that
245    /// implements the imports of a component
246    pub import_param_var: Option<Ident>,
247    /// The Rust type parameter used to represent the current Rust
248    /// state type
249    pub self_param_var: Option<Ident>,
250    /// Whether we are emitting an implementation of the component
251    /// interfaces, or just the types of the interface
252    pub is_impl: bool,
253    /// A namespace path and a name representing the Rust trait
254    /// generated for the root component that we started codegen from
255    pub root_component_name: Option<(TokenStream, &'a str)>,
256    /// Whether we are generating code for the Hyperlight host or the
257    /// Hyperlight guest
258    pub is_guest: bool,
259    /// A temporary hack to enable some special cases used by the
260    /// wasmtime guest emit. When that is refactored to use the host
261    /// guest emit, this can go away.
262    pub is_wasmtime_guest: bool,
263}
264
265/// Create a State with all of its &mut references pointing to
266/// sensible things, run a function that emits code into the state,
267/// and then generate a token stream representing everything emitted
268pub fn run_state<'b, F: for<'a> FnMut(&mut State<'a, 'b>)>(
269    is_guest: bool,
270    is_wasmtime_guest: bool,
271    mut f: F,
272) -> TokenStream {
273    let mut root_mod = Mod::empty();
274    let mut bound_vars = std::collections::VecDeque::new();
275    let mut vars_needs_vars = std::collections::VecDeque::new();
276    {
277        let mut state = State::new(
278            &mut root_mod,
279            &mut bound_vars,
280            &mut vars_needs_vars,
281            is_guest,
282            is_wasmtime_guest,
283        );
284        f(&mut state);
285    }
286    root_mod.into_tokens()
287}
288
289impl<'a, 'b> State<'a, 'b> {
290    pub fn new(
291        root_mod: &'a mut Mod,
292        bound_vars: &'a mut VecDeque<BoundedTyvar<'b>>,
293        vars_needs_vars: &'a mut VecDeque<BTreeSet<u32>>,
294        is_guest: bool,
295        is_wasmtime_guest: bool,
296    ) -> Self {
297        Self {
298            root_mod,
299            mod_cursor: Vec::new(),
300            cur_trait: None,
301            cur_helper_mod: None,
302            is_helper: false,
303            bound_vars,
304            var_offset: 0,
305            origin: Vec::new(),
306            cur_needs_vars: None,
307            vars_needs_vars,
308            import_param_var: None,
309            self_param_var: None,
310            is_impl: false,
311            root_component_name: None,
312            is_guest,
313            is_wasmtime_guest,
314        }
315    }
316    pub fn clone<'c>(&'c mut self) -> State<'c, 'b> {
317        State {
318            root_mod: self.root_mod,
319            mod_cursor: self.mod_cursor.clone(),
320            cur_trait: self.cur_trait.clone(),
321            cur_helper_mod: self.cur_helper_mod.clone(),
322            is_helper: self.is_helper,
323            bound_vars: self.bound_vars,
324            var_offset: self.var_offset,
325            origin: self.origin.clone(),
326            cur_needs_vars: self.cur_needs_vars.as_deref_mut(),
327            vars_needs_vars: self.vars_needs_vars,
328            import_param_var: self.import_param_var.clone(),
329            self_param_var: self.self_param_var.clone(),
330            is_impl: self.is_impl,
331            root_component_name: self.root_component_name.clone(),
332            is_guest: self.is_guest,
333            is_wasmtime_guest: self.is_wasmtime_guest,
334        }
335    }
336    /// Obtain a reference to the [`Mod`] that we are currently
337    /// generating code in, creating it if necessary
338    pub fn cur_mod<'c>(&'c mut self) -> &'c mut Mod {
339        let mut m: &'c mut Mod = self.root_mod;
340        for i in &self.mod_cursor {
341            m = m.submod(i.clone());
342        }
343        if self.is_helper {
344            m = m.submod(self.cur_helper_mod.clone().unwrap());
345        }
346        m
347    }
348    /// Obtain an immutable reference to the [`Mod`] that we are
349    /// currently generating code in.
350    ///
351    /// Precondition: the module must already exist
352    pub fn cur_mod_immut<'c>(&'c self) -> &'c Mod {
353        let mut m: &'c Mod = self.root_mod;
354        for i in &self.mod_cursor {
355            m = m.submod_immut(i.clone());
356        }
357        if self.is_helper {
358            m = m.submod_immut(self.cur_helper_mod.clone().unwrap());
359        }
360        m
361    }
362    /// Copy the state, changing its module cursor to emit code into a
363    /// different module
364    pub fn with_cursor<'c>(&'c mut self, cursor: Vec<Ident>) -> State<'c, 'b> {
365        let mut s = self.clone();
366        s.mod_cursor = cursor;
367        s
368    }
369    /// Copy the state, replacing its [`State::cur_needs_vars`] reference,
370    /// allowing a caller to capture the vars referenced by any emit
371    /// run with the resultant state
372    pub fn with_needs_vars<'c>(&'c mut self, needs_vars: &'c mut BTreeSet<u32>) -> State<'c, 'b> {
373        let mut s = self.clone();
374        s.cur_needs_vars = Some(needs_vars);
375        s
376    }
377    /// Record that an emit sequence needed a var, given an absolute
378    /// index for the var (i.e. ignoring [`State::var_offset`])
379    pub fn need_noff_var(&mut self, n: u32) {
380        self.cur_needs_vars.as_mut().map(|vs| vs.insert(n));
381    }
382    /// Use the [`State::cur_needs_vars`] map to populate
383    /// [`State::vars_needs_vars`] for a var that we presumably just
384    /// finished emitting a bound for
385    pub fn record_needs_vars(&mut self, n: u32) {
386        let un = n as usize;
387        if self.vars_needs_vars.len() < un + 1 {
388            self.vars_needs_vars.resize(un + 1, BTreeSet::new());
389        }
390        let Some(ref mut cnvs) = self.cur_needs_vars else {
391            return;
392        };
393        log::debug!("debug varref: recording {:?} for var {:?}", cnvs.iter(), un);
394        self.vars_needs_vars[un].extend(cnvs.iter());
395    }
396    /// Get a list of all the variables needed by a var, given its absolute
397    /// index (i.e. ignoring [`State::var_offset`])
398    pub fn get_noff_var_refs(&mut self, n: u32) -> BTreeSet<u32> {
399        let un = n as usize;
400        if self.vars_needs_vars.len() < un + 1 {
401            return BTreeSet::new();
402        };
403        log::debug!(
404            "debug varref: looking up {:?} for var {:?}",
405            self.vars_needs_vars[un].iter(),
406            un
407        );
408        self.vars_needs_vars[un].clone()
409    }
410    /// Find the exported name which gave rise to a component type
411    /// variable, given its absolute index (i.e. ignoring
412    /// [`State::var_offset`])
413    pub fn noff_var_id(&self, n: u32) -> Ident {
414        let Some(n) = self.bound_vars[n as usize].origin.last_name() else {
415            panic!("missing origin on tyvar in rust emit")
416        };
417        kebab_to_type(n)
418    }
419    /// Copy the state, changing it to emit into the helper module of
420    /// the current trait
421    pub fn helper<'c>(&'c mut self) -> State<'c, 'b> {
422        let mut s = self.clone();
423        s.is_helper = true;
424        s
425    }
426    /// Construct a namespace token stream that can be emitted in the
427    /// current module to refer to a name in the root module
428    pub fn root_path(&self) -> TokenStream {
429        if self.is_impl {
430            return TokenStream::new();
431        }
432        let mut s = self
433            .mod_cursor
434            .iter()
435            .map(|_| quote! { super })
436            .collect::<Vec<_>>();
437        if self.is_helper {
438            s.push(quote! { super });
439        }
440        quote! { #(#s::)* }
441    }
442    /// Construct a namespace token stream that can be emitted in the
443    /// current module to refer to a name in the helper module
444    pub fn helper_path(&self) -> TokenStream {
445        if self.is_impl {
446            let c = &self.mod_cursor;
447            let helper = self.cur_helper_mod.clone().unwrap();
448            let h = if !self.is_helper {
449                quote! { #helper:: }
450            } else {
451                TokenStream::new()
452            };
453            quote! { #(#c::)*#h }
454        } else if self.is_helper {
455            quote! { self:: }
456        } else {
457            let helper = self.cur_helper_mod.clone().unwrap();
458            quote! { #helper:: }
459        }
460    }
461    /// Emit a namespace token stream that can be emitted in the root
462    /// module to refer to the current trait
463    pub fn cur_trait_path(&self) -> TokenStream {
464        let tns = &self.mod_cursor;
465        let tid = self.cur_trait.clone().unwrap();
466        quote! { #(#tns::)* #tid }
467    }
468    /// Add a supertrait constraint referring to a trait in the helper
469    /// module; primarily used to add a constraint for the trait
470    /// representing a resource type.
471    pub fn add_helper_supertrait(&mut self, r: Ident) {
472        let (Some(t), Some(hm)) = (self.cur_trait.clone(), &self.cur_helper_mod.clone()) else {
473            panic!("invariant violation")
474        };
475        self.cur_mod()
476            .r#trait(t)
477            .supertraits
478            .insert(vec![hm.clone(), r], TokenStream::new());
479    }
480    /// Obtain a reference to the [`Trait`] that we are currently
481    /// generating code in, creating it if necessary.
482    ///
483    /// Precondition: we are currently generating code in a trait
484    /// (i.e. [`State::cur_trait`] is not [`None`])
485    pub fn cur_trait<'c>(&'c mut self) -> &'c mut Trait {
486        let n = self.cur_trait.as_ref().unwrap().clone();
487        self.cur_mod().r#trait(n)
488    }
489    /// Obtain an immutable reference to the [`Trait`] that we are
490    /// currently generating code in.
491    ///
492    /// Precondition: we are currently generating code in a trait
493    /// (i.e. [`State::cur_trait`] is not [`None`]), and that trait has
494    /// already been created
495    pub fn cur_trait_immut<'c>(&'c self) -> &'c Trait {
496        let n = self.cur_trait.as_ref().unwrap().clone();
497        self.cur_mod_immut().trait_immut(n)
498    }
499    /// Obtain a reference to the trait at the given module path and
500    /// name from the root module, creating it and any named modules
501    /// if necessary
502    pub fn r#trait<'c>(&'c mut self, namespace: &'c [Ident], name: Ident) -> &'c mut Trait {
503        let mut m: &'c mut Mod = self.root_mod;
504        for i in namespace {
505            m = m.submod(i.clone());
506        }
507        m.r#trait(name)
508    }
509    /// Add an import/export to [`State::origin`], reflecting that we are now
510    /// looking at code underneath it
511    pub fn push_origin<'c>(&'c mut self, is_export: bool, name: &'b str) -> State<'c, 'b> {
512        let mut s = self.clone();
513        s.origin.push(if is_export {
514            ImportExport::Export(name)
515        } else {
516            ImportExport::Import(name)
517        });
518        s
519    }
520    /// Find out if a [`Defined`] type is actually a reference to a
521    /// locally defined type variable, returning its index and bound
522    /// if it is
523    pub fn is_var_defn(&self, t: &Defined<'b>) -> Option<(u32, TypeBound<'b>)> {
524        match t {
525            Defined::Handleable(Handleable::Var(tv)) => match tv {
526                Tyvar::Bound(n) => {
527                    let bv = &self.bound_vars[self.var_offset + (*n as usize)];
528                    log::debug!("checking an origin {:?} {:?}", bv.origin, self.origin);
529                    if bv.origin.matches(self.origin.iter()) {
530                        Some((*n, bv.bound.clone()))
531                    } else {
532                        None
533                    }
534                }
535                Tyvar::Free(_) => panic!("free tyvar in finished type"),
536            },
537            _ => None,
538        }
539    }
540    /// Find out if a variable is locally-defined given its absolute
541    /// index, returning its origin and bound if it is
542    pub fn is_noff_var_local<'c>(
543        &'c self,
544        n: u32,
545    ) -> Option<(Vec<ImportExport<'c>>, TypeBound<'a>)> {
546        let bv = &self.bound_vars[n as usize];
547        bv.origin
548            .is_local(self.origin.iter())
549            .map(|path| (path, bv.bound.clone()))
550    }
551    /// Obtain an immutable reference to the trait at the specified
552    /// namespace path, either from the root module (if `absolute`)
553    /// is true, or from the current module
554    ///
555    /// Precondition: all named traits/modules must exist
556    pub fn resolve_trait_immut(&self, absolute: bool, path: &[Ident]) -> &Trait {
557        log::debug!("resolving trait {:?} {:?}", absolute, path);
558        let mut m = if absolute {
559            &*self.root_mod
560        } else {
561            self.cur_mod_immut()
562        };
563        for x in &path[0..path.len() - 1] {
564            m = &m.submods[x];
565        }
566        &m.traits[&path[path.len() - 1]]
567    }
568    /// Shift all of the type variable indices over, because we have
569    /// gone under some binders.  Used when we switch from looking at
570    /// a component's import types (where type idxs are de Bruijn into
571    /// the component's uvar list) to a component's export types
572    /// (where type idx are de Bruijn first into the evar list and
573    /// then the uvar list, as we go under the existential binders).
574    pub fn adjust_vars(&mut self, n: u32) {
575        self.vars_needs_vars
576            .iter_mut()
577            .enumerate()
578            .for_each(|(i, vs)| {
579                *vs = vs.iter().map(|v| v + n).collect();
580                log::debug!("updated {:?} to {:?}", i, *vs);
581            });
582        for _ in 0..n {
583            self.vars_needs_vars.push_front(BTreeSet::new());
584        }
585        self.root_mod.adjust_vars(n);
586    }
587    /// Resolve a type variable as far as possible: either this ends
588    /// up with a definition, in which case, let's get that, or it
589    /// ends up with a resource type, in which case we return the
590    /// resource index
591    pub fn resolve_tv(&self, n: u32) -> (u32, Option<Defined<'b>>) {
592        match &self.bound_vars[self.var_offset + n as usize].bound {
593            TypeBound::Eq(Defined::Handleable(Handleable::Var(Tyvar::Bound(nn)))) => {
594                self.resolve_tv(n + 1 + nn)
595            }
596            TypeBound::Eq(t) => (n, Some(t.clone())),
597            TypeBound::SubResource => (n, None),
598        }
599    }
600    /// Construct a namespace path referring to the resource trait for
601    /// a resource with the given name
602    pub fn resource_trait_path(&self, r: Ident) -> Vec<Ident> {
603        let mut path = self.mod_cursor.clone();
604        let helper = self
605            .cur_helper_mod
606            .as_ref()
607            .expect("There should always be a helper mod to hold a resource trait")
608            .clone();
609        path.push(helper);
610        path.push(r);
611        path
612    }
613}
614
615/// A parsed representation of a WIT name, containing package
616/// namespaces, an actual name, and possibly a SemVer version
617#[derive(Debug, Clone)]
618pub struct WitName<'a> {
619    pub namespaces: Vec<&'a str>,
620    pub name: &'a str,
621    pub _version: Vec<&'a str>,
622}
623impl<'a> WitName<'a> {
624    /// Extract a list of Rust module names corresponding to the WIT
625    /// namespace/package
626    pub fn namespace_idents(&self) -> Vec<Ident> {
627        self.namespaces
628            .iter()
629            .map(|x| kebab_to_namespace(x))
630            .collect::<Vec<_>>()
631    }
632    /// Extract a token stream representing the Rust namespace path
633    /// corresponding to the WIT namespace/package
634    pub fn namespace_path(&self) -> TokenStream {
635        let ns = self.namespace_idents();
636        quote! { #(#ns)::* }
637    }
638}
639/// Parse a kebab-name as a WIT name
640pub fn split_wit_name(n: &str) -> WitName {
641    let mut namespaces = Vec::new();
642    let mut colon_components = n.split(':').rev();
643    let last = colon_components.next().unwrap();
644    namespaces.extend(colon_components.rev());
645    let mut slash_components = last.split('/').rev();
646    let mut versioned_name = slash_components.next().unwrap().split('@');
647    let name = versioned_name.next().unwrap();
648    namespaces.extend(slash_components.rev());
649    WitName {
650        namespaces,
651        name,
652        _version: versioned_name.collect(),
653    }
654}
655
656fn kebab_to_snake(n: &str) -> Ident {
657    if n == "self" {
658        return format_ident!("self_");
659    }
660    let mut ret = String::new();
661    for c in n.chars() {
662        if c == '-' {
663            ret.push('_');
664            continue;
665        }
666        ret.push(c);
667    }
668    format_ident!("r#{}", ret)
669}
670
671fn kebab_to_camel(n: &str) -> Ident {
672    let mut word_start = true;
673    let mut ret = String::new();
674    for c in n.chars() {
675        if c == '-' {
676            word_start = true;
677            continue;
678        }
679        if word_start {
680            ret.extend(c.to_uppercase())
681        } else {
682            ret.push(c)
683        };
684        word_start = false;
685    }
686    format_ident!("{}", ret)
687}
688
689/// Convert a kebab name to something suitable for use as a
690/// (value-level) variable
691pub fn kebab_to_var(n: &str) -> Ident {
692    kebab_to_snake(n)
693}
694/// Convert a kebab name to something suitable for use as a
695/// type constructor
696pub fn kebab_to_cons(n: &str) -> Ident {
697    kebab_to_camel(n)
698}
699/// Convert a kebab name to something suitable for use as a getter
700/// function name
701pub fn kebab_to_getter(n: &str) -> Ident {
702    kebab_to_snake(n)
703}
704/// Convert a kebab name to something suitable for use as a type name
705pub fn kebab_to_type(n: &str) -> Ident {
706    kebab_to_camel(n)
707}
708/// Convert a kebab name to something suitable for use as a module
709/// name/namespace path entry
710pub fn kebab_to_namespace(n: &str) -> Ident {
711    kebab_to_snake(n)
712}
713/// From a kebab name for a Component, derive something suitable for
714/// use as the name of the imports trait for that component
715pub fn kebab_to_imports_name(trait_name: &str) -> Ident {
716    format_ident!("{}Imports", kebab_to_type(trait_name))
717}
718/// From a kebab name for a Component, derive something suitable for
719/// use as the name of the imports trait for that component
720pub fn kebab_to_exports_name(trait_name: &str) -> Ident {
721    format_ident!("{}Exports", kebab_to_type(trait_name))
722}
723
724/// The kinds of names that a function associated with a resource in
725/// WIT can have
726pub enum ResourceItemName {
727    Constructor,
728    Method(Ident),
729    Static(Ident),
730}
731
732/// The kinds of names that a function in WIT can have
733pub enum FnName {
734    Associated(Ident, ResourceItemName),
735    Plain(Ident),
736}
737/// Parse a kebab-name as a WIT function name, figuring out if it is
738/// associated with a resource
739pub fn kebab_to_fn(n: &str) -> FnName {
740    if let Some(n) = n.strip_prefix("[constructor]") {
741        return FnName::Associated(kebab_to_type(n), ResourceItemName::Constructor);
742    }
743    if let Some(n) = n.strip_prefix("[method]") {
744        let mut i = n.split('.');
745        let r = i.next().unwrap();
746        let n = i.next().unwrap();
747        return FnName::Associated(
748            kebab_to_type(r),
749            ResourceItemName::Method(kebab_to_snake(n)),
750        );
751    }
752    if let Some(n) = n.strip_prefix("[static]") {
753        let mut i = n.split('.');
754        let r = i.next().unwrap();
755        let n = i.next().unwrap();
756        return FnName::Associated(
757            kebab_to_type(r),
758            ResourceItemName::Static(kebab_to_snake(n)),
759        );
760    }
761    FnName::Plain(kebab_to_snake(n))
762}