1 use crate::config::{AsyncConf, ErrorConf};
2 use anyhow::{anyhow, Error};
3 use proc_macro2::TokenStream;
4 use quote::quote;
5 use std::collections::HashMap;
6 use std::rc::Rc;
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 }
16 impl CodegenSettings {
17     pub fn new(
18         error_conf: &ErrorConf,
19         async_: &AsyncConf,
20         doc: &Document,
21         wasmtime: bool,
22     ) -> Result<Self, Error> {
23         let errors = ErrorTransform::new(error_conf, doc)?;
24         Ok(Self {
25             errors,
26             async_: async_.clone(),
27             wasmtime,
28         })
29     }
30     pub fn get_async(&self, module: &Module, func: &InterfaceFunc) -> Asyncness {
31         self.async_.get(module.name.as_str(), func.name.as_str())
32     }
33 }
34 
35 pub struct ErrorTransform {
36     m: Vec<UserErrorType>,
37 }
38 
39 impl ErrorTransform {
40     pub fn empty() -> Self {
41         Self { m: Vec::new() }
42     }
43     pub fn new(conf: &ErrorConf, doc: &Document) -> Result<Self, Error> {
44         let mut richtype_identifiers = HashMap::new();
45         let m = conf.iter().map(|(ident, field)|
46             if let Some(abi_type) = doc.typename(&Id::new(ident.to_string())) {
47                     if let Some(ident) = field.rich_error.get_ident() {
48                         if let Some(prior_def) = richtype_identifiers.insert(ident.clone(), field.err_loc.clone())
49                          {
50                             return Err(anyhow!(
51                                     "duplicate rich type identifier of {:?} not allowed. prior definition at {:?}",
52                                     ident, prior_def
53                                 ));
54                         }
55                         Ok(UserErrorType {
56                             abi_type,
57                             rich_type: field.rich_error.clone(),
58                             method_fragment: ident.to_string()
59                         })
60                     } else {
61                         return Err(anyhow!(
62                             "rich error type must be identifier for now - TODO add ability to provide a corresponding identifier: {:?}",
63                             field.err_loc
64                         ))
65                     }
66                 }
67                 else { Err(anyhow!("No witx typename \"{}\" found", ident.to_string())) }
68         ).collect::<Result<Vec<_>, Error>>()?;
69         Ok(Self { m })
70     }
71 
72     pub fn iter(&self) -> impl Iterator<Item = &UserErrorType> {
73         self.m.iter()
74     }
75 
76     pub fn for_abi_error(&self, tref: &TypeRef) -> Option<&UserErrorType> {
77         match tref {
78             TypeRef::Name(nt) => self.for_name(nt),
79             TypeRef::Value { .. } => None,
80         }
81     }
82 
83     pub fn for_name(&self, nt: &NamedType) -> Option<&UserErrorType> {
84         self.m.iter().find(|u| u.abi_type.name == nt.name)
85     }
86 }
87 
88 pub struct UserErrorType {
89     abi_type: Rc<NamedType>,
90     rich_type: syn::Path,
91     method_fragment: String,
92 }
93 
94 impl UserErrorType {
95     pub fn abi_type(&self) -> TypeRef {
96         TypeRef::Name(self.abi_type.clone())
97     }
98     pub fn typename(&self) -> TokenStream {
99         let t = &self.rich_type;
100         quote!(#t)
101     }
102     pub fn method_fragment(&self) -> &str {
103         &self.method_fragment
104     }
105 }
106