bitwarden_error_macro/flat/
attribute.rs1use quote::quote;
2use syn::Data;
3
4pub(crate) fn bitwarden_error_flat(
5 input: &syn::DeriveInput,
6 type_identifier: &proc_macro2::Ident,
7 export_as_identifier: &proc_macro2::Ident,
8) -> proc_macro::TokenStream {
9 match &input.data {
10 Data::Enum(data) => {
11 let variant_names = data.variants.iter().map(|variant| &variant.ident);
12 let match_arms = data.variants.iter().map(|variant| {
13 let variant_ident = &variant.ident;
14 let variant_str = variant_ident.to_string();
15
16 match variant.fields {
17 syn::Fields::Unit => {
18 quote! {
19 #type_identifier::#variant_ident => #variant_str
20 }
21 }
22 syn::Fields::Named(_) => {
23 quote! {
24 #type_identifier::#variant_ident { .. } => #variant_str
25 }
26 }
27 syn::Fields::Unnamed(_) => {
28 quote! {
29 #type_identifier::#variant_ident(..) => #variant_str
30 }
31 }
32 }
33 });
34
35 let wasm = cfg!(feature = "wasm").then(|| {
36 flat_error_wasm(
37 type_identifier,
38 export_as_identifier,
39 &variant_names.collect::<Vec<_>>(),
40 )
41 });
42
43 quote! {
44 #input
45 #wasm
46
47 #[automatically_derived]
48 impl ::bitwarden_error::flat_error::FlatError for #type_identifier {
49 fn error_variant(&self) -> &'static str {
50 match &self {
51 #(#match_arms), *
52 }
53 }
54 }
55 }
56 .into()
57 }
58 _ => syn::Error::new_spanned(input, "bitwarden_error can only be used with enums")
59 .to_compile_error()
60 .into(),
61 }
62}
63
64fn flat_error_wasm(
65 type_identifier: &proc_macro2::Ident,
66 export_as_identifier: &proc_macro2::Ident,
67 variant_names: &[&proc_macro2::Ident],
68) -> proc_macro2::TokenStream {
69 let export_as_identifier_str = export_as_identifier.to_string();
70 let is_error_function_name = format!("is{}", export_as_identifier);
71 let ts_variant_names = variant_names
72 .iter()
73 .map(|vn| format!(r#""{vn}""#))
74 .collect::<Vec<String>>()
75 .join("|");
76 let ts_code_str = format!(
77 r##"r#"
78 export interface {export_as_identifier_str} extends Error {{
79 name: "{export_as_identifier_str}";
80 variant: {ts_variant_names};
81 }};
82
83 export function {is_error_function_name}(error: any): error is {export_as_identifier_str};
84 "#"##,
85 );
86 let ts_code: proc_macro2::TokenStream = ts_code_str
87 .parse()
88 .expect("Could not generate TypeScript code");
89
90 quote! {
91 const _: () = {
92 use bitwarden_error::wasm_bindgen::prelude::*;
93 use bitwarden_error::wasm_bindgen as wasm_bindgen;
94
95 #[wasm_bindgen(typescript_custom_section)]
96 const TS_APPEND_CONTENT: &'static str = #ts_code;
97
98 #[wasm_bindgen(js_name = #is_error_function_name, skip_typescript)]
99 pub fn is_error(error: &JsValue) -> bool {
100 let name_js_value = ::bitwarden_error::js_sys::Reflect::get(&error, &JsValue::from_str("name")).unwrap_or(JsValue::NULL);
101 let name = name_js_value.as_string().unwrap_or_default();
102 name == #export_as_identifier_str
103 }
104
105 #[automatically_derived]
106 impl From<#type_identifier> for JsValue {
107 fn from(error: #type_identifier) -> Self {
108 let js_error = ::bitwarden_error::wasm::SdkJsError::new(error.to_string());
109 js_error.set_name(#export_as_identifier_str.to_owned());
110 js_error.set_variant(::bitwarden_error::flat_error::FlatError::error_variant(&error).to_owned());
111 js_error.into()
112 }
113 }
114 };
115 }
116}