1 use crate::config::{AsyncConf, ErrorConf, ErrorConfField, TracingConf};
2 use proc_macro2::{Ident, TokenStream};
3 use quote::quote;
4 use std::collections::HashMap;
5 use std::rc::Rc;
6 use wasmtime_environ::error::{Error, format_err};
7 use witx::{Document, Id, InterfaceFunc, Module, NamedType, TypeRef};
8 
9 pub use crate::config::Asyncness;
10 
11 pub 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 }
23 impl CodegenSettings {
new( error_conf: &ErrorConf, async_: &AsyncConf, doc: &Document, wasmtime: bool, tracing: &TracingConf, mutable: bool, ) -> Result<Self, Error>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     }
get_async(&self, module: &Module, func: &InterfaceFunc) -> Asyncness41     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 
46 pub struct ErrorTransform {
47     m: Vec<ErrorType>,
48 }
49 
50 impl ErrorTransform {
empty() -> Self51     pub fn empty() -> Self {
52         Self { m: Vec::new() }
53     }
new(conf: &ErrorConf, doc: &Document) -> Result<Self, Error>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(format_err!("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(format_err!(
68                                     "duplicate rich type identifier of {ident:?} not allowed. prior definition at {prior_def:?}",
69                                 ));
70                         }
71                         Ok(ErrorType::User(UserErrorType {
72                             abi_type,
73                             rich_type: field.rich_error.clone(),
74                             method_fragment: ident.to_string()
75                         }))
76                     } else {
77                         return Err(format_err!(
78                             "rich error type must be identifier for now - TODO add ability to provide a corresponding identifier: {:?}",
79                             field.err_loc
80                         ))
81                     }
82                 }
83                 else { Err(format_err!("No witx typename \"{}\" found", ident.to_string())) }
84             }
85         ).collect::<Result<Vec<_>, Error>>()?;
86         Ok(Self { m })
87     }
88 
iter(&self) -> impl Iterator<Item = &ErrorType>89     pub fn iter(&self) -> impl Iterator<Item = &ErrorType> {
90         self.m.iter()
91     }
92 
for_abi_error(&self, tref: &TypeRef) -> Option<&ErrorType>93     pub fn for_abi_error(&self, tref: &TypeRef) -> Option<&ErrorType> {
94         match tref {
95             TypeRef::Name(nt) => self.for_name(nt),
96             TypeRef::Value { .. } => None,
97         }
98     }
99 
for_name(&self, nt: &NamedType) -> Option<&ErrorType>100     pub fn for_name(&self, nt: &NamedType) -> Option<&ErrorType> {
101         self.m.iter().find(|e| e.abi_type().name == nt.name)
102     }
103 }
104 
105 pub enum ErrorType {
106     User(UserErrorType),
107     Generated(TrappableErrorType),
108 }
109 impl ErrorType {
abi_type(&self) -> &NamedType110     pub fn abi_type(&self) -> &NamedType {
111         match self {
112             Self::User(u) => &u.abi_type,
113             Self::Generated(r) => &r.abi_type,
114         }
115     }
116 }
117 
118 pub struct TrappableErrorType {
119     abi_type: Rc<NamedType>,
120     rich_type: Ident,
121 }
122 
123 impl TrappableErrorType {
abi_type(&self) -> TypeRef124     pub fn abi_type(&self) -> TypeRef {
125         TypeRef::Name(self.abi_type.clone())
126     }
typename(&self) -> TokenStream127     pub fn typename(&self) -> TokenStream {
128         let richtype = &self.rich_type;
129         quote!(#richtype)
130     }
131 }
132 
133 pub struct UserErrorType {
134     abi_type: Rc<NamedType>,
135     rich_type: syn::Path,
136     method_fragment: String,
137 }
138 
139 impl UserErrorType {
abi_type(&self) -> TypeRef140     pub fn abi_type(&self) -> TypeRef {
141         TypeRef::Name(self.abi_type.clone())
142     }
typename(&self) -> TokenStream143     pub fn typename(&self) -> TokenStream {
144         let t = &self.rich_type;
145         quote!(#t)
146     }
method_fragment(&self) -> &str147     pub fn method_fragment(&self) -> &str {
148         &self.method_fragment
149     }
150 }
151