wiggle_generate/
codegen_settings.rs

1use crate::config::{AsyncConf, ErrorConf, ErrorConfField, TracingConf};
2use anyhow::{anyhow, Error};
3use proc_macro2::{Ident, TokenStream};
4use quote::quote;
5use std::collections::HashMap;
6use std::rc::Rc;
7use witx::{Document, Id, InterfaceFunc, Module, NamedType, TypeRef};
8
9pub use crate::config::Asyncness;
10
11pub struct CodegenSettings {
12    pub errors: ErrorTransform,
13    pub async_: AsyncConf,
14    pub wasmtime: bool,
15    /// Disabling this feature makes it possible to remove all of the tracing
16    /// code emitted in the Wiggle-generated code; this can be helpful while
17    /// inspecting the code (e.g., with `cargo expand`).
18    pub tracing: TracingConf,
19    /// Determine whether the context structure will use `&mut self` (true) or
20    /// simply `&self`.
21    pub mutable: bool,
22}
23impl CodegenSettings {
24    pub fn new(
25        error_conf: &ErrorConf,
26        async_: &AsyncConf,
27        doc: &Document,
28        wasmtime: bool,
29        tracing: &TracingConf,
30        mutable: bool,
31    ) -> Result<Self, Error> {
32        let errors = ErrorTransform::new(error_conf, doc)?;
33        Ok(Self {
34            errors,
35            async_: async_.clone(),
36            wasmtime,
37            tracing: tracing.clone(),
38            mutable,
39        })
40    }
41    pub fn get_async(&self, module: &Module, func: &InterfaceFunc) -> Asyncness {
42        self.async_.get(module.name.as_str(), func.name.as_str())
43    }
44}
45
46pub struct ErrorTransform {
47    m: Vec<ErrorType>,
48}
49
50impl ErrorTransform {
51    pub fn empty() -> Self {
52        Self { m: Vec::new() }
53    }
54    pub fn new(conf: &ErrorConf, doc: &Document) -> Result<Self, Error> {
55        let mut richtype_identifiers = HashMap::new();
56        let m = conf.iter().map(|(ident, field)|
57            match field {
58                ErrorConfField::Trappable(field) => if let Some(abi_type) = doc.typename(&Id::new(ident.to_string())) {
59                    Ok(ErrorType::Generated(TrappableErrorType { abi_type, rich_type: field.rich_error.clone() }))
60                } else {
61                    Err(anyhow!("No witx typename \"{}\" found", ident.to_string()))
62                },
63                ErrorConfField::User(field) => if let Some(abi_type) = doc.typename(&Id::new(ident.to_string())) {
64                    if let Some(ident) = field.rich_error.get_ident() {
65                        if let Some(prior_def) = richtype_identifiers.insert(ident.clone(), field.err_loc)
66                         {
67                            return Err(anyhow!(
68                                    "duplicate rich type identifier of {:?} not allowed. prior definition at {:?}",
69                                    ident, prior_def
70                                ));
71                        }
72                        Ok(ErrorType::User(UserErrorType {
73                            abi_type,
74                            rich_type: field.rich_error.clone(),
75                            method_fragment: ident.to_string()
76                        }))
77                    } else {
78                        return Err(anyhow!(
79                            "rich error type must be identifier for now - TODO add ability to provide a corresponding identifier: {:?}",
80                            field.err_loc
81                        ))
82                    }
83                }
84                else { Err(anyhow!("No witx typename \"{}\" found", ident.to_string())) }
85            }
86        ).collect::<Result<Vec<_>, Error>>()?;
87        Ok(Self { m })
88    }
89
90    pub fn iter(&self) -> impl Iterator<Item = &ErrorType> {
91        self.m.iter()
92    }
93
94    pub fn for_abi_error(&self, tref: &TypeRef) -> Option<&ErrorType> {
95        match tref {
96            TypeRef::Name(nt) => self.for_name(nt),
97            TypeRef::Value { .. } => None,
98        }
99    }
100
101    pub fn for_name(&self, nt: &NamedType) -> Option<&ErrorType> {
102        self.m.iter().find(|e| e.abi_type().name == nt.name)
103    }
104}
105
106pub enum ErrorType {
107    User(UserErrorType),
108    Generated(TrappableErrorType),
109}
110impl ErrorType {
111    pub fn abi_type(&self) -> &NamedType {
112        match self {
113            Self::User(u) => &u.abi_type,
114            Self::Generated(r) => &r.abi_type,
115        }
116    }
117}
118
119pub struct TrappableErrorType {
120    abi_type: Rc<NamedType>,
121    rich_type: Ident,
122}
123
124impl TrappableErrorType {
125    pub fn abi_type(&self) -> TypeRef {
126        TypeRef::Name(self.abi_type.clone())
127    }
128    pub fn typename(&self) -> TokenStream {
129        let richtype = &self.rich_type;
130        quote!(#richtype)
131    }
132}
133
134pub struct UserErrorType {
135    abi_type: Rc<NamedType>,
136    rich_type: syn::Path,
137    method_fragment: String,
138}
139
140impl UserErrorType {
141    pub fn abi_type(&self) -> TypeRef {
142        TypeRef::Name(self.abi_type.clone())
143    }
144    pub fn typename(&self) -> TokenStream {
145        let t = &self.rich_type;
146        quote!(#t)
147    }
148    pub fn method_fragment(&self) -> &str {
149        &self.method_fragment
150    }
151}