From 4b7ebf328dfd95609389683107fb3f4a5fe26a84 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Tue, 5 Nov 2024 13:55:34 +0100 Subject: [PATCH 01/53] feat: scaffold error crates --- Cargo.lock | 8 ++++++++ crates/bitwarden-error-macro/Cargo.toml | 18 ++++++++++++++++++ crates/bitwarden-error-macro/src/lib.rs | 1 + crates/bitwarden-error/Cargo.toml | 15 +++++++++++++++ crates/bitwarden-error/src/lib.rs | 1 + 5 files changed, 43 insertions(+) create mode 100644 crates/bitwarden-error-macro/Cargo.toml create mode 100644 crates/bitwarden-error-macro/src/lib.rs create mode 100644 crates/bitwarden-error/Cargo.toml create mode 100644 crates/bitwarden-error/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index be749ab9..8f7a1a7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -413,6 +413,14 @@ dependencies = [ "zeroize", ] +[[package]] +name = "bitwarden-error" +version = "1.0.0" + +[[package]] +name = "bitwarden-error-macro" +version = "1.0.0" + [[package]] name = "bitwarden-exporters" version = "1.0.0" diff --git a/crates/bitwarden-error-macro/Cargo.toml b/crates/bitwarden-error-macro/Cargo.toml new file mode 100644 index 00000000..79f69946 --- /dev/null +++ b/crates/bitwarden-error-macro/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "bitwarden-error-macro" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +homepage.workspace = true +repository.workspace = true +license-file.workspace = true +keywords.workspace = true + +[dependencies] + +[lints] +workspace = true + +[lib] +proc-macro = true diff --git a/crates/bitwarden-error-macro/src/lib.rs b/crates/bitwarden-error-macro/src/lib.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/crates/bitwarden-error-macro/src/lib.rs @@ -0,0 +1 @@ + diff --git a/crates/bitwarden-error/Cargo.toml b/crates/bitwarden-error/Cargo.toml new file mode 100644 index 00000000..913510e7 --- /dev/null +++ b/crates/bitwarden-error/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "bitwarden-error" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +homepage.workspace = true +repository.workspace = true +license-file.workspace = true +keywords.workspace = true + +[dependencies] + +[lints] +workspace = true diff --git a/crates/bitwarden-error/src/lib.rs b/crates/bitwarden-error/src/lib.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/crates/bitwarden-error/src/lib.rs @@ -0,0 +1 @@ + From 270647742b9cee1868b4d5c98713ee283afbf115 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Tue, 5 Nov 2024 14:18:11 +0100 Subject: [PATCH 02/53] feat: add basic manual implementation --- crates/bitwarden-error/src/lib.rs | 6 ++++++ crates/bitwarden-error/tests/mod.rs | 32 +++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 crates/bitwarden-error/tests/mod.rs diff --git a/crates/bitwarden-error/src/lib.rs b/crates/bitwarden-error/src/lib.rs index 8b137891..c7709252 100644 --- a/crates/bitwarden-error/src/lib.rs +++ b/crates/bitwarden-error/src/lib.rs @@ -1 +1,7 @@ +pub trait BitwardenError { + /// The name of the error. + fn variant(&self) -> &'static str; + /// A human-readable description of the error. + fn message(&self) -> &'static str; +} diff --git a/crates/bitwarden-error/tests/mod.rs b/crates/bitwarden-error/tests/mod.rs new file mode 100644 index 00000000..07bcd10f --- /dev/null +++ b/crates/bitwarden-error/tests/mod.rs @@ -0,0 +1,32 @@ +use bitwarden_error::BitwardenError; + +#[test] +fn variant_for_basic_enum() { + enum SimpleError { + Foo, + Bar, + Baz, + } + + impl BitwardenError for SimpleError { + fn variant(&self) -> &'static str { + match self { + SimpleError::Foo => "SimpleError::Foo", + SimpleError::Bar => "SimpleError::Bar", + SimpleError::Baz => "SimpleError::Baz", + } + } + + fn message(&self) -> &'static str { + todo!() + } + } + + let foo = SimpleError::Foo; + let bar = SimpleError::Bar; + let baz = SimpleError::Baz; + + assert_eq!(foo.variant(), "SimpleError::Foo"); + assert_eq!(bar.variant(), "SimpleError::Bar"); + assert_eq!(baz.variant(), "SimpleError::Baz"); +} From 03dbebabe1a610dc656dd11232a4685ecbbe4c3b Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Tue, 5 Nov 2024 14:42:30 +0100 Subject: [PATCH 03/53] refactor: convert into metadata trait/struct --- crates/bitwarden-error/src/lib.rs | 8 +------ crates/bitwarden-error/src/metadata.rs | 11 +++++++++ crates/bitwarden-error/tests/mod.rs | 31 +++++++++++++++----------- 3 files changed, 30 insertions(+), 20 deletions(-) create mode 100644 crates/bitwarden-error/src/metadata.rs diff --git a/crates/bitwarden-error/src/lib.rs b/crates/bitwarden-error/src/lib.rs index c7709252..de5643fd 100644 --- a/crates/bitwarden-error/src/lib.rs +++ b/crates/bitwarden-error/src/lib.rs @@ -1,7 +1 @@ -pub trait BitwardenError { - /// The name of the error. - fn variant(&self) -> &'static str; - - /// A human-readable description of the error. - fn message(&self) -> &'static str; -} +pub mod metadata; diff --git a/crates/bitwarden-error/src/metadata.rs b/crates/bitwarden-error/src/metadata.rs new file mode 100644 index 00000000..9e5dbd19 --- /dev/null +++ b/crates/bitwarden-error/src/metadata.rs @@ -0,0 +1,11 @@ +pub trait AsErrorMetadata { + fn as_metadata(&self) -> ErrorMetadata; +} + +pub struct ErrorMetadata { + /// The name of the error + pub name: &'static str, + + /// A human-readable description of the error + pub message: &'static str, +} diff --git a/crates/bitwarden-error/tests/mod.rs b/crates/bitwarden-error/tests/mod.rs index 07bcd10f..4f431f27 100644 --- a/crates/bitwarden-error/tests/mod.rs +++ b/crates/bitwarden-error/tests/mod.rs @@ -1,4 +1,4 @@ -use bitwarden_error::BitwardenError; +use bitwarden_error::metadata::{AsErrorMetadata, ErrorMetadata}; #[test] fn variant_for_basic_enum() { @@ -8,25 +8,30 @@ fn variant_for_basic_enum() { Baz, } - impl BitwardenError for SimpleError { - fn variant(&self) -> &'static str { + impl AsErrorMetadata for SimpleError { + fn as_metadata(&self) -> ErrorMetadata { match self { - SimpleError::Foo => "SimpleError::Foo", - SimpleError::Bar => "SimpleError::Bar", - SimpleError::Baz => "SimpleError::Baz", + SimpleError::Foo => ErrorMetadata { + name: "SimpleError::Foo", + message: "An error occurred in the Foo variant", + }, + SimpleError::Bar => ErrorMetadata { + name: "SimpleError::Bar", + message: "An error occurred in the Bar variant", + }, + SimpleError::Baz => ErrorMetadata { + name: "SimpleError::Baz", + message: "An error occurred in the Baz variant", + }, } } - - fn message(&self) -> &'static str { - todo!() - } } let foo = SimpleError::Foo; let bar = SimpleError::Bar; let baz = SimpleError::Baz; - assert_eq!(foo.variant(), "SimpleError::Foo"); - assert_eq!(bar.variant(), "SimpleError::Bar"); - assert_eq!(baz.variant(), "SimpleError::Baz"); + assert_eq!(foo.as_metadata().name, "SimpleError::Foo"); + assert_eq!(bar.as_metadata().name, "SimpleError::Bar"); + assert_eq!(baz.as_metadata().name, "SimpleError::Baz"); } From e45a994dba27ec37e5f6732edd008acc5c7e6727 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Tue, 5 Nov 2024 14:42:41 +0100 Subject: [PATCH 04/53] feat: scaffold proc macro --- crates/bitwarden-error-macro/src/lib.rs | 8 +++++++- crates/bitwarden-error-macro/tests/mod.rs | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 crates/bitwarden-error-macro/tests/mod.rs diff --git a/crates/bitwarden-error-macro/src/lib.rs b/crates/bitwarden-error-macro/src/lib.rs index 8b137891..183a4166 100644 --- a/crates/bitwarden-error-macro/src/lib.rs +++ b/crates/bitwarden-error-macro/src/lib.rs @@ -1 +1,7 @@ - +#[proc_macro_attribute] +pub fn bitwarden_error( + _args: proc_macro::TokenStream, + item: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + item +} diff --git a/crates/bitwarden-error-macro/tests/mod.rs b/crates/bitwarden-error-macro/tests/mod.rs new file mode 100644 index 00000000..c785d2fa --- /dev/null +++ b/crates/bitwarden-error-macro/tests/mod.rs @@ -0,0 +1,19 @@ +use bitwarden_error_macro::bitwarden_error; + +#[test] +fn variant_for_basic_enum() { + #[bitwarden_error] + enum SimpleError { + Foo, + Bar, + Baz, + } + + let foo = SimpleError::Foo; + let bar = SimpleError::Bar; + let baz = SimpleError::Baz; + + // assert_eq!(foo.variant(), "SimpleError::Foo"); + // assert_eq!(bar.variant(), "SimpleError::Bar"); + // assert_eq!(baz.variant(), "SimpleError::Baz"); +} From 97ff4ae11fad785713d158e912ac2c658d876758 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Tue, 5 Nov 2024 15:32:14 +0100 Subject: [PATCH 05/53] feat: implement derive macro --- Cargo.lock | 63 +++++++++++--------- crates/bitwarden-error-macro/Cargo.toml | 2 + crates/bitwarden-error-macro/src/lib.rs | 49 +++++++++++++++ crates/bitwarden-error-macro/src/metadata.rs | 1 + crates/bitwarden-error-macro/tests/mod.rs | 19 ------ crates/bitwarden-error/Cargo.toml | 1 + crates/bitwarden-error/src/lib.rs | 5 ++ crates/bitwarden-error/tests/mod.rs | 22 +------ 8 files changed, 95 insertions(+), 67 deletions(-) create mode 100644 crates/bitwarden-error-macro/src/metadata.rs delete mode 100644 crates/bitwarden-error-macro/tests/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 8f7a1a7a..921cd8d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -167,7 +167,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -216,7 +216,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -416,10 +416,17 @@ dependencies = [ [[package]] name = "bitwarden-error" version = "1.0.0" +dependencies = [ + "bitwarden-error-macro", +] [[package]] name = "bitwarden-error-macro" version = "1.0.0" +dependencies = [ + "quote", + "syn 2.0.87", +] [[package]] name = "bitwarden-exporters" @@ -800,7 +807,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -1095,7 +1102,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -1106,7 +1113,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -1185,7 +1192,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -1195,7 +1202,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -1415,7 +1422,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -2826,7 +2833,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -2852,7 +2859,7 @@ checksum = "7f81c2fde025af7e69b1d1420531c8a8811ca898919db177141a85313b1cb932" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -2931,7 +2938,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -2942,7 +2949,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -2977,7 +2984,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -3019,7 +3026,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -3199,7 +3206,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -3212,7 +3219,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -3242,9 +3249,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.82" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83540f837a8afc019423a8edb95b52a8effe46957ee402287f4292fae35be021" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -3299,7 +3306,7 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -3392,7 +3399,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -3502,7 +3509,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -3614,7 +3621,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2c801f0f05b06df456a2da4c41b9c2c4fdccc6b9916643c6c67275c4c9e4d07" dependencies = [ "quote", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -3646,7 +3653,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.82", + "syn 2.0.87", "toml", "uniffi_meta", ] @@ -3749,7 +3756,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -3813,7 +3820,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.87", "wasm-bindgen-shared", ] @@ -3847,7 +3854,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.87", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4146,7 +4153,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] @@ -4167,7 +4174,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.82", + "syn 2.0.87", ] [[package]] diff --git a/crates/bitwarden-error-macro/Cargo.toml b/crates/bitwarden-error-macro/Cargo.toml index 79f69946..01c38a00 100644 --- a/crates/bitwarden-error-macro/Cargo.toml +++ b/crates/bitwarden-error-macro/Cargo.toml @@ -10,6 +10,8 @@ license-file.workspace = true keywords.workspace = true [dependencies] +quote = "1.0.37" +syn = "2.0.87" [lints] workspace = true diff --git a/crates/bitwarden-error-macro/src/lib.rs b/crates/bitwarden-error-macro/src/lib.rs index 183a4166..18480371 100644 --- a/crates/bitwarden-error-macro/src/lib.rs +++ b/crates/bitwarden-error-macro/src/lib.rs @@ -1,3 +1,8 @@ +mod metadata; + +use quote::quote; +use syn::Data; + #[proc_macro_attribute] pub fn bitwarden_error( _args: proc_macro::TokenStream, @@ -5,3 +10,47 @@ pub fn bitwarden_error( ) -> proc_macro::TokenStream { item } + +#[proc_macro_derive(AsErrorMetadata)] +pub fn as_error_metadata(item: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = syn::parse_macro_input!(item as syn::DeriveInput); + let struct_identifier = &input.ident; + + // let mut implementation = {quote, format_ident}!{ + // match &self { + // }; + // }; + + match &input.data { + Data::Enum(data) => { + let field_identifiers = data + .variants + .iter() + .map(|item| item.ident.clone()) + .collect::>(); + + let variant_names: Vec<_> = field_identifiers + .iter() + .map(|ident| format!("{}::{}", struct_identifier, ident)) + .collect(); + + quote! { + #[automatically_derived] + impl AsErrorMetadata for #struct_identifier { + fn as_metadata(&self) -> ErrorMetadata { + match &self { + #( + #struct_identifier::#field_identifiers => ErrorMetadata { + name: #variant_names, + message: concat!("An error occurred in the ", stringify!(#field_identifiers), " variant"), + }, + )* + } + } + } + } + .into() + } + _ => unimplemented!(), + } +} diff --git a/crates/bitwarden-error-macro/src/metadata.rs b/crates/bitwarden-error-macro/src/metadata.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/crates/bitwarden-error-macro/src/metadata.rs @@ -0,0 +1 @@ + diff --git a/crates/bitwarden-error-macro/tests/mod.rs b/crates/bitwarden-error-macro/tests/mod.rs deleted file mode 100644 index c785d2fa..00000000 --- a/crates/bitwarden-error-macro/tests/mod.rs +++ /dev/null @@ -1,19 +0,0 @@ -use bitwarden_error_macro::bitwarden_error; - -#[test] -fn variant_for_basic_enum() { - #[bitwarden_error] - enum SimpleError { - Foo, - Bar, - Baz, - } - - let foo = SimpleError::Foo; - let bar = SimpleError::Bar; - let baz = SimpleError::Baz; - - // assert_eq!(foo.variant(), "SimpleError::Foo"); - // assert_eq!(bar.variant(), "SimpleError::Bar"); - // assert_eq!(baz.variant(), "SimpleError::Baz"); -} diff --git a/crates/bitwarden-error/Cargo.toml b/crates/bitwarden-error/Cargo.toml index 913510e7..4a682d61 100644 --- a/crates/bitwarden-error/Cargo.toml +++ b/crates/bitwarden-error/Cargo.toml @@ -10,6 +10,7 @@ license-file.workspace = true keywords.workspace = true [dependencies] +bitwarden-error-macro = { version = "1.0.0", path = "../bitwarden-error-macro" } [lints] workspace = true diff --git a/crates/bitwarden-error/src/lib.rs b/crates/bitwarden-error/src/lib.rs index de5643fd..a03c06f9 100644 --- a/crates/bitwarden-error/src/lib.rs +++ b/crates/bitwarden-error/src/lib.rs @@ -1 +1,6 @@ pub mod metadata; + +pub mod prelude { + pub use crate::metadata::{AsErrorMetadata, ErrorMetadata}; + pub use bitwarden_error_macro::AsErrorMetadata; +} diff --git a/crates/bitwarden-error/tests/mod.rs b/crates/bitwarden-error/tests/mod.rs index 4f431f27..d5239f04 100644 --- a/crates/bitwarden-error/tests/mod.rs +++ b/crates/bitwarden-error/tests/mod.rs @@ -1,32 +1,14 @@ -use bitwarden_error::metadata::{AsErrorMetadata, ErrorMetadata}; +use bitwarden_error::prelude::*; #[test] fn variant_for_basic_enum() { + #[derive(AsErrorMetadata)] enum SimpleError { Foo, Bar, Baz, } - impl AsErrorMetadata for SimpleError { - fn as_metadata(&self) -> ErrorMetadata { - match self { - SimpleError::Foo => ErrorMetadata { - name: "SimpleError::Foo", - message: "An error occurred in the Foo variant", - }, - SimpleError::Bar => ErrorMetadata { - name: "SimpleError::Bar", - message: "An error occurred in the Bar variant", - }, - SimpleError::Baz => ErrorMetadata { - name: "SimpleError::Baz", - message: "An error occurred in the Baz variant", - }, - } - } - } - let foo = SimpleError::Foo; let bar = SimpleError::Bar; let baz = SimpleError::Baz; From 3393de9d1461042fb16b5298eb8107eb131afeb7 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Tue, 5 Nov 2024 15:40:46 +0100 Subject: [PATCH 06/53] feat: use attribute macro as alias for derive macro --- crates/bitwarden-error-macro/src/lib.rs | 8 +++++++- crates/bitwarden-error/src/lib.rs | 2 +- crates/bitwarden-error/tests/mod.rs | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/crates/bitwarden-error-macro/src/lib.rs b/crates/bitwarden-error-macro/src/lib.rs index 18480371..586105be 100644 --- a/crates/bitwarden-error-macro/src/lib.rs +++ b/crates/bitwarden-error-macro/src/lib.rs @@ -8,7 +8,13 @@ pub fn bitwarden_error( _args: proc_macro::TokenStream, item: proc_macro::TokenStream, ) -> proc_macro::TokenStream { - item + let input = syn::parse_macro_input!(item as syn::DeriveInput); + + quote! { + #[derive(AsErrorMetadata)] + #input + } + .into() } #[proc_macro_derive(AsErrorMetadata)] diff --git a/crates/bitwarden-error/src/lib.rs b/crates/bitwarden-error/src/lib.rs index a03c06f9..51432ba9 100644 --- a/crates/bitwarden-error/src/lib.rs +++ b/crates/bitwarden-error/src/lib.rs @@ -2,5 +2,5 @@ pub mod metadata; pub mod prelude { pub use crate::metadata::{AsErrorMetadata, ErrorMetadata}; - pub use bitwarden_error_macro::AsErrorMetadata; + pub use bitwarden_error_macro::{bitwarden_error, AsErrorMetadata}; } diff --git a/crates/bitwarden-error/tests/mod.rs b/crates/bitwarden-error/tests/mod.rs index d5239f04..d7d4acc9 100644 --- a/crates/bitwarden-error/tests/mod.rs +++ b/crates/bitwarden-error/tests/mod.rs @@ -2,7 +2,7 @@ use bitwarden_error::prelude::*; #[test] fn variant_for_basic_enum() { - #[derive(AsErrorMetadata)] + #[bitwarden_error] enum SimpleError { Foo, Bar, From 9a9c59b0ff35715a717598088dd20fc8815115a1 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Tue, 5 Nov 2024 15:53:27 +0100 Subject: [PATCH 07/53] chore: clean up commented old code --- crates/bitwarden-error-macro/src/lib.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/crates/bitwarden-error-macro/src/lib.rs b/crates/bitwarden-error-macro/src/lib.rs index 586105be..2b9e4277 100644 --- a/crates/bitwarden-error-macro/src/lib.rs +++ b/crates/bitwarden-error-macro/src/lib.rs @@ -22,11 +22,6 @@ pub fn as_error_metadata(item: proc_macro::TokenStream) -> proc_macro::TokenStre let input = syn::parse_macro_input!(item as syn::DeriveInput); let struct_identifier = &input.ident; - // let mut implementation = {quote, format_ident}!{ - // match &self { - // }; - // }; - match &input.data { Data::Enum(data) => { let field_identifiers = data From 51b23fff95c1969f9998260db5591e59841d474f Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Tue, 5 Nov 2024 16:15:34 +0100 Subject: [PATCH 08/53] refactor: move iteration into match_arms variable --- crates/bitwarden-error-macro/src/lib.rs | 36 ++++++++++--------------- crates/bitwarden-error/src/lib.rs | 6 ++--- crates/bitwarden-error/src/metadata.rs | 11 -------- crates/bitwarden-error/src/variant.rs | 3 +++ 4 files changed, 20 insertions(+), 36 deletions(-) delete mode 100644 crates/bitwarden-error/src/metadata.rs create mode 100644 crates/bitwarden-error/src/variant.rs diff --git a/crates/bitwarden-error-macro/src/lib.rs b/crates/bitwarden-error-macro/src/lib.rs index 2b9e4277..eb0f968c 100644 --- a/crates/bitwarden-error-macro/src/lib.rs +++ b/crates/bitwarden-error-macro/src/lib.rs @@ -11,41 +11,33 @@ pub fn bitwarden_error( let input = syn::parse_macro_input!(item as syn::DeriveInput); quote! { - #[derive(AsErrorMetadata)] + #[derive(ErrorVariant)] #input } .into() } -#[proc_macro_derive(AsErrorMetadata)] -pub fn as_error_metadata(item: proc_macro::TokenStream) -> proc_macro::TokenStream { +#[proc_macro_derive(ErrorVariant)] +pub fn error_variant(item: proc_macro::TokenStream) -> proc_macro::TokenStream { let input = syn::parse_macro_input!(item as syn::DeriveInput); - let struct_identifier = &input.ident; + let type_identifier = &input.ident; match &input.data { Data::Enum(data) => { - let field_identifiers = data - .variants - .iter() - .map(|item| item.ident.clone()) - .collect::>(); - - let variant_names: Vec<_> = field_identifiers - .iter() - .map(|ident| format!("{}::{}", struct_identifier, ident)) - .collect(); + let match_arms = data.variants.iter().map(|variant| { + let variant_ident = &variant.ident; + let variant_name = format!("{}::{}", type_identifier, variant_ident); + quote! { + #type_identifier::#variant_ident => #variant_name + } + }); quote! { #[automatically_derived] - impl AsErrorMetadata for #struct_identifier { - fn as_metadata(&self) -> ErrorMetadata { + impl ErrorVariant for #type_identifier { + fn error_variant(&self) -> &'static str { match &self { - #( - #struct_identifier::#field_identifiers => ErrorMetadata { - name: #variant_names, - message: concat!("An error occurred in the ", stringify!(#field_identifiers), " variant"), - }, - )* + #(#match_arms), * } } } diff --git a/crates/bitwarden-error/src/lib.rs b/crates/bitwarden-error/src/lib.rs index 51432ba9..3b81fc74 100644 --- a/crates/bitwarden-error/src/lib.rs +++ b/crates/bitwarden-error/src/lib.rs @@ -1,6 +1,6 @@ -pub mod metadata; +pub mod variant; pub mod prelude { - pub use crate::metadata::{AsErrorMetadata, ErrorMetadata}; - pub use bitwarden_error_macro::{bitwarden_error, AsErrorMetadata}; + pub use crate::variant::ErrorVariant; + pub use bitwarden_error_macro::*; } diff --git a/crates/bitwarden-error/src/metadata.rs b/crates/bitwarden-error/src/metadata.rs deleted file mode 100644 index 9e5dbd19..00000000 --- a/crates/bitwarden-error/src/metadata.rs +++ /dev/null @@ -1,11 +0,0 @@ -pub trait AsErrorMetadata { - fn as_metadata(&self) -> ErrorMetadata; -} - -pub struct ErrorMetadata { - /// The name of the error - pub name: &'static str, - - /// A human-readable description of the error - pub message: &'static str, -} diff --git a/crates/bitwarden-error/src/variant.rs b/crates/bitwarden-error/src/variant.rs new file mode 100644 index 00000000..8017d0c0 --- /dev/null +++ b/crates/bitwarden-error/src/variant.rs @@ -0,0 +1,3 @@ +pub trait ErrorVariant { + fn error_variant(&self) -> &'static str; +} From d16450132d1f8a8b875c94195ff24c930ad65772 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Tue, 5 Nov 2024 16:21:04 +0100 Subject: [PATCH 09/53] feat: add support for complex enums --- crates/bitwarden-error-macro/src/lib.rs | 26 ++++++++++++++++++++----- crates/bitwarden-error/tests/mod.rs | 25 +++++++++++++++++++++--- 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/crates/bitwarden-error-macro/src/lib.rs b/crates/bitwarden-error-macro/src/lib.rs index eb0f968c..67289f20 100644 --- a/crates/bitwarden-error-macro/src/lib.rs +++ b/crates/bitwarden-error-macro/src/lib.rs @@ -24,11 +24,27 @@ pub fn error_variant(item: proc_macro::TokenStream) -> proc_macro::TokenStream { match &input.data { Data::Enum(data) => { - let match_arms = data.variants.iter().map(|variant| { - let variant_ident = &variant.ident; - let variant_name = format!("{}::{}", type_identifier, variant_ident); - quote! { - #type_identifier::#variant_ident => #variant_name + let match_arms = data.variants.iter().map(|variant| match variant.fields { + syn::Fields::Unit => { + let variant_ident = &variant.ident; + let variant_name = format!("{}::{}", type_identifier, variant_ident); + quote! { + #type_identifier::#variant_ident => #variant_name + } + } + syn::Fields::Named(_) => { + let variant_ident = &variant.ident; + let variant_name = format!("{}::{}", type_identifier, variant_ident); + quote! { + #type_identifier::#variant_ident { .. } => #variant_name + } + } + syn::Fields::Unnamed(_) => { + let variant_ident = &variant.ident; + let variant_name = format!("{}::{}", type_identifier, variant_ident); + quote! { + #type_identifier::#variant_ident(..) => #variant_name + } } }); diff --git a/crates/bitwarden-error/tests/mod.rs b/crates/bitwarden-error/tests/mod.rs index d7d4acc9..8d572859 100644 --- a/crates/bitwarden-error/tests/mod.rs +++ b/crates/bitwarden-error/tests/mod.rs @@ -13,7 +13,26 @@ fn variant_for_basic_enum() { let bar = SimpleError::Bar; let baz = SimpleError::Baz; - assert_eq!(foo.as_metadata().name, "SimpleError::Foo"); - assert_eq!(bar.as_metadata().name, "SimpleError::Bar"); - assert_eq!(baz.as_metadata().name, "SimpleError::Baz"); + assert_eq!(foo.error_variant(), "SimpleError::Foo"); + assert_eq!(bar.error_variant(), "SimpleError::Bar"); + assert_eq!(baz.error_variant(), "SimpleError::Baz"); +} + +#[test] +fn variant_for_enum_with_fields() { + #[allow(dead_code)] + #[bitwarden_error] + enum ComplexError { + Foo(String), + Bar { x: i32, y: i32 }, + Baz(bool, bool), + } + + let foo = ComplexError::Foo("hello".to_string()); + let bar = ComplexError::Bar { x: 1, y: 2 }; + let baz = ComplexError::Baz(true, true); + + assert_eq!(foo.error_variant(), "ComplexError::Foo"); + assert_eq!(bar.error_variant(), "ComplexError::Bar"); + assert_eq!(baz.error_variant(), "ComplexError::Baz"); } From 3f00e38cfc65b94f25745957f145c33a4dbea2bd Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Tue, 5 Nov 2024 16:22:47 +0100 Subject: [PATCH 10/53] feat: add support for struct --- crates/bitwarden-error-macro/src/lib.rs | 14 ++++++++++++++ crates/bitwarden-error/tests/mod.rs | 10 ++++++++++ 2 files changed, 24 insertions(+) diff --git a/crates/bitwarden-error-macro/src/lib.rs b/crates/bitwarden-error-macro/src/lib.rs index 67289f20..ce586bbb 100644 --- a/crates/bitwarden-error-macro/src/lib.rs +++ b/crates/bitwarden-error-macro/src/lib.rs @@ -60,6 +60,20 @@ pub fn error_variant(item: proc_macro::TokenStream) -> proc_macro::TokenStream { } .into() } + Data::Struct(_) => { + let type_identifier = &input.ident; + let variant_name = format!("{}", type_identifier); + + quote! { + #[automatically_derived] + impl ErrorVariant for #type_identifier { + fn error_variant(&self) -> &'static str { + #variant_name + } + } + } + .into() + } _ => unimplemented!(), } } diff --git a/crates/bitwarden-error/tests/mod.rs b/crates/bitwarden-error/tests/mod.rs index 8d572859..8ee4206a 100644 --- a/crates/bitwarden-error/tests/mod.rs +++ b/crates/bitwarden-error/tests/mod.rs @@ -36,3 +36,13 @@ fn variant_for_enum_with_fields() { assert_eq!(bar.error_variant(), "ComplexError::Bar"); assert_eq!(baz.error_variant(), "ComplexError::Baz"); } + +#[test] +fn variant_for_struct() { + #[bitwarden_error] + struct SimpleStruct; + + let simple = SimpleStruct; + + assert_eq!(simple.error_variant(), "SimpleStruct"); +} From d868324f0e546e637ee8943db73aca78c16b2eeb Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Tue, 5 Nov 2024 16:24:28 +0100 Subject: [PATCH 11/53] feat: add error handling for unions --- crates/bitwarden-error-macro/src/lib.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/bitwarden-error-macro/src/lib.rs b/crates/bitwarden-error-macro/src/lib.rs index ce586bbb..76a0e536 100644 --- a/crates/bitwarden-error-macro/src/lib.rs +++ b/crates/bitwarden-error-macro/src/lib.rs @@ -74,6 +74,10 @@ pub fn error_variant(item: proc_macro::TokenStream) -> proc_macro::TokenStream { } .into() } - _ => unimplemented!(), + Data::Union(_) => { + syn::Error::new_spanned(input, "bitwarden_error cannot be used with unions") + .to_compile_error() + .into() + } } } From d6ee79dbb439541aec9a1c06cb662790673a6da2 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Tue, 5 Nov 2024 16:30:52 +0100 Subject: [PATCH 12/53] feat: add support for getting all variants --- crates/bitwarden-error-macro/src/lib.rs | 12 ++++++++++++ crates/bitwarden-error/src/variant.rs | 1 + crates/bitwarden-error/tests/mod.rs | 24 ++++++++++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/crates/bitwarden-error-macro/src/lib.rs b/crates/bitwarden-error-macro/src/lib.rs index 76a0e536..71e8a971 100644 --- a/crates/bitwarden-error-macro/src/lib.rs +++ b/crates/bitwarden-error-macro/src/lib.rs @@ -24,6 +24,10 @@ pub fn error_variant(item: proc_macro::TokenStream) -> proc_macro::TokenStream { match &input.data { Data::Enum(data) => { + let variant_names = data.variants.iter().map(|variant| { + let variant_ident = &variant.ident; + format!("{}::{}", type_identifier, variant_ident) + }); let match_arms = data.variants.iter().map(|variant| match variant.fields { syn::Fields::Unit => { let variant_ident = &variant.ident; @@ -51,6 +55,10 @@ pub fn error_variant(item: proc_macro::TokenStream) -> proc_macro::TokenStream { quote! { #[automatically_derived] impl ErrorVariant for #type_identifier { + fn error_variants() -> &'static [&'static str] { + &[#(#variant_names), *] + } + fn error_variant(&self) -> &'static str { match &self { #(#match_arms), * @@ -67,6 +75,10 @@ pub fn error_variant(item: proc_macro::TokenStream) -> proc_macro::TokenStream { quote! { #[automatically_derived] impl ErrorVariant for #type_identifier { + fn error_variants() -> &'static [&'static str] { + &[#variant_name] + } + fn error_variant(&self) -> &'static str { #variant_name } diff --git a/crates/bitwarden-error/src/variant.rs b/crates/bitwarden-error/src/variant.rs index 8017d0c0..a27973ed 100644 --- a/crates/bitwarden-error/src/variant.rs +++ b/crates/bitwarden-error/src/variant.rs @@ -1,3 +1,4 @@ pub trait ErrorVariant { + fn error_variants() -> &'static [&'static str]; fn error_variant(&self) -> &'static str; } diff --git a/crates/bitwarden-error/tests/mod.rs b/crates/bitwarden-error/tests/mod.rs index 8ee4206a..0cb5d5ba 100644 --- a/crates/bitwarden-error/tests/mod.rs +++ b/crates/bitwarden-error/tests/mod.rs @@ -46,3 +46,27 @@ fn variant_for_struct() { assert_eq!(simple.error_variant(), "SimpleStruct"); } + +#[test] +fn variant_names_for_enum() { + #[allow(dead_code)] + #[bitwarden_error] + enum SimpleError { + Foo, + Bar, + Baz, + } + + assert_eq!( + SimpleError::error_variants(), + &["SimpleError::Foo", "SimpleError::Bar", "SimpleError::Baz"] + ); +} + +#[test] +fn variant_names_for_struct() { + #[bitwarden_error] + struct SimpleStruct; + + assert_eq!(SimpleStruct::error_variants(), &["SimpleStruct"]); +} From 8e91caf89c72ee73793048bc440fc67cc3026692 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Wed, 6 Nov 2024 14:29:24 +0100 Subject: [PATCH 13/53] feat: add support for throwing bitwarden error in wasm --- Cargo.lock | 2 + crates/bitwarden-core/Cargo.toml | 1 + .../src/client/encryption_settings.rs | 2 + crates/bitwarden-error-macro/src/lib.rs | 3 ++ crates/bitwarden-error/src/lib.rs | 3 ++ crates/bitwarden-error/tests/mod.rs | 35 +++++++++++++++++ crates/bitwarden-wasm-internal/Cargo.toml | 1 + crates/bitwarden-wasm-internal/src/client.rs | 17 +++++++-- crates/bitwarden-wasm-internal/src/crypto.rs | 12 +++++- crates/bitwarden-wasm-internal/src/error.rs | 38 +++++++++++++++++++ 10 files changed, 109 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 921cd8d9..2a81da4d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -353,6 +353,7 @@ dependencies = [ "bitwarden-api-api", "bitwarden-api-identity", "bitwarden-crypto", + "bitwarden-error", "chrono", "getrandom", "hmac", @@ -582,6 +583,7 @@ version = "0.1.0" dependencies = [ "bitwarden-core", "bitwarden-crypto", + "bitwarden-error", "bitwarden-vault", "console_error_panic_hook", "console_log", diff --git a/crates/bitwarden-core/Cargo.toml b/crates/bitwarden-core/Cargo.toml index d212afb7..d2cf7d28 100644 --- a/crates/bitwarden-core/Cargo.toml +++ b/crates/bitwarden-core/Cargo.toml @@ -49,6 +49,7 @@ wasm-bindgen = { workspace = true, optional = true } zeroize = { version = ">=1.7.0, <2.0", features = ["derive", "aarch64"] } zxcvbn = { version = ">=3.0.1, <4.0", optional = true } tsify-next = { workspace = true, optional = true } +bitwarden-error = { version = "1.0.0", path = "../bitwarden-error" } [target.'cfg(not(target_arch="wasm32"))'.dependencies] # By default, we use rustls as the TLS stack and rust-platform-verifier to support user-installed root certificates diff --git a/crates/bitwarden-core/src/client/encryption_settings.rs b/crates/bitwarden-core/src/client/encryption_settings.rs index 9d954902..fc8c87b8 100644 --- a/crates/bitwarden-core/src/client/encryption_settings.rs +++ b/crates/bitwarden-core/src/client/encryption_settings.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use bitwarden_crypto::{AsymmetricCryptoKey, CryptoError, KeyContainer, SymmetricCryptoKey}; #[cfg(feature = "internal")] use bitwarden_crypto::{AsymmetricEncString, EncString, MasterKey}; +use bitwarden_error::prelude::*; use thiserror::Error; use uuid::Uuid; @@ -10,6 +11,7 @@ use uuid::Uuid; use crate::error::Result; use crate::VaultLocked; +#[bitwarden_error] #[derive(Debug, Error)] pub enum EncryptionSettingsError { #[error("Cryptography error, {0}")] diff --git a/crates/bitwarden-error-macro/src/lib.rs b/crates/bitwarden-error-macro/src/lib.rs index 71e8a971..397099c2 100644 --- a/crates/bitwarden-error-macro/src/lib.rs +++ b/crates/bitwarden-error-macro/src/lib.rs @@ -9,10 +9,13 @@ pub fn bitwarden_error( item: proc_macro::TokenStream, ) -> proc_macro::TokenStream { let input = syn::parse_macro_input!(item as syn::DeriveInput); + let type_identifier = &input.ident; quote! { #[derive(ErrorVariant)] #input + + impl BitwardenError for #type_identifier {} } .into() } diff --git a/crates/bitwarden-error/src/lib.rs b/crates/bitwarden-error/src/lib.rs index 3b81fc74..425aee70 100644 --- a/crates/bitwarden-error/src/lib.rs +++ b/crates/bitwarden-error/src/lib.rs @@ -2,5 +2,8 @@ pub mod variant; pub mod prelude { pub use crate::variant::ErrorVariant; + pub use crate::BitwardenError; pub use bitwarden_error_macro::*; } + +pub trait BitwardenError: variant::ErrorVariant + ToString {} diff --git a/crates/bitwarden-error/tests/mod.rs b/crates/bitwarden-error/tests/mod.rs index 0cb5d5ba..ac7300cf 100644 --- a/crates/bitwarden-error/tests/mod.rs +++ b/crates/bitwarden-error/tests/mod.rs @@ -2,6 +2,7 @@ use bitwarden_error::prelude::*; #[test] fn variant_for_basic_enum() { + #[derive(Debug)] #[bitwarden_error] enum SimpleError { Foo, @@ -9,6 +10,12 @@ fn variant_for_basic_enum() { Baz, } + impl ToString for SimpleError { + fn to_string(&self) -> String { + format!("{:?}", self) + } + } + let foo = SimpleError::Foo; let bar = SimpleError::Bar; let baz = SimpleError::Baz; @@ -21,6 +28,7 @@ fn variant_for_basic_enum() { #[test] fn variant_for_enum_with_fields() { #[allow(dead_code)] + #[derive(Debug)] #[bitwarden_error] enum ComplexError { Foo(String), @@ -28,6 +36,12 @@ fn variant_for_enum_with_fields() { Baz(bool, bool), } + impl ToString for ComplexError { + fn to_string(&self) -> String { + format!("{:?}", self) + } + } + let foo = ComplexError::Foo("hello".to_string()); let bar = ComplexError::Bar { x: 1, y: 2 }; let baz = ComplexError::Baz(true, true); @@ -39,9 +53,16 @@ fn variant_for_enum_with_fields() { #[test] fn variant_for_struct() { + #[derive(Debug)] #[bitwarden_error] struct SimpleStruct; + impl ToString for SimpleStruct { + fn to_string(&self) -> String { + format!("{:?}", self) + } + } + let simple = SimpleStruct; assert_eq!(simple.error_variant(), "SimpleStruct"); @@ -50,6 +71,7 @@ fn variant_for_struct() { #[test] fn variant_names_for_enum() { #[allow(dead_code)] + #[derive(Debug)] #[bitwarden_error] enum SimpleError { Foo, @@ -57,6 +79,12 @@ fn variant_names_for_enum() { Baz, } + impl ToString for SimpleError { + fn to_string(&self) -> String { + format!("{:?}", self) + } + } + assert_eq!( SimpleError::error_variants(), &["SimpleError::Foo", "SimpleError::Bar", "SimpleError::Baz"] @@ -65,8 +93,15 @@ fn variant_names_for_enum() { #[test] fn variant_names_for_struct() { + #[derive(Debug)] #[bitwarden_error] struct SimpleStruct; + impl ToString for SimpleStruct { + fn to_string(&self) -> String { + format!("{:?}", self) + } + } + assert_eq!(SimpleStruct::error_variants(), &["SimpleStruct"]); } diff --git a/crates/bitwarden-wasm-internal/Cargo.toml b/crates/bitwarden-wasm-internal/Cargo.toml index 33f8128e..41639d10 100644 --- a/crates/bitwarden-wasm-internal/Cargo.toml +++ b/crates/bitwarden-wasm-internal/Cargo.toml @@ -17,6 +17,7 @@ crate-type = ["cdylib"] [dependencies] bitwarden-core = { workspace = true, features = ["wasm", "internal"] } bitwarden-crypto = { workspace = true, features = ["wasm"] } +bitwarden-error = { version = "1.0.0", path = "../bitwarden-error" } bitwarden-vault = { workspace = true, features = ["wasm"] } console_error_panic_hook = "0.1.7" console_log = { version = "1.0.0", features = ["color"] } diff --git a/crates/bitwarden-wasm-internal/src/client.rs b/crates/bitwarden-wasm-internal/src/client.rs index a824b427..1b057c71 100644 --- a/crates/bitwarden-wasm-internal/src/client.rs +++ b/crates/bitwarden-wasm-internal/src/client.rs @@ -5,7 +5,7 @@ use bitwarden_core::{Client, ClientSettings}; use log::{set_max_level, Level}; use wasm_bindgen::prelude::*; -use crate::{vault::ClientVault, ClientCrypto}; +use crate::{error::WasmError, vault::ClientVault, ClientCrypto}; #[wasm_bindgen] pub enum LogLevel { @@ -53,8 +53,19 @@ impl BitwardenClient { env!("SDK_VERSION").to_owned() } - pub fn throw(&self, msg: String) -> Result<(), crate::error::GenericError> { - Err(crate::error::GenericError(msg)) + pub async fn throw(&self, msg: String) -> Result<(), WasmError> { + use bitwarden_error::prelude::*; + + #[bitwarden_error] + struct TestError(String); + + impl ToString for TestError { + fn to_string(&self) -> String { + self.0.clone() + } + } + + Err(TestError(msg).into()) } /// Test method, calls http endpoint diff --git a/crates/bitwarden-wasm-internal/src/crypto.rs b/crates/bitwarden-wasm-internal/src/crypto.rs index f1b58196..7af788dc 100644 --- a/crates/bitwarden-wasm-internal/src/crypto.rs +++ b/crates/bitwarden-wasm-internal/src/crypto.rs @@ -21,8 +21,16 @@ impl ClientCrypto { impl ClientCrypto { /// Initialization method for the user crypto. Needs to be called before any other crypto /// operations. - pub async fn initialize_user_crypto(&self, req: InitUserCryptoRequest) -> Result<()> { - Ok(self.0.crypto().initialize_user_crypto(req).await?) + pub async fn initialize_user_crypto( + &self, + req: InitUserCryptoRequest, + ) -> Result<(), crate::error::WasmError> { + Ok(self + .0 + .crypto() + .initialize_user_crypto(req) + .await + .map_err(|e| crate::error::WasmError::from(e))?) } /// Initialization method for the organization crypto. Needs to be called after diff --git a/crates/bitwarden-wasm-internal/src/error.rs b/crates/bitwarden-wasm-internal/src/error.rs index 237ee494..17bfc63b 100644 --- a/crates/bitwarden-wasm-internal/src/error.rs +++ b/crates/bitwarden-wasm-internal/src/error.rs @@ -1,3 +1,4 @@ +use bitwarden_error::BitwardenError; use wasm_bindgen::prelude::*; // Importing an error class defined in JavaScript instead of defining it in Rust @@ -9,10 +10,47 @@ extern "C" { #[wasm_bindgen(constructor, js_class = Error)] fn new(message: String) -> JsError; + + #[wasm_bindgen(method, getter, structural)] + fn name(this: &JsError) -> String; + + #[wasm_bindgen(method, setter, structural)] + fn set_name(this: &JsError, name: String); + + #[wasm_bindgen(method, getter, structural)] + fn variant(this: &JsError) -> String; + + #[wasm_bindgen(method, setter, structural)] + fn set_variant(this: &JsError, variant: String); } pub type Result = std::result::Result; +pub struct WasmError { + pub message: String, + pub name: String, + pub variant: String, +} + +impl From for WasmError { + fn from(error: T) -> Self { + WasmError { + message: error.to_string(), + name: std::any::type_name::().to_owned(), + variant: error.error_variant().to_owned(), + } + } +} + +impl From for JsValue { + fn from(error: WasmError) -> Self { + let js_error = JsError::new(error.message); + js_error.set_name(error.name); + js_error.set_variant(error.variant); + js_error.into() + } +} + pub struct GenericError(pub String); impl From for GenericError { From eaf33e06ee1b69c21867bddaf0c110dee176d897 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Wed, 6 Nov 2024 17:02:57 +0100 Subject: [PATCH 14/53] chore: add proc-macro2 as dependency --- Cargo.lock | 5 +++-- crates/bitwarden-error-macro/Cargo.toml | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2a81da4d..7ba48dfe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -425,6 +425,7 @@ dependencies = [ name = "bitwarden-error-macro" version = "1.0.0" dependencies = [ + "proc-macro2", "quote", "syn 2.0.87", ] @@ -2432,9 +2433,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.88" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] diff --git a/crates/bitwarden-error-macro/Cargo.toml b/crates/bitwarden-error-macro/Cargo.toml index 01c38a00..11c1557a 100644 --- a/crates/bitwarden-error-macro/Cargo.toml +++ b/crates/bitwarden-error-macro/Cargo.toml @@ -10,6 +10,7 @@ license-file.workspace = true keywords.workspace = true [dependencies] +proc-macro2 = "1.0.89" quote = "1.0.37" syn = "2.0.87" From 9d81fd822cb2071bff29bd825aa9d88c840ee17e Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Thu, 7 Nov 2024 08:56:50 +0100 Subject: [PATCH 15/53] chore: clean up unused file --- crates/bitwarden-error-macro/src/lib.rs | 2 -- crates/bitwarden-error-macro/src/metadata.rs | 1 - 2 files changed, 3 deletions(-) delete mode 100644 crates/bitwarden-error-macro/src/metadata.rs diff --git a/crates/bitwarden-error-macro/src/lib.rs b/crates/bitwarden-error-macro/src/lib.rs index 397099c2..80cfe663 100644 --- a/crates/bitwarden-error-macro/src/lib.rs +++ b/crates/bitwarden-error-macro/src/lib.rs @@ -1,5 +1,3 @@ -mod metadata; - use quote::quote; use syn::Data; diff --git a/crates/bitwarden-error-macro/src/metadata.rs b/crates/bitwarden-error-macro/src/metadata.rs deleted file mode 100644 index 8b137891..00000000 --- a/crates/bitwarden-error-macro/src/metadata.rs +++ /dev/null @@ -1 +0,0 @@ - From e3aa732df4eeeb2a7654a8dcad54eec72f83ba1e Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Thu, 7 Nov 2024 10:51:37 +0100 Subject: [PATCH 16/53] feat: generate typescript types this won't fully work because the TS types will be appended into crates that won't necessarily have WASM enabled --- Cargo.lock | 1 + crates/bitwarden-error-macro/Cargo.toml | 3 ++ crates/bitwarden-error-macro/src/lib.rs | 58 ++++++++++++++++++++----- crates/bitwarden-error/Cargo.toml | 4 ++ crates/bitwarden-error/src/lib.rs | 3 ++ crates/bitwarden-error/src/variant.rs | 1 - crates/bitwarden-error/tests/mod.rs | 24 +++++++--- 7 files changed, 76 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7ba48dfe..aa5114aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -419,6 +419,7 @@ name = "bitwarden-error" version = "1.0.0" dependencies = [ "bitwarden-error-macro", + "wasm-bindgen", ] [[package]] diff --git a/crates/bitwarden-error-macro/Cargo.toml b/crates/bitwarden-error-macro/Cargo.toml index 11c1557a..79268153 100644 --- a/crates/bitwarden-error-macro/Cargo.toml +++ b/crates/bitwarden-error-macro/Cargo.toml @@ -9,6 +9,9 @@ repository.workspace = true license-file.workspace = true keywords.workspace = true +[features] +wasm = [] + [dependencies] proc-macro2 = "1.0.89" quote = "1.0.37" diff --git a/crates/bitwarden-error-macro/src/lib.rs b/crates/bitwarden-error-macro/src/lib.rs index 80cfe663..28e654f4 100644 --- a/crates/bitwarden-error-macro/src/lib.rs +++ b/crates/bitwarden-error-macro/src/lib.rs @@ -25,10 +25,11 @@ pub fn error_variant(item: proc_macro::TokenStream) -> proc_macro::TokenStream { match &input.data { Data::Enum(data) => { - let variant_names = data.variants.iter().map(|variant| { - let variant_ident = &variant.ident; - format!("{}::{}", type_identifier, variant_ident) - }); + let variant_names = data.variants.iter().map(|variant| &variant.ident); + // let variant_names = data.variants.iter().map(|variant| { + // let variant_ident = &variant.ident; + // format!("{}::{}", type_identifier, variant_ident) + // }); let match_arms = data.variants.iter().map(|variant| match variant.fields { syn::Fields::Unit => { let variant_ident = &variant.ident; @@ -53,13 +54,13 @@ pub fn error_variant(item: proc_macro::TokenStream) -> proc_macro::TokenStream { } }); + let types = types(&type_identifier, &variant_names.collect::>()); + quote! { + #types + #[automatically_derived] impl ErrorVariant for #type_identifier { - fn error_variants() -> &'static [&'static str] { - &[#(#variant_names), *] - } - fn error_variant(&self) -> &'static str { match &self { #(#match_arms), * @@ -76,10 +77,6 @@ pub fn error_variant(item: proc_macro::TokenStream) -> proc_macro::TokenStream { quote! { #[automatically_derived] impl ErrorVariant for #type_identifier { - fn error_variants() -> &'static [&'static str] { - &[#variant_name] - } - fn error_variant(&self) -> &'static str { #variant_name } @@ -94,3 +91,40 @@ pub fn error_variant(item: proc_macro::TokenStream) -> proc_macro::TokenStream { } } } + +#[cfg(feature = "wasm")] +fn types( + type_identifier: &proc_macro2::Ident, + variant_names: &[&proc_macro2::Ident], +) -> proc_macro2::TokenStream { + let ts_identifier = quote::format_ident!("TS_TYPES_{}", type_identifier); + let ts_code_str = format!( + r##"r#" + export interface {} extends Error {{ + name: "{}"; + variant: {}; + }}; + "#"##, + type_identifier, + type_identifier, + variant_names + .iter() + .map(|vn| format!(r#""{vn}""#)) + .collect::>() + .join("|") + ); + let ts_code: proc_macro2::TokenStream = ts_code_str.parse().unwrap(); + + quote! { + #[wasm_bindgen(typescript_custom_section)] + const #ts_identifier: &'static str = #ts_code; + } +} + +#[cfg(not(feature = "wasm"))] +fn types( + type_identifier: &proc_macro2::Ident, + variant_names: &[&proc_macro2::Ident], +) -> proc_macro2::TokenStream { + quote! {} +} diff --git a/crates/bitwarden-error/Cargo.toml b/crates/bitwarden-error/Cargo.toml index 4a682d61..91b54fb3 100644 --- a/crates/bitwarden-error/Cargo.toml +++ b/crates/bitwarden-error/Cargo.toml @@ -9,8 +9,12 @@ repository.workspace = true license-file.workspace = true keywords.workspace = true +[features] +wasm = ["bitwarden-error-macro/wasm", "wasm-bindgen"] + [dependencies] bitwarden-error-macro = { version = "1.0.0", path = "../bitwarden-error-macro" } +wasm-bindgen = { workspace = true, optional = true } [lints] workspace = true diff --git a/crates/bitwarden-error/src/lib.rs b/crates/bitwarden-error/src/lib.rs index 425aee70..35ecc056 100644 --- a/crates/bitwarden-error/src/lib.rs +++ b/crates/bitwarden-error/src/lib.rs @@ -4,6 +4,9 @@ pub mod prelude { pub use crate::variant::ErrorVariant; pub use crate::BitwardenError; pub use bitwarden_error_macro::*; + + #[cfg(feature = "wasm")] + pub use wasm_bindgen::prelude::*; } pub trait BitwardenError: variant::ErrorVariant + ToString {} diff --git a/crates/bitwarden-error/src/variant.rs b/crates/bitwarden-error/src/variant.rs index a27973ed..8017d0c0 100644 --- a/crates/bitwarden-error/src/variant.rs +++ b/crates/bitwarden-error/src/variant.rs @@ -1,4 +1,3 @@ pub trait ErrorVariant { - fn error_variants() -> &'static [&'static str]; fn error_variant(&self) -> &'static str; } diff --git a/crates/bitwarden-error/tests/mod.rs b/crates/bitwarden-error/tests/mod.rs index ac7300cf..012734fd 100644 --- a/crates/bitwarden-error/tests/mod.rs +++ b/crates/bitwarden-error/tests/mod.rs @@ -69,6 +69,7 @@ fn variant_for_struct() { } #[test] +#[cfg(feature = "wasm")] fn variant_names_for_enum() { #[allow(dead_code)] #[derive(Debug)] @@ -85,14 +86,20 @@ fn variant_names_for_enum() { } } - assert_eq!( - SimpleError::error_variants(), - &["SimpleError::Foo", "SimpleError::Bar", "SimpleError::Baz"] - ); + // TODO: Not sure how to test this yet + // let types = TS_TYPES_SimpleError; + // assert_eq!( + // types, + // r#" + // export const TS_TYPES_SimpleError = ""; + // "# + // ); } #[test] +#[cfg(feature = "wasm")] fn variant_names_for_struct() { + #[allow(dead_code)] #[derive(Debug)] #[bitwarden_error] struct SimpleStruct; @@ -103,5 +110,12 @@ fn variant_names_for_struct() { } } - assert_eq!(SimpleStruct::error_variants(), &["SimpleStruct"]); + // TODO: Not sure how to test this yet + // let types = TS_TYPES_SimpleStruct; + // assert_eq!( + // types, + // r#" + // export const TS_TYPES_SimpleStruct = ""; + // "# + // ); } From fd86a64a9732c14fd0023ce77baa30423e0e7b1c Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Thu, 7 Nov 2024 13:37:15 +0100 Subject: [PATCH 17/53] chore: add missing dependencies --- Cargo.toml | 2 ++ crates/bitwarden-core/Cargo.toml | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 485747ad..ca5d6c1f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,8 @@ bitwarden-generators = { path = "crates/bitwarden-generators", version = "=1.0.0 bitwarden-send = { path = "crates/bitwarden-send", version = "=1.0.0" } bitwarden-sm = { path = "bitwarden_license/bitwarden-sm", version = "=1.0.0" } bitwarden-vault = { path = "crates/bitwarden-vault", version = "=1.0.0" } +bitwarden-error = { path = "crates/bitwarden-error", version = "=1.0.0" } +bitwarden-error-macro = { path = "crates/bitwarden-error-macro", version = "=1.0.0" } # External crates that are expected to maintain a consistent version across all crates chrono = { version = ">=0.4.26, <0.5", features = [ diff --git a/crates/bitwarden-core/Cargo.toml b/crates/bitwarden-core/Cargo.toml index d2cf7d28..fe1c6a1c 100644 --- a/crates/bitwarden-core/Cargo.toml +++ b/crates/bitwarden-core/Cargo.toml @@ -20,7 +20,11 @@ no-memory-hardening = [ ] # Disable memory hardening features uniffi = ["bitwarden-crypto/uniffi", "dep:uniffi"] # Uniffi bindings secrets = [] # Secrets manager API -wasm = ["dep:wasm-bindgen", "dep:tsify-next"] # WASM support +wasm = [ + "bitwarden-error/wasm", + "dep:wasm-bindgen", + "dep:tsify-next", +] # WASM support [dependencies] base64 = ">=0.22.1, <0.23" @@ -49,7 +53,7 @@ wasm-bindgen = { workspace = true, optional = true } zeroize = { version = ">=1.7.0, <2.0", features = ["derive", "aarch64"] } zxcvbn = { version = ">=3.0.1, <4.0", optional = true } tsify-next = { workspace = true, optional = true } -bitwarden-error = { version = "1.0.0", path = "../bitwarden-error" } +bitwarden-error = { workspace = true } [target.'cfg(not(target_arch="wasm32"))'.dependencies] # By default, we use rustls as the TLS stack and rust-platform-verifier to support user-installed root certificates From e4be0ee11b93e31f028f1bd4c78882cc72ef8c43 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Fri, 8 Nov 2024 10:15:13 +0100 Subject: [PATCH 18/53] refactor: rename variant to flat error --- crates/bitwarden-error-macro/src/lib.rs | 10 +++++----- .../bitwarden-error/src/{variant.rs => flat_error.rs} | 2 +- crates/bitwarden-error/src/lib.rs | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) rename crates/bitwarden-error/src/{variant.rs => flat_error.rs} (65%) diff --git a/crates/bitwarden-error-macro/src/lib.rs b/crates/bitwarden-error-macro/src/lib.rs index 28e654f4..05584066 100644 --- a/crates/bitwarden-error-macro/src/lib.rs +++ b/crates/bitwarden-error-macro/src/lib.rs @@ -10,7 +10,7 @@ pub fn bitwarden_error( let type_identifier = &input.ident; quote! { - #[derive(ErrorVariant)] + #[derive(FlatError)] #input impl BitwardenError for #type_identifier {} @@ -18,8 +18,8 @@ pub fn bitwarden_error( .into() } -#[proc_macro_derive(ErrorVariant)] -pub fn error_variant(item: proc_macro::TokenStream) -> proc_macro::TokenStream { +#[proc_macro_derive(FlatError)] +pub fn flat_error(item: proc_macro::TokenStream) -> proc_macro::TokenStream { let input = syn::parse_macro_input!(item as syn::DeriveInput); let type_identifier = &input.ident; @@ -60,7 +60,7 @@ pub fn error_variant(item: proc_macro::TokenStream) -> proc_macro::TokenStream { #types #[automatically_derived] - impl ErrorVariant for #type_identifier { + impl FlatError for #type_identifier { fn error_variant(&self) -> &'static str { match &self { #(#match_arms), * @@ -76,7 +76,7 @@ pub fn error_variant(item: proc_macro::TokenStream) -> proc_macro::TokenStream { quote! { #[automatically_derived] - impl ErrorVariant for #type_identifier { + impl FlatError for #type_identifier { fn error_variant(&self) -> &'static str { #variant_name } diff --git a/crates/bitwarden-error/src/variant.rs b/crates/bitwarden-error/src/flat_error.rs similarity index 65% rename from crates/bitwarden-error/src/variant.rs rename to crates/bitwarden-error/src/flat_error.rs index 8017d0c0..78c19dc3 100644 --- a/crates/bitwarden-error/src/variant.rs +++ b/crates/bitwarden-error/src/flat_error.rs @@ -1,3 +1,3 @@ -pub trait ErrorVariant { +pub trait FlatError { fn error_variant(&self) -> &'static str; } diff --git a/crates/bitwarden-error/src/lib.rs b/crates/bitwarden-error/src/lib.rs index 35ecc056..b2a1aea8 100644 --- a/crates/bitwarden-error/src/lib.rs +++ b/crates/bitwarden-error/src/lib.rs @@ -1,7 +1,7 @@ -pub mod variant; +pub mod flat_error; pub mod prelude { - pub use crate::variant::ErrorVariant; + pub use crate::flat_error::FlatError; pub use crate::BitwardenError; pub use bitwarden_error_macro::*; @@ -9,4 +9,4 @@ pub mod prelude { pub use wasm_bindgen::prelude::*; } -pub trait BitwardenError: variant::ErrorVariant + ToString {} +pub trait BitwardenError: flat_error::FlatError + ToString {} From e9087d3968f88260b3055d3434d5cf9d2ff9ccf3 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Fri, 8 Nov 2024 11:30:36 +0100 Subject: [PATCH 19/53] feat: add macro type attribute --- Cargo.lock | 1 + .../src/client/encryption_settings.rs | 2 +- crates/bitwarden-error-macro/Cargo.toml | 1 + crates/bitwarden-error-macro/src/lib.rs | 38 ++++++++++++++++++- crates/bitwarden-error/tests/mod.rs | 10 ++--- crates/bitwarden-wasm-internal/src/client.rs | 2 +- 6 files changed, 46 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aa5114aa..075e39b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -426,6 +426,7 @@ dependencies = [ name = "bitwarden-error-macro" version = "1.0.0" dependencies = [ + "darling", "proc-macro2", "quote", "syn 2.0.87", diff --git a/crates/bitwarden-core/src/client/encryption_settings.rs b/crates/bitwarden-core/src/client/encryption_settings.rs index fc8c87b8..467ad61c 100644 --- a/crates/bitwarden-core/src/client/encryption_settings.rs +++ b/crates/bitwarden-core/src/client/encryption_settings.rs @@ -11,7 +11,7 @@ use uuid::Uuid; use crate::error::Result; use crate::VaultLocked; -#[bitwarden_error] +#[bitwarden_error(flat)] #[derive(Debug, Error)] pub enum EncryptionSettingsError { #[error("Cryptography error, {0}")] diff --git a/crates/bitwarden-error-macro/Cargo.toml b/crates/bitwarden-error-macro/Cargo.toml index 79268153..8fed98b3 100644 --- a/crates/bitwarden-error-macro/Cargo.toml +++ b/crates/bitwarden-error-macro/Cargo.toml @@ -13,6 +13,7 @@ keywords.workspace = true wasm = [] [dependencies] +darling = "0.20.10" proc-macro2 = "1.0.89" quote = "1.0.37" syn = "2.0.87" diff --git a/crates/bitwarden-error-macro/src/lib.rs b/crates/bitwarden-error-macro/src/lib.rs index 05584066..e48aee36 100644 --- a/crates/bitwarden-error-macro/src/lib.rs +++ b/crates/bitwarden-error-macro/src/lib.rs @@ -1,11 +1,47 @@ +use darling::{ast::NestedMeta, FromMeta}; use quote::quote; use syn::Data; +#[derive(FromMeta)] +struct BitwardenErrorArgs { + #[darling(flatten)] + error_type: BitwardenErrorType, +} + +#[derive(FromMeta)] +enum BitwardenErrorType { + /// The error is going to be converted into a string using the `ToString` trait + #[darling(rename = "basic")] + Basic, + + /// The error is going to be converted into a flat error using the `FlatError` trait + #[darling(rename = "flat")] + Flat, + + /// The entire error stack is going to be made available using `serde` + #[darling(rename = "basic")] + Full, +} + #[proc_macro_attribute] pub fn bitwarden_error( - _args: proc_macro::TokenStream, + args: proc_macro::TokenStream, item: proc_macro::TokenStream, ) -> proc_macro::TokenStream { + let attr_args = match NestedMeta::parse_meta_list(args.into()) { + Ok(v) => v, + Err(e) => { + return proc_macro::TokenStream::from(darling::Error::from(e).write_errors()); + } + }; + + let BitwardenErrorArgs { error_type } = match BitwardenErrorArgs::from_list(&attr_args) { + Ok(params) => params, + Err(error) => { + return proc_macro::TokenStream::from(darling::Error::from(error).write_errors()); + } + }; + let input = syn::parse_macro_input!(item as syn::DeriveInput); let type_identifier = &input.ident; diff --git a/crates/bitwarden-error/tests/mod.rs b/crates/bitwarden-error/tests/mod.rs index 012734fd..710ef564 100644 --- a/crates/bitwarden-error/tests/mod.rs +++ b/crates/bitwarden-error/tests/mod.rs @@ -3,7 +3,7 @@ use bitwarden_error::prelude::*; #[test] fn variant_for_basic_enum() { #[derive(Debug)] - #[bitwarden_error] + #[bitwarden_error(flat)] enum SimpleError { Foo, Bar, @@ -29,7 +29,7 @@ fn variant_for_basic_enum() { fn variant_for_enum_with_fields() { #[allow(dead_code)] #[derive(Debug)] - #[bitwarden_error] + #[bitwarden_error(flat)] enum ComplexError { Foo(String), Bar { x: i32, y: i32 }, @@ -54,7 +54,7 @@ fn variant_for_enum_with_fields() { #[test] fn variant_for_struct() { #[derive(Debug)] - #[bitwarden_error] + #[bitwarden_error(flat)] struct SimpleStruct; impl ToString for SimpleStruct { @@ -73,7 +73,7 @@ fn variant_for_struct() { fn variant_names_for_enum() { #[allow(dead_code)] #[derive(Debug)] - #[bitwarden_error] + #[bitwarden_error(flat)] enum SimpleError { Foo, Bar, @@ -101,7 +101,7 @@ fn variant_names_for_enum() { fn variant_names_for_struct() { #[allow(dead_code)] #[derive(Debug)] - #[bitwarden_error] + #[bitwarden_error(flat)] struct SimpleStruct; impl ToString for SimpleStruct { diff --git a/crates/bitwarden-wasm-internal/src/client.rs b/crates/bitwarden-wasm-internal/src/client.rs index 1b057c71..08b21420 100644 --- a/crates/bitwarden-wasm-internal/src/client.rs +++ b/crates/bitwarden-wasm-internal/src/client.rs @@ -56,7 +56,7 @@ impl BitwardenClient { pub async fn throw(&self, msg: String) -> Result<(), WasmError> { use bitwarden_error::prelude::*; - #[bitwarden_error] + #[bitwarden_error(flat)] struct TestError(String); impl ToString for TestError { From 4fedaac07ec0766a56bbd56f4570faf218e3c12b Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Fri, 8 Nov 2024 14:02:40 +0100 Subject: [PATCH 20/53] feat: add basic (ToString) error support --- Cargo.lock | 43 ++++++++ crates/bitwarden-error-macro/src/lib.rs | 56 +++++++++- crates/bitwarden-error/.cargo/config | 2 + crates/bitwarden-error/Cargo.toml | 3 + crates/bitwarden-error/src/basic_error.rs | 1 + crates/bitwarden-error/src/lib.rs | 7 +- crates/bitwarden-error/src/wasm.rs | 25 +++++ crates/bitwarden-error/tests/basic.rs | 21 ++++ crates/bitwarden-error/tests/flat.rs | 121 +++++++++++++++++++++ crates/bitwarden-error/tests/mod.rs | 123 +--------------------- 10 files changed, 276 insertions(+), 126 deletions(-) create mode 100644 crates/bitwarden-error/.cargo/config create mode 100644 crates/bitwarden-error/src/basic_error.rs create mode 100644 crates/bitwarden-error/src/wasm.rs create mode 100644 crates/bitwarden-error/tests/basic.rs create mode 100644 crates/bitwarden-error/tests/flat.rs diff --git a/Cargo.lock b/Cargo.lock index 075e39b6..fc95c9b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -420,6 +420,7 @@ version = "1.0.0" dependencies = [ "bitwarden-error-macro", "wasm-bindgen", + "wasm-bindgen-test", ] [[package]] @@ -2003,6 +2004,16 @@ dependencies = [ "unicase", ] +[[package]] +name = "minicov" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27fe9f1cc3c22e1687f9446c2083c4c5fc7f0bcf1c7a86bdbded14985895b4b" +dependencies = [ + "cc", + "walkdir", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -2841,6 +2852,12 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "scopeguard" version = "1.2.0" @@ -3870,6 +3887,32 @@ version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +[[package]] +name = "wasm-bindgen-test" +version = "0.3.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d381749acb0943d357dcbd8f0b100640679883fcdeeef04def49daf8d33a5426" +dependencies = [ + "console_error_panic_hook", + "js-sys", + "minicov", + "scoped-tls", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test-macro", +] + +[[package]] +name = "wasm-bindgen-test-macro" +version = "0.3.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c97b2ef2c8d627381e51c071c2ab328eac606d3f69dd82bcbca20a9e389d95f0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "web-sys" version = "0.3.72" diff --git a/crates/bitwarden-error-macro/src/lib.rs b/crates/bitwarden-error-macro/src/lib.rs index e48aee36..0249c490 100644 --- a/crates/bitwarden-error-macro/src/lib.rs +++ b/crates/bitwarden-error-macro/src/lib.rs @@ -43,6 +43,22 @@ pub fn bitwarden_error( }; let input = syn::parse_macro_input!(item as syn::DeriveInput); + match error_type { + BitwardenErrorType::Basic => basic_error_impl(&input), + BitwardenErrorType::Flat => flat_error_impl(&input), + BitwardenErrorType::Full => full_error_impl(&input), + } +} + +fn basic_error_impl(input: &syn::DeriveInput) -> proc_macro::TokenStream { + quote! { + #[derive(BasicError)] + #input + } + .into() +} + +fn flat_error_impl(input: &syn::DeriveInput) -> proc_macro::TokenStream { let type_identifier = &input.ident; quote! { @@ -54,6 +70,41 @@ pub fn bitwarden_error( .into() } +fn full_error_impl(input: &syn::DeriveInput) -> proc_macro::TokenStream { + quote! {}.into() +} + +#[proc_macro_derive(BasicError)] +pub fn basic_error(item: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = syn::parse_macro_input!(item as syn::DeriveInput); + let type_identifier = &input.ident; + + let js_value = basic_error_js_value(&type_identifier); + quote! { + #js_value + } + .into() +} + +#[cfg(feature = "wasm")] +fn basic_error_js_value(type_identifier: &proc_macro2::Ident) -> proc_macro2::TokenStream { + quote! { + #[automatically_derived] + impl From<#type_identifier> for JsValue { + fn from(error: #type_identifier) -> Self { + let js_error = JsError::new(error.to_string()); + js_error.set_name(stringify!(#type_identifier).to_owned()); + js_error.into() + } + } + } +} + +#[cfg(not(feature = "wasm"))] +fn basic_error_js_value(type_identifier: &proc_macro2::Ident) -> proc_macro2::TokenStream { + quote! {} +} + #[proc_macro_derive(FlatError)] pub fn flat_error(item: proc_macro::TokenStream) -> proc_macro::TokenStream { let input = syn::parse_macro_input!(item as syn::DeriveInput); @@ -62,10 +113,7 @@ pub fn flat_error(item: proc_macro::TokenStream) -> proc_macro::TokenStream { match &input.data { Data::Enum(data) => { let variant_names = data.variants.iter().map(|variant| &variant.ident); - // let variant_names = data.variants.iter().map(|variant| { - // let variant_ident = &variant.ident; - // format!("{}::{}", type_identifier, variant_ident) - // }); + let match_arms = data.variants.iter().map(|variant| match variant.fields { syn::Fields::Unit => { let variant_ident = &variant.ident; diff --git a/crates/bitwarden-error/.cargo/config b/crates/bitwarden-error/.cargo/config new file mode 100644 index 00000000..4ec2f3b8 --- /dev/null +++ b/crates/bitwarden-error/.cargo/config @@ -0,0 +1,2 @@ +[target.wasm32-unknown-unknown] +runner = 'wasm-bindgen-test-runner' diff --git a/crates/bitwarden-error/Cargo.toml b/crates/bitwarden-error/Cargo.toml index 91b54fb3..e3a727c0 100644 --- a/crates/bitwarden-error/Cargo.toml +++ b/crates/bitwarden-error/Cargo.toml @@ -18,3 +18,6 @@ wasm-bindgen = { workspace = true, optional = true } [lints] workspace = true + +[dev-dependencies] +wasm-bindgen-test = "0.3.45" diff --git a/crates/bitwarden-error/src/basic_error.rs b/crates/bitwarden-error/src/basic_error.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/crates/bitwarden-error/src/basic_error.rs @@ -0,0 +1 @@ + diff --git a/crates/bitwarden-error/src/lib.rs b/crates/bitwarden-error/src/lib.rs index b2a1aea8..edbdf29d 100644 --- a/crates/bitwarden-error/src/lib.rs +++ b/crates/bitwarden-error/src/lib.rs @@ -1,12 +1,17 @@ +pub mod basic_error; pub mod flat_error; +#[cfg(feature = "wasm")] +pub mod wasm; + pub mod prelude { + pub use crate::basic_error; pub use crate::flat_error::FlatError; pub use crate::BitwardenError; pub use bitwarden_error_macro::*; #[cfg(feature = "wasm")] - pub use wasm_bindgen::prelude::*; + pub use {crate::wasm::JsError, wasm_bindgen::prelude::*}; } pub trait BitwardenError: flat_error::FlatError + ToString {} diff --git a/crates/bitwarden-error/src/wasm.rs b/crates/bitwarden-error/src/wasm.rs new file mode 100644 index 00000000..f39eba98 --- /dev/null +++ b/crates/bitwarden-error/src/wasm.rs @@ -0,0 +1,25 @@ +use wasm_bindgen::prelude::*; + +#[cfg_attr(feature = "wasm", wasm_bindgen)] +extern "C" { + #[wasm_bindgen(js_name = Error)] + pub type JsError; + + #[wasm_bindgen(constructor, js_class = Error)] + pub fn new(message: String) -> JsError; + + #[wasm_bindgen(method, getter, structural)] + pub fn message(this: &JsError) -> String; + + #[wasm_bindgen(method, getter, structural)] + pub fn name(this: &JsError) -> String; + + #[wasm_bindgen(method, setter, structural)] + pub fn set_name(this: &JsError, name: String); + + #[wasm_bindgen(method, getter, structural)] + pub fn variant(this: &JsError) -> String; + + #[wasm_bindgen(method, setter, structural)] + pub fn set_variant(this: &JsError, variant: String); +} diff --git a/crates/bitwarden-error/tests/basic.rs b/crates/bitwarden-error/tests/basic.rs new file mode 100644 index 00000000..bd455dc4 --- /dev/null +++ b/crates/bitwarden-error/tests/basic.rs @@ -0,0 +1,21 @@ +use bitwarden_error::prelude::*; +use wasm_bindgen_test::*; + +#[wasm_bindgen_test] +#[cfg(feature = "wasm")] +fn converts_to_js_error_using_to_string() { + #[derive(Debug, BasicError)] + struct SomeError; + impl ToString for SomeError { + fn to_string(&self) -> String { + "This is an error".to_string() + } + } + + let simple = SomeError; + let js_value: JsValue = simple.into(); + + let js_error = JsError::from(js_value); + assert_eq!(js_error.name(), "SomeError"); + assert_eq!(js_error.message(), "This is an error"); +} diff --git a/crates/bitwarden-error/tests/flat.rs b/crates/bitwarden-error/tests/flat.rs new file mode 100644 index 00000000..710ef564 --- /dev/null +++ b/crates/bitwarden-error/tests/flat.rs @@ -0,0 +1,121 @@ +use bitwarden_error::prelude::*; + +#[test] +fn variant_for_basic_enum() { + #[derive(Debug)] + #[bitwarden_error(flat)] + enum SimpleError { + Foo, + Bar, + Baz, + } + + impl ToString for SimpleError { + fn to_string(&self) -> String { + format!("{:?}", self) + } + } + + let foo = SimpleError::Foo; + let bar = SimpleError::Bar; + let baz = SimpleError::Baz; + + assert_eq!(foo.error_variant(), "SimpleError::Foo"); + assert_eq!(bar.error_variant(), "SimpleError::Bar"); + assert_eq!(baz.error_variant(), "SimpleError::Baz"); +} + +#[test] +fn variant_for_enum_with_fields() { + #[allow(dead_code)] + #[derive(Debug)] + #[bitwarden_error(flat)] + enum ComplexError { + Foo(String), + Bar { x: i32, y: i32 }, + Baz(bool, bool), + } + + impl ToString for ComplexError { + fn to_string(&self) -> String { + format!("{:?}", self) + } + } + + let foo = ComplexError::Foo("hello".to_string()); + let bar = ComplexError::Bar { x: 1, y: 2 }; + let baz = ComplexError::Baz(true, true); + + assert_eq!(foo.error_variant(), "ComplexError::Foo"); + assert_eq!(bar.error_variant(), "ComplexError::Bar"); + assert_eq!(baz.error_variant(), "ComplexError::Baz"); +} + +#[test] +fn variant_for_struct() { + #[derive(Debug)] + #[bitwarden_error(flat)] + struct SimpleStruct; + + impl ToString for SimpleStruct { + fn to_string(&self) -> String { + format!("{:?}", self) + } + } + + let simple = SimpleStruct; + + assert_eq!(simple.error_variant(), "SimpleStruct"); +} + +#[test] +#[cfg(feature = "wasm")] +fn variant_names_for_enum() { + #[allow(dead_code)] + #[derive(Debug)] + #[bitwarden_error(flat)] + enum SimpleError { + Foo, + Bar, + Baz, + } + + impl ToString for SimpleError { + fn to_string(&self) -> String { + format!("{:?}", self) + } + } + + // TODO: Not sure how to test this yet + // let types = TS_TYPES_SimpleError; + // assert_eq!( + // types, + // r#" + // export const TS_TYPES_SimpleError = ""; + // "# + // ); +} + +#[test] +#[cfg(feature = "wasm")] +fn variant_names_for_struct() { + #[allow(dead_code)] + #[derive(Debug)] + #[bitwarden_error(flat)] + struct SimpleStruct; + + impl ToString for SimpleStruct { + fn to_string(&self) -> String { + format!("{:?}", self) + } + } + + // TODO: Not sure how to test this yet + // let types = TS_TYPES_SimpleStruct; + // assert_eq!( + // types, + // r#" + // export const TS_TYPES_SimpleStruct = ""; + // "# + // ); +} diff --git a/crates/bitwarden-error/tests/mod.rs b/crates/bitwarden-error/tests/mod.rs index 710ef564..5c99c69e 100644 --- a/crates/bitwarden-error/tests/mod.rs +++ b/crates/bitwarden-error/tests/mod.rs @@ -1,121 +1,2 @@ -use bitwarden_error::prelude::*; - -#[test] -fn variant_for_basic_enum() { - #[derive(Debug)] - #[bitwarden_error(flat)] - enum SimpleError { - Foo, - Bar, - Baz, - } - - impl ToString for SimpleError { - fn to_string(&self) -> String { - format!("{:?}", self) - } - } - - let foo = SimpleError::Foo; - let bar = SimpleError::Bar; - let baz = SimpleError::Baz; - - assert_eq!(foo.error_variant(), "SimpleError::Foo"); - assert_eq!(bar.error_variant(), "SimpleError::Bar"); - assert_eq!(baz.error_variant(), "SimpleError::Baz"); -} - -#[test] -fn variant_for_enum_with_fields() { - #[allow(dead_code)] - #[derive(Debug)] - #[bitwarden_error(flat)] - enum ComplexError { - Foo(String), - Bar { x: i32, y: i32 }, - Baz(bool, bool), - } - - impl ToString for ComplexError { - fn to_string(&self) -> String { - format!("{:?}", self) - } - } - - let foo = ComplexError::Foo("hello".to_string()); - let bar = ComplexError::Bar { x: 1, y: 2 }; - let baz = ComplexError::Baz(true, true); - - assert_eq!(foo.error_variant(), "ComplexError::Foo"); - assert_eq!(bar.error_variant(), "ComplexError::Bar"); - assert_eq!(baz.error_variant(), "ComplexError::Baz"); -} - -#[test] -fn variant_for_struct() { - #[derive(Debug)] - #[bitwarden_error(flat)] - struct SimpleStruct; - - impl ToString for SimpleStruct { - fn to_string(&self) -> String { - format!("{:?}", self) - } - } - - let simple = SimpleStruct; - - assert_eq!(simple.error_variant(), "SimpleStruct"); -} - -#[test] -#[cfg(feature = "wasm")] -fn variant_names_for_enum() { - #[allow(dead_code)] - #[derive(Debug)] - #[bitwarden_error(flat)] - enum SimpleError { - Foo, - Bar, - Baz, - } - - impl ToString for SimpleError { - fn to_string(&self) -> String { - format!("{:?}", self) - } - } - - // TODO: Not sure how to test this yet - // let types = TS_TYPES_SimpleError; - // assert_eq!( - // types, - // r#" - // export const TS_TYPES_SimpleError = ""; - // "# - // ); -} - -#[test] -#[cfg(feature = "wasm")] -fn variant_names_for_struct() { - #[allow(dead_code)] - #[derive(Debug)] - #[bitwarden_error(flat)] - struct SimpleStruct; - - impl ToString for SimpleStruct { - fn to_string(&self) -> String { - format!("{:?}", self) - } - } - - // TODO: Not sure how to test this yet - // let types = TS_TYPES_SimpleStruct; - // assert_eq!( - // types, - // r#" - // export const TS_TYPES_SimpleStruct = ""; - // "# - // ); -} +mod basic; +mod flat; From c3647ffb8bea1699be5bd910995a31a8abd95056 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Fri, 8 Nov 2024 16:25:21 +0100 Subject: [PATCH 21/53] feat: add native JSValue support to errors using bitwarden_error --- .../src/client/encryption_settings.rs | 2 +- crates/bitwarden-error-macro/src/lib.rs | 14 +++++++++ crates/bitwarden-error/tests/basic.rs | 1 + crates/bitwarden-wasm-internal/src/client.rs | 30 +++++++++---------- crates/bitwarden-wasm-internal/src/crypto.rs | 17 +++++------ crates/bitwarden-wasm-internal/src/error.rs | 26 ---------------- 6 files changed, 38 insertions(+), 52 deletions(-) diff --git a/crates/bitwarden-core/src/client/encryption_settings.rs b/crates/bitwarden-core/src/client/encryption_settings.rs index 467ad61c..1f1d7de6 100644 --- a/crates/bitwarden-core/src/client/encryption_settings.rs +++ b/crates/bitwarden-core/src/client/encryption_settings.rs @@ -11,7 +11,7 @@ use uuid::Uuid; use crate::error::Result; use crate::VaultLocked; -#[bitwarden_error(flat)] +#[bitwarden_error(basic)] #[derive(Debug, Error)] pub enum EncryptionSettingsError { #[error("Cryptography error, {0}")] diff --git a/crates/bitwarden-error-macro/src/lib.rs b/crates/bitwarden-error-macro/src/lib.rs index 0249c490..516ca9af 100644 --- a/crates/bitwarden-error-macro/src/lib.rs +++ b/crates/bitwarden-error-macro/src/lib.rs @@ -88,7 +88,21 @@ pub fn basic_error(item: proc_macro::TokenStream) -> proc_macro::TokenStream { #[cfg(feature = "wasm")] fn basic_error_js_value(type_identifier: &proc_macro2::Ident) -> proc_macro2::TokenStream { + let ts_identifier = quote::format_ident!("TS_TYPES_{}", type_identifier); + let ts_code_str = format!( + r##"r#" + export interface {} extends Error {{ + name: "{}"; + }}; + "#"##, + type_identifier, type_identifier + ); + let ts_code: proc_macro2::TokenStream = ts_code_str.parse().unwrap(); + quote! { + #[wasm_bindgen(typescript_custom_section)] + const #ts_identifier: &'static str = #ts_code; + #[automatically_derived] impl From<#type_identifier> for JsValue { fn from(error: #type_identifier) -> Self { diff --git a/crates/bitwarden-error/tests/basic.rs b/crates/bitwarden-error/tests/basic.rs index bd455dc4..5da811e4 100644 --- a/crates/bitwarden-error/tests/basic.rs +++ b/crates/bitwarden-error/tests/basic.rs @@ -3,6 +3,7 @@ use wasm_bindgen_test::*; #[wasm_bindgen_test] #[cfg(feature = "wasm")] +#[allow(dead_code)] // Not actually dead, but rust-analyzer doesn't understand `wasm_bindgen_test` fn converts_to_js_error_using_to_string() { #[derive(Debug, BasicError)] struct SomeError; diff --git a/crates/bitwarden-wasm-internal/src/client.rs b/crates/bitwarden-wasm-internal/src/client.rs index 08b21420..022bc207 100644 --- a/crates/bitwarden-wasm-internal/src/client.rs +++ b/crates/bitwarden-wasm-internal/src/client.rs @@ -1,11 +1,11 @@ extern crate console_error_panic_hook; -use std::rc::Rc; +use std::{fmt::Display, rc::Rc}; use bitwarden_core::{Client, ClientSettings}; +use bitwarden_error::prelude::*; use log::{set_max_level, Level}; -use wasm_bindgen::prelude::*; -use crate::{error::WasmError, vault::ClientVault, ClientCrypto}; +use crate::{vault::ClientVault, ClientCrypto}; #[wasm_bindgen] pub enum LogLevel { @@ -53,19 +53,8 @@ impl BitwardenClient { env!("SDK_VERSION").to_owned() } - pub async fn throw(&self, msg: String) -> Result<(), WasmError> { - use bitwarden_error::prelude::*; - - #[bitwarden_error(flat)] - struct TestError(String); - - impl ToString for TestError { - fn to_string(&self) -> String { - self.0.clone() - } - } - - Err(TestError(msg).into()) + pub async fn throw(&self, msg: String) -> Result<(), TestError> { + Err(TestError(msg)) } /// Test method, calls http endpoint @@ -84,3 +73,12 @@ impl BitwardenClient { ClientVault::new(self.0.clone()) } } + +#[bitwarden_error(basic)] +pub struct TestError(String); + +impl Display for TestError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} diff --git a/crates/bitwarden-wasm-internal/src/crypto.rs b/crates/bitwarden-wasm-internal/src/crypto.rs index 7af788dc..795024d8 100644 --- a/crates/bitwarden-wasm-internal/src/crypto.rs +++ b/crates/bitwarden-wasm-internal/src/crypto.rs @@ -1,6 +1,7 @@ use std::rc::Rc; use bitwarden_core::{ + client::encryption_settings::EncryptionSettingsError, mobile::crypto::{InitOrgCryptoRequest, InitUserCryptoRequest}, Client, }; @@ -24,18 +25,16 @@ impl ClientCrypto { pub async fn initialize_user_crypto( &self, req: InitUserCryptoRequest, - ) -> Result<(), crate::error::WasmError> { - Ok(self - .0 - .crypto() - .initialize_user_crypto(req) - .await - .map_err(|e| crate::error::WasmError::from(e))?) + ) -> Result<(), EncryptionSettingsError> { + self.0.crypto().initialize_user_crypto(req).await } /// Initialization method for the organization crypto. Needs to be called after /// `initialize_user_crypto` but before any other crypto operations. - pub async fn initialize_org_crypto(&self, req: InitOrgCryptoRequest) -> Result<()> { - Ok(self.0.crypto().initialize_org_crypto(req).await?) + pub async fn initialize_org_crypto( + &self, + req: InitOrgCryptoRequest, + ) -> Result<(), EncryptionSettingsError> { + self.0.crypto().initialize_org_crypto(req).await } } diff --git a/crates/bitwarden-wasm-internal/src/error.rs b/crates/bitwarden-wasm-internal/src/error.rs index 17bfc63b..137ae81b 100644 --- a/crates/bitwarden-wasm-internal/src/error.rs +++ b/crates/bitwarden-wasm-internal/src/error.rs @@ -1,4 +1,3 @@ -use bitwarden_error::BitwardenError; use wasm_bindgen::prelude::*; // Importing an error class defined in JavaScript instead of defining it in Rust @@ -26,31 +25,6 @@ extern "C" { pub type Result = std::result::Result; -pub struct WasmError { - pub message: String, - pub name: String, - pub variant: String, -} - -impl From for WasmError { - fn from(error: T) -> Self { - WasmError { - message: error.to_string(), - name: std::any::type_name::().to_owned(), - variant: error.error_variant().to_owned(), - } - } -} - -impl From for JsValue { - fn from(error: WasmError) -> Self { - let js_error = JsError::new(error.message); - js_error.set_name(error.name); - js_error.set_variant(error.variant); - js_error.into() - } -} - pub struct GenericError(pub String); impl From for GenericError { From 8e8b56bbc635bb4ee5fa945d3e78ec279c935036 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Mon, 11 Nov 2024 10:10:47 +0100 Subject: [PATCH 22/53] feat: add native wasm support to flat errors --- .../src/client/encryption_settings.rs | 2 +- crates/bitwarden-error-macro/src/lib.rs | 32 ++++++++++++------- crates/bitwarden-error/tests/flat.rs | 26 +++++++++++++++ 3 files changed, 48 insertions(+), 12 deletions(-) diff --git a/crates/bitwarden-core/src/client/encryption_settings.rs b/crates/bitwarden-core/src/client/encryption_settings.rs index 1f1d7de6..467ad61c 100644 --- a/crates/bitwarden-core/src/client/encryption_settings.rs +++ b/crates/bitwarden-core/src/client/encryption_settings.rs @@ -11,7 +11,7 @@ use uuid::Uuid; use crate::error::Result; use crate::VaultLocked; -#[bitwarden_error(basic)] +#[bitwarden_error(flat)] #[derive(Debug, Error)] pub enum EncryptionSettingsError { #[error("Cryptography error, {0}")] diff --git a/crates/bitwarden-error-macro/src/lib.rs b/crates/bitwarden-error-macro/src/lib.rs index 516ca9af..e717b2f2 100644 --- a/crates/bitwarden-error-macro/src/lib.rs +++ b/crates/bitwarden-error-macro/src/lib.rs @@ -79,15 +79,15 @@ pub fn basic_error(item: proc_macro::TokenStream) -> proc_macro::TokenStream { let input = syn::parse_macro_input!(item as syn::DeriveInput); let type_identifier = &input.ident; - let js_value = basic_error_js_value(&type_identifier); + let wasm = basic_error_wasm(&type_identifier); quote! { - #js_value + #wasm } .into() } #[cfg(feature = "wasm")] -fn basic_error_js_value(type_identifier: &proc_macro2::Ident) -> proc_macro2::TokenStream { +fn basic_error_wasm(type_identifier: &proc_macro2::Ident) -> proc_macro2::TokenStream { let ts_identifier = quote::format_ident!("TS_TYPES_{}", type_identifier); let ts_code_str = format!( r##"r#" @@ -115,7 +115,7 @@ fn basic_error_js_value(type_identifier: &proc_macro2::Ident) -> proc_macro2::To } #[cfg(not(feature = "wasm"))] -fn basic_error_js_value(type_identifier: &proc_macro2::Ident) -> proc_macro2::TokenStream { +fn basic_error_wasm(type_identifier: &proc_macro2::Ident) -> proc_macro2::TokenStream { quote! {} } @@ -131,31 +131,31 @@ pub fn flat_error(item: proc_macro::TokenStream) -> proc_macro::TokenStream { let match_arms = data.variants.iter().map(|variant| match variant.fields { syn::Fields::Unit => { let variant_ident = &variant.ident; - let variant_name = format!("{}::{}", type_identifier, variant_ident); + let variant_name = format!("{}", variant_ident); quote! { #type_identifier::#variant_ident => #variant_name } } syn::Fields::Named(_) => { let variant_ident = &variant.ident; - let variant_name = format!("{}::{}", type_identifier, variant_ident); + let variant_name = format!("{}", variant_ident); quote! { #type_identifier::#variant_ident { .. } => #variant_name } } syn::Fields::Unnamed(_) => { let variant_ident = &variant.ident; - let variant_name = format!("{}::{}", type_identifier, variant_ident); + let variant_name = format!("{}", variant_ident); quote! { #type_identifier::#variant_ident(..) => #variant_name } } }); - let types = types(&type_identifier, &variant_names.collect::>()); + let wasm = flat_error_wasm(&type_identifier, &variant_names.collect::>()); quote! { - #types + #wasm #[automatically_derived] impl FlatError for #type_identifier { @@ -191,7 +191,7 @@ pub fn flat_error(item: proc_macro::TokenStream) -> proc_macro::TokenStream { } #[cfg(feature = "wasm")] -fn types( +fn flat_error_wasm( type_identifier: &proc_macro2::Ident, variant_names: &[&proc_macro2::Ident], ) -> proc_macro2::TokenStream { @@ -216,11 +216,21 @@ fn types( quote! { #[wasm_bindgen(typescript_custom_section)] const #ts_identifier: &'static str = #ts_code; + + #[automatically_derived] + impl From<#type_identifier> for JsValue { + fn from(error: #type_identifier) -> Self { + let js_error = JsError::new(error.to_string()); + js_error.set_name(stringify!(#type_identifier).to_owned()); + js_error.set_variant(error.error_variant().to_owned()); + js_error.into() + } + } } } #[cfg(not(feature = "wasm"))] -fn types( +fn flat_error_wasm( type_identifier: &proc_macro2::Ident, variant_names: &[&proc_macro2::Ident], ) -> proc_macro2::TokenStream { diff --git a/crates/bitwarden-error/tests/flat.rs b/crates/bitwarden-error/tests/flat.rs index 710ef564..4f2f1480 100644 --- a/crates/bitwarden-error/tests/flat.rs +++ b/crates/bitwarden-error/tests/flat.rs @@ -1,4 +1,5 @@ use bitwarden_error::prelude::*; +use wasm_bindgen_test::*; #[test] fn variant_for_basic_enum() { @@ -119,3 +120,28 @@ fn variant_names_for_struct() { // "# // ); } + +#[wasm_bindgen_test] +#[cfg(feature = "wasm")] +#[allow(dead_code)] // Not actually dead, but rust-analyzer doesn't understand `wasm_bindgen_test` +fn converts_to_js_error_using_to_string() { + #[derive(Debug, FlatError)] + enum SomeError { + Foo, + Bar, + Baz, + } + impl ToString for SomeError { + fn to_string(&self) -> String { + "This is an error".to_string() + } + } + + let simple = SomeError::Baz; + let js_value: JsValue = simple.into(); + + let js_error = JsError::from(js_value); + assert_eq!(js_error.name(), "SomeError"); + assert_eq!(js_error.message(), "This is an error"); + assert_eq!(js_error.variant(), "Baz"); +} From e0132031981808b4ef9f5aa18880a0f3a06a4302 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Mon, 11 Nov 2024 13:16:37 +0100 Subject: [PATCH 23/53] feat: add support for fully serialized errors --- Cargo.lock | 18 ++++++++++++++++++ crates/bitwarden-error-macro/src/lib.rs | 16 ++++++++++++++-- crates/bitwarden-error/Cargo.toml | 3 +++ crates/bitwarden-error/tests/full.rs | 23 +++++++++++++++++++++++ crates/bitwarden-error/tests/mod.rs | 1 + 5 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 crates/bitwarden-error/tests/full.rs diff --git a/Cargo.lock b/Cargo.lock index fc95c9b8..f9dc3610 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -419,6 +419,9 @@ name = "bitwarden-error" version = "1.0.0" dependencies = [ "bitwarden-error-macro", + "js-sys", + "serde", + "tsify-next", "wasm-bindgen", "wasm-bindgen-test", ] @@ -1515,6 +1518,19 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "gloo-utils" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "goblin" version = "0.8.2" @@ -3516,8 +3532,10 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f4a645dca4ee0800f5ab60ce166deba2db6a0315de795a2691e138a3d55d756" dependencies = [ + "gloo-utils", "serde", "serde-wasm-bindgen", + "serde_json", "tsify-next-macros", "wasm-bindgen", ] diff --git a/crates/bitwarden-error-macro/src/lib.rs b/crates/bitwarden-error-macro/src/lib.rs index e717b2f2..4bbab028 100644 --- a/crates/bitwarden-error-macro/src/lib.rs +++ b/crates/bitwarden-error-macro/src/lib.rs @@ -19,7 +19,7 @@ enum BitwardenErrorType { Flat, /// The entire error stack is going to be made available using `serde` - #[darling(rename = "basic")] + #[darling(rename = "full")] Full, } @@ -71,7 +71,19 @@ fn flat_error_impl(input: &syn::DeriveInput) -> proc_macro::TokenStream { } fn full_error_impl(input: &syn::DeriveInput) -> proc_macro::TokenStream { - quote! {}.into() + let wasm_attributes = cfg!(feature = "wasm").then(|| { + quote! { + #[derive(tsify_next::Tsify)] + #[tsify(into_wasm_abi)] + } + }); + + quote! { + #[derive(serde::Serialize)] + #wasm_attributes + #input + } + .into() } #[proc_macro_derive(BasicError)] diff --git a/crates/bitwarden-error/Cargo.toml b/crates/bitwarden-error/Cargo.toml index e3a727c0..7b105a83 100644 --- a/crates/bitwarden-error/Cargo.toml +++ b/crates/bitwarden-error/Cargo.toml @@ -20,4 +20,7 @@ wasm-bindgen = { workspace = true, optional = true } workspace = true [dev-dependencies] +js-sys = "0.3.72" +serde.workspace = true +tsify-next = "0.5.4" wasm-bindgen-test = "0.3.45" diff --git a/crates/bitwarden-error/tests/full.rs b/crates/bitwarden-error/tests/full.rs new file mode 100644 index 00000000..d367c868 --- /dev/null +++ b/crates/bitwarden-error/tests/full.rs @@ -0,0 +1,23 @@ +use bitwarden_error::prelude::*; +use wasm_bindgen_test::*; + +#[allow(dead_code)] +// Not actually dead, but rust-analyzer doesn't understand `wasm_bindgen_test` +#[wasm_bindgen_test] +#[cfg(feature = "wasm")] +// `full` errors are just forwarded to TSify and Serde so this is just a smoke test +fn converts_to_js() { + #[bitwarden_error(full)] + enum SomeError { + Foo(String), + Bar(String), + Baz(String), + } + + let simple = SomeError::Baz("This is an error".to_string()); + let js_value: JsValue = simple.into(); + + // Errors only natively support rust -> js and so we use Reflect to get the value straight from the JSValue + let value = js_sys::Reflect::get(&js_value, &JsValue::from("Baz")).unwrap(); + assert_eq!(value.as_string().unwrap(), "This is an error"); +} diff --git a/crates/bitwarden-error/tests/mod.rs b/crates/bitwarden-error/tests/mod.rs index 5c99c69e..3a3c2621 100644 --- a/crates/bitwarden-error/tests/mod.rs +++ b/crates/bitwarden-error/tests/mod.rs @@ -1,2 +1,3 @@ mod basic; mod flat; +mod full; From eba5023e2895ab6744aac3bf5f7959547a75dd0c Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Mon, 11 Nov 2024 14:39:38 +0100 Subject: [PATCH 24/53] refactor: move basic implementations into separate module --- .../src/basic/attribute.rs | 9 ++++ .../bitwarden-error-macro/src/basic/derive.rs | 39 ++++++++++++++ crates/bitwarden-error-macro/src/basic/mod.rs | 2 + crates/bitwarden-error-macro/src/lib.rs | 54 ++----------------- 4 files changed, 54 insertions(+), 50 deletions(-) create mode 100644 crates/bitwarden-error-macro/src/basic/attribute.rs create mode 100644 crates/bitwarden-error-macro/src/basic/derive.rs create mode 100644 crates/bitwarden-error-macro/src/basic/mod.rs diff --git a/crates/bitwarden-error-macro/src/basic/attribute.rs b/crates/bitwarden-error-macro/src/basic/attribute.rs new file mode 100644 index 00000000..48c20fa1 --- /dev/null +++ b/crates/bitwarden-error-macro/src/basic/attribute.rs @@ -0,0 +1,9 @@ +use quote::quote; + +pub(crate) fn bitwarden_error_basic(input: &syn::DeriveInput) -> proc_macro::TokenStream { + quote! { + #[derive(BasicError)] + #input + } + .into() +} diff --git a/crates/bitwarden-error-macro/src/basic/derive.rs b/crates/bitwarden-error-macro/src/basic/derive.rs new file mode 100644 index 00000000..524db79e --- /dev/null +++ b/crates/bitwarden-error-macro/src/basic/derive.rs @@ -0,0 +1,39 @@ +use quote::quote; + +pub(crate) fn basic_error(item: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = syn::parse_macro_input!(item as syn::DeriveInput); + let type_identifier = &input.ident; + + let wasm = cfg!(feature = "wasm").then(|| basic_error_wasm(&type_identifier)); + quote! { + #wasm + } + .into() +} + +fn basic_error_wasm(type_identifier: &proc_macro2::Ident) -> proc_macro2::TokenStream { + let ts_identifier = quote::format_ident!("TS_TYPES_{}", type_identifier); + let ts_code_str = format!( + r##"r#" + export interface {} extends Error {{ + name: "{}"; + }}; + "#"##, + type_identifier, type_identifier + ); + let ts_code: proc_macro2::TokenStream = ts_code_str.parse().unwrap(); + + quote! { + #[wasm_bindgen(typescript_custom_section)] + const #ts_identifier: &'static str = #ts_code; + + #[automatically_derived] + impl From<#type_identifier> for JsValue { + fn from(error: #type_identifier) -> Self { + let js_error = JsError::new(error.to_string()); + js_error.set_name(stringify!(#type_identifier).to_owned()); + js_error.into() + } + } + } +} diff --git a/crates/bitwarden-error-macro/src/basic/mod.rs b/crates/bitwarden-error-macro/src/basic/mod.rs new file mode 100644 index 00000000..6e66ace8 --- /dev/null +++ b/crates/bitwarden-error-macro/src/basic/mod.rs @@ -0,0 +1,2 @@ +pub mod attribute; +pub mod derive; diff --git a/crates/bitwarden-error-macro/src/lib.rs b/crates/bitwarden-error-macro/src/lib.rs index 4bbab028..10ed5c93 100644 --- a/crates/bitwarden-error-macro/src/lib.rs +++ b/crates/bitwarden-error-macro/src/lib.rs @@ -1,3 +1,5 @@ +mod basic; + use darling::{ast::NestedMeta, FromMeta}; use quote::quote; use syn::Data; @@ -44,20 +46,12 @@ pub fn bitwarden_error( let input = syn::parse_macro_input!(item as syn::DeriveInput); match error_type { - BitwardenErrorType::Basic => basic_error_impl(&input), + BitwardenErrorType::Basic => basic::attribute::bitwarden_error_basic(&input), BitwardenErrorType::Flat => flat_error_impl(&input), BitwardenErrorType::Full => full_error_impl(&input), } } -fn basic_error_impl(input: &syn::DeriveInput) -> proc_macro::TokenStream { - quote! { - #[derive(BasicError)] - #input - } - .into() -} - fn flat_error_impl(input: &syn::DeriveInput) -> proc_macro::TokenStream { let type_identifier = &input.ident; @@ -88,47 +82,7 @@ fn full_error_impl(input: &syn::DeriveInput) -> proc_macro::TokenStream { #[proc_macro_derive(BasicError)] pub fn basic_error(item: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input = syn::parse_macro_input!(item as syn::DeriveInput); - let type_identifier = &input.ident; - - let wasm = basic_error_wasm(&type_identifier); - quote! { - #wasm - } - .into() -} - -#[cfg(feature = "wasm")] -fn basic_error_wasm(type_identifier: &proc_macro2::Ident) -> proc_macro2::TokenStream { - let ts_identifier = quote::format_ident!("TS_TYPES_{}", type_identifier); - let ts_code_str = format!( - r##"r#" - export interface {} extends Error {{ - name: "{}"; - }}; - "#"##, - type_identifier, type_identifier - ); - let ts_code: proc_macro2::TokenStream = ts_code_str.parse().unwrap(); - - quote! { - #[wasm_bindgen(typescript_custom_section)] - const #ts_identifier: &'static str = #ts_code; - - #[automatically_derived] - impl From<#type_identifier> for JsValue { - fn from(error: #type_identifier) -> Self { - let js_error = JsError::new(error.to_string()); - js_error.set_name(stringify!(#type_identifier).to_owned()); - js_error.into() - } - } - } -} - -#[cfg(not(feature = "wasm"))] -fn basic_error_wasm(type_identifier: &proc_macro2::Ident) -> proc_macro2::TokenStream { - quote! {} + basic::derive::basic_error(item) } #[proc_macro_derive(FlatError)] From 3224046b05a3cc45047a6d38aed693da7fd68dee Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Mon, 11 Nov 2024 15:44:22 +0100 Subject: [PATCH 25/53] refactor: move flat implementations into separate module --- .../src/flat/attribute.rs | 13 ++ .../bitwarden-error-macro/src/flat/derive.rs | 119 ++++++++++++++++ crates/bitwarden-error-macro/src/flat/mod.rs | 2 + crates/bitwarden-error-macro/src/lib.rs | 131 +----------------- 4 files changed, 137 insertions(+), 128 deletions(-) create mode 100644 crates/bitwarden-error-macro/src/flat/attribute.rs create mode 100644 crates/bitwarden-error-macro/src/flat/derive.rs create mode 100644 crates/bitwarden-error-macro/src/flat/mod.rs diff --git a/crates/bitwarden-error-macro/src/flat/attribute.rs b/crates/bitwarden-error-macro/src/flat/attribute.rs new file mode 100644 index 00000000..51ce1423 --- /dev/null +++ b/crates/bitwarden-error-macro/src/flat/attribute.rs @@ -0,0 +1,13 @@ +use quote::quote; + +pub(crate) fn bitwarden_error_flat(input: &syn::DeriveInput) -> proc_macro::TokenStream { + let type_identifier = &input.ident; + + quote! { + #[derive(FlatError)] + #input + + impl BitwardenError for #type_identifier {} + } + .into() +} diff --git a/crates/bitwarden-error-macro/src/flat/derive.rs b/crates/bitwarden-error-macro/src/flat/derive.rs new file mode 100644 index 00000000..37638ad5 --- /dev/null +++ b/crates/bitwarden-error-macro/src/flat/derive.rs @@ -0,0 +1,119 @@ +use quote::quote; +use syn::Data; + +pub(crate) fn flat_error(item: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = syn::parse_macro_input!(item as syn::DeriveInput); + let type_identifier = &input.ident; + + match &input.data { + Data::Enum(data) => { + let variant_names = data.variants.iter().map(|variant| &variant.ident); + + let match_arms = data.variants.iter().map(|variant| match variant.fields { + syn::Fields::Unit => { + let variant_ident = &variant.ident; + let variant_name = format!("{}", variant_ident); + quote! { + #type_identifier::#variant_ident => #variant_name + } + } + syn::Fields::Named(_) => { + let variant_ident = &variant.ident; + let variant_name = format!("{}", variant_ident); + quote! { + #type_identifier::#variant_ident { .. } => #variant_name + } + } + syn::Fields::Unnamed(_) => { + let variant_ident = &variant.ident; + let variant_name = format!("{}", variant_ident); + quote! { + #type_identifier::#variant_ident(..) => #variant_name + } + } + }); + + let wasm = flat_error_wasm(&type_identifier, &variant_names.collect::>()); + + quote! { + #wasm + + #[automatically_derived] + impl FlatError for #type_identifier { + fn error_variant(&self) -> &'static str { + match &self { + #(#match_arms), * + } + } + } + } + .into() + } + Data::Struct(_) => { + let type_identifier = &input.ident; + let variant_name = format!("{}", type_identifier); + + quote! { + #[automatically_derived] + impl FlatError for #type_identifier { + fn error_variant(&self) -> &'static str { + #variant_name + } + } + } + .into() + } + Data::Union(_) => { + syn::Error::new_spanned(input, "bitwarden_error cannot be used with unions") + .to_compile_error() + .into() + } + } +} + +#[cfg(feature = "wasm")] +fn flat_error_wasm( + type_identifier: &proc_macro2::Ident, + variant_names: &[&proc_macro2::Ident], +) -> proc_macro2::TokenStream { + let ts_identifier = quote::format_ident!("TS_TYPES_{}", type_identifier); + let ts_code_str = format!( + r##"r#" + export interface {} extends Error {{ + name: "{}"; + variant: {}; + }}; + "#"##, + type_identifier, + type_identifier, + variant_names + .iter() + .map(|vn| format!(r#""{vn}""#)) + .collect::>() + .join("|") + ); + let ts_code: proc_macro2::TokenStream = ts_code_str.parse().unwrap(); + + quote! { + #[wasm_bindgen(typescript_custom_section)] + const #ts_identifier: &'static str = #ts_code; + + #[automatically_derived] + impl From<#type_identifier> for JsValue { + fn from(error: #type_identifier) -> Self { + let js_error = JsError::new(error.to_string()); + js_error.set_name(stringify!(#type_identifier).to_owned()); + js_error.set_variant(error.error_variant().to_owned()); + js_error.into() + } + } + } +} + +#[cfg(not(feature = "wasm"))] +fn flat_error_wasm( + type_identifier: &proc_macro2::Ident, + variant_names: &[&proc_macro2::Ident], +) -> proc_macro2::TokenStream { + quote! {} +} diff --git a/crates/bitwarden-error-macro/src/flat/mod.rs b/crates/bitwarden-error-macro/src/flat/mod.rs new file mode 100644 index 00000000..6e66ace8 --- /dev/null +++ b/crates/bitwarden-error-macro/src/flat/mod.rs @@ -0,0 +1,2 @@ +pub mod attribute; +pub mod derive; diff --git a/crates/bitwarden-error-macro/src/lib.rs b/crates/bitwarden-error-macro/src/lib.rs index 10ed5c93..a7adb31e 100644 --- a/crates/bitwarden-error-macro/src/lib.rs +++ b/crates/bitwarden-error-macro/src/lib.rs @@ -1,8 +1,8 @@ mod basic; +mod flat; use darling::{ast::NestedMeta, FromMeta}; use quote::quote; -use syn::Data; #[derive(FromMeta)] struct BitwardenErrorArgs { @@ -47,23 +47,11 @@ pub fn bitwarden_error( let input = syn::parse_macro_input!(item as syn::DeriveInput); match error_type { BitwardenErrorType::Basic => basic::attribute::bitwarden_error_basic(&input), - BitwardenErrorType::Flat => flat_error_impl(&input), + BitwardenErrorType::Flat => flat::attribute::bitwarden_error_flat(&input), BitwardenErrorType::Full => full_error_impl(&input), } } -fn flat_error_impl(input: &syn::DeriveInput) -> proc_macro::TokenStream { - let type_identifier = &input.ident; - - quote! { - #[derive(FlatError)] - #input - - impl BitwardenError for #type_identifier {} - } - .into() -} - fn full_error_impl(input: &syn::DeriveInput) -> proc_macro::TokenStream { let wasm_attributes = cfg!(feature = "wasm").then(|| { quote! { @@ -87,118 +75,5 @@ pub fn basic_error(item: proc_macro::TokenStream) -> proc_macro::TokenStream { #[proc_macro_derive(FlatError)] pub fn flat_error(item: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input = syn::parse_macro_input!(item as syn::DeriveInput); - let type_identifier = &input.ident; - - match &input.data { - Data::Enum(data) => { - let variant_names = data.variants.iter().map(|variant| &variant.ident); - - let match_arms = data.variants.iter().map(|variant| match variant.fields { - syn::Fields::Unit => { - let variant_ident = &variant.ident; - let variant_name = format!("{}", variant_ident); - quote! { - #type_identifier::#variant_ident => #variant_name - } - } - syn::Fields::Named(_) => { - let variant_ident = &variant.ident; - let variant_name = format!("{}", variant_ident); - quote! { - #type_identifier::#variant_ident { .. } => #variant_name - } - } - syn::Fields::Unnamed(_) => { - let variant_ident = &variant.ident; - let variant_name = format!("{}", variant_ident); - quote! { - #type_identifier::#variant_ident(..) => #variant_name - } - } - }); - - let wasm = flat_error_wasm(&type_identifier, &variant_names.collect::>()); - - quote! { - #wasm - - #[automatically_derived] - impl FlatError for #type_identifier { - fn error_variant(&self) -> &'static str { - match &self { - #(#match_arms), * - } - } - } - } - .into() - } - Data::Struct(_) => { - let type_identifier = &input.ident; - let variant_name = format!("{}", type_identifier); - - quote! { - #[automatically_derived] - impl FlatError for #type_identifier { - fn error_variant(&self) -> &'static str { - #variant_name - } - } - } - .into() - } - Data::Union(_) => { - syn::Error::new_spanned(input, "bitwarden_error cannot be used with unions") - .to_compile_error() - .into() - } - } -} - -#[cfg(feature = "wasm")] -fn flat_error_wasm( - type_identifier: &proc_macro2::Ident, - variant_names: &[&proc_macro2::Ident], -) -> proc_macro2::TokenStream { - let ts_identifier = quote::format_ident!("TS_TYPES_{}", type_identifier); - let ts_code_str = format!( - r##"r#" - export interface {} extends Error {{ - name: "{}"; - variant: {}; - }}; - "#"##, - type_identifier, - type_identifier, - variant_names - .iter() - .map(|vn| format!(r#""{vn}""#)) - .collect::>() - .join("|") - ); - let ts_code: proc_macro2::TokenStream = ts_code_str.parse().unwrap(); - - quote! { - #[wasm_bindgen(typescript_custom_section)] - const #ts_identifier: &'static str = #ts_code; - - #[automatically_derived] - impl From<#type_identifier> for JsValue { - fn from(error: #type_identifier) -> Self { - let js_error = JsError::new(error.to_string()); - js_error.set_name(stringify!(#type_identifier).to_owned()); - js_error.set_variant(error.error_variant().to_owned()); - js_error.into() - } - } - } -} - -#[cfg(not(feature = "wasm"))] -fn flat_error_wasm( - type_identifier: &proc_macro2::Ident, - variant_names: &[&proc_macro2::Ident], -) -> proc_macro2::TokenStream { - quote! {} + flat::derive::flat_error(item) } From 33e6bcd1f44c7bb2b33435c4d87db72514f6af57 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Mon, 11 Nov 2024 15:49:52 +0100 Subject: [PATCH 26/53] refactor: move full implementations into separate module --- .../src/full/attribute.rs | 17 ++++++++++++++++ crates/bitwarden-error-macro/src/full/mod.rs | 1 + crates/bitwarden-error-macro/src/lib.rs | 20 ++----------------- 3 files changed, 20 insertions(+), 18 deletions(-) create mode 100644 crates/bitwarden-error-macro/src/full/attribute.rs create mode 100644 crates/bitwarden-error-macro/src/full/mod.rs diff --git a/crates/bitwarden-error-macro/src/full/attribute.rs b/crates/bitwarden-error-macro/src/full/attribute.rs new file mode 100644 index 00000000..82d7ae2a --- /dev/null +++ b/crates/bitwarden-error-macro/src/full/attribute.rs @@ -0,0 +1,17 @@ +use quote::quote; + +pub(crate) fn bitwarden_error_full(input: &syn::DeriveInput) -> proc_macro::TokenStream { + let wasm_attributes = cfg!(feature = "wasm").then(|| { + quote! { + #[derive(tsify_next::Tsify)] + #[tsify(into_wasm_abi)] + } + }); + + quote! { + #[derive(serde::Serialize)] + #wasm_attributes + #input + } + .into() +} diff --git a/crates/bitwarden-error-macro/src/full/mod.rs b/crates/bitwarden-error-macro/src/full/mod.rs new file mode 100644 index 00000000..8d46249f --- /dev/null +++ b/crates/bitwarden-error-macro/src/full/mod.rs @@ -0,0 +1 @@ +pub mod attribute; diff --git a/crates/bitwarden-error-macro/src/lib.rs b/crates/bitwarden-error-macro/src/lib.rs index a7adb31e..27701700 100644 --- a/crates/bitwarden-error-macro/src/lib.rs +++ b/crates/bitwarden-error-macro/src/lib.rs @@ -1,8 +1,8 @@ mod basic; mod flat; +mod full; use darling::{ast::NestedMeta, FromMeta}; -use quote::quote; #[derive(FromMeta)] struct BitwardenErrorArgs { @@ -48,26 +48,10 @@ pub fn bitwarden_error( match error_type { BitwardenErrorType::Basic => basic::attribute::bitwarden_error_basic(&input), BitwardenErrorType::Flat => flat::attribute::bitwarden_error_flat(&input), - BitwardenErrorType::Full => full_error_impl(&input), + BitwardenErrorType::Full => full::attribute::bitwarden_error_full(&input), } } -fn full_error_impl(input: &syn::DeriveInput) -> proc_macro::TokenStream { - let wasm_attributes = cfg!(feature = "wasm").then(|| { - quote! { - #[derive(tsify_next::Tsify)] - #[tsify(into_wasm_abi)] - } - }); - - quote! { - #[derive(serde::Serialize)] - #wasm_attributes - #input - } - .into() -} - #[proc_macro_derive(BasicError)] pub fn basic_error(item: proc_macro::TokenStream) -> proc_macro::TokenStream { basic::derive::basic_error(item) From e32f5b60f1dd9e836b5918c6ca9f8610234bd360 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Mon, 11 Nov 2024 15:52:37 +0100 Subject: [PATCH 27/53] refactor: move main attribute into separate module --- crates/bitwarden-error-macro/src/attribute.rs | 48 +++++++++++++++++++ crates/bitwarden-error-macro/src/lib.rs | 45 +---------------- crates/bitwarden-error/tests/flat.rs | 2 +- 3 files changed, 51 insertions(+), 44 deletions(-) create mode 100644 crates/bitwarden-error-macro/src/attribute.rs diff --git a/crates/bitwarden-error-macro/src/attribute.rs b/crates/bitwarden-error-macro/src/attribute.rs new file mode 100644 index 00000000..a521694b --- /dev/null +++ b/crates/bitwarden-error-macro/src/attribute.rs @@ -0,0 +1,48 @@ +use darling::{ast::NestedMeta, FromMeta}; + +#[derive(FromMeta)] +struct BitwardenErrorArgs { + #[darling(flatten)] + error_type: BitwardenErrorType, +} + +#[derive(FromMeta)] +enum BitwardenErrorType { + /// The error is going to be converted into a string using the `ToString` trait + #[darling(rename = "basic")] + Basic, + + /// The error is going to be converted into a flat error using the `FlatError` trait + #[darling(rename = "flat")] + Flat, + + /// The entire error stack is going to be made available using `serde` + #[darling(rename = "full")] + Full, +} + +pub(crate) fn bitwarden_error( + args: proc_macro::TokenStream, + item: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + let attr_args = match NestedMeta::parse_meta_list(args.into()) { + Ok(v) => v, + Err(e) => { + return proc_macro::TokenStream::from(darling::Error::from(e).write_errors()); + } + }; + + let BitwardenErrorArgs { error_type } = match BitwardenErrorArgs::from_list(&attr_args) { + Ok(params) => params, + Err(error) => { + return proc_macro::TokenStream::from(darling::Error::from(error).write_errors()); + } + }; + + let input = syn::parse_macro_input!(item as syn::DeriveInput); + match error_type { + BitwardenErrorType::Basic => crate::basic::attribute::bitwarden_error_basic(&input), + BitwardenErrorType::Flat => crate::flat::attribute::bitwarden_error_flat(&input), + BitwardenErrorType::Full => crate::full::attribute::bitwarden_error_full(&input), + } +} diff --git a/crates/bitwarden-error-macro/src/lib.rs b/crates/bitwarden-error-macro/src/lib.rs index 27701700..ab662a97 100644 --- a/crates/bitwarden-error-macro/src/lib.rs +++ b/crates/bitwarden-error-macro/src/lib.rs @@ -1,55 +1,14 @@ +mod attribute; mod basic; mod flat; mod full; -use darling::{ast::NestedMeta, FromMeta}; - -#[derive(FromMeta)] -struct BitwardenErrorArgs { - #[darling(flatten)] - error_type: BitwardenErrorType, -} - -#[derive(FromMeta)] -enum BitwardenErrorType { - /// The error is going to be converted into a string using the `ToString` trait - #[darling(rename = "basic")] - Basic, - - /// The error is going to be converted into a flat error using the `FlatError` trait - #[darling(rename = "flat")] - Flat, - - /// The entire error stack is going to be made available using `serde` - #[darling(rename = "full")] - Full, -} - #[proc_macro_attribute] pub fn bitwarden_error( args: proc_macro::TokenStream, item: proc_macro::TokenStream, ) -> proc_macro::TokenStream { - let attr_args = match NestedMeta::parse_meta_list(args.into()) { - Ok(v) => v, - Err(e) => { - return proc_macro::TokenStream::from(darling::Error::from(e).write_errors()); - } - }; - - let BitwardenErrorArgs { error_type } = match BitwardenErrorArgs::from_list(&attr_args) { - Ok(params) => params, - Err(error) => { - return proc_macro::TokenStream::from(darling::Error::from(error).write_errors()); - } - }; - - let input = syn::parse_macro_input!(item as syn::DeriveInput); - match error_type { - BitwardenErrorType::Basic => basic::attribute::bitwarden_error_basic(&input), - BitwardenErrorType::Flat => flat::attribute::bitwarden_error_flat(&input), - BitwardenErrorType::Full => full::attribute::bitwarden_error_full(&input), - } + attribute::bitwarden_error(args, item) } #[proc_macro_derive(BasicError)] diff --git a/crates/bitwarden-error/tests/flat.rs b/crates/bitwarden-error/tests/flat.rs index 4f2f1480..d87d49e8 100644 --- a/crates/bitwarden-error/tests/flat.rs +++ b/crates/bitwarden-error/tests/flat.rs @@ -124,7 +124,7 @@ fn variant_names_for_struct() { #[wasm_bindgen_test] #[cfg(feature = "wasm")] #[allow(dead_code)] // Not actually dead, but rust-analyzer doesn't understand `wasm_bindgen_test` -fn converts_to_js_error_using_to_string() { +fn converts_to_js_error() { #[derive(Debug, FlatError)] enum SomeError { Foo, From e8901c67c04c0d52352ac7916054985641243e80 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Mon, 11 Nov 2024 15:54:42 +0100 Subject: [PATCH 28/53] refactor: only allow flat for enums [bitwarden_error(flat)] on struct was doing basically the same thing as #[bitwarden_error(flat)] so I'm removing it --- .../bitwarden-error-macro/src/flat/derive.rs | 22 ++-------- crates/bitwarden-error/tests/flat.rs | 41 ------------------- 2 files changed, 3 insertions(+), 60 deletions(-) diff --git a/crates/bitwarden-error-macro/src/flat/derive.rs b/crates/bitwarden-error-macro/src/flat/derive.rs index 37638ad5..be1d57b9 100644 --- a/crates/bitwarden-error-macro/src/flat/derive.rs +++ b/crates/bitwarden-error-macro/src/flat/derive.rs @@ -49,25 +49,9 @@ pub(crate) fn flat_error(item: proc_macro::TokenStream) -> proc_macro::TokenStre } .into() } - Data::Struct(_) => { - let type_identifier = &input.ident; - let variant_name = format!("{}", type_identifier); - - quote! { - #[automatically_derived] - impl FlatError for #type_identifier { - fn error_variant(&self) -> &'static str { - #variant_name - } - } - } - .into() - } - Data::Union(_) => { - syn::Error::new_spanned(input, "bitwarden_error cannot be used with unions") - .to_compile_error() - .into() - } + _ => syn::Error::new_spanned(input, "bitwarden_error can only be used with enums") + .to_compile_error() + .into(), } } diff --git a/crates/bitwarden-error/tests/flat.rs b/crates/bitwarden-error/tests/flat.rs index d87d49e8..8d88ab87 100644 --- a/crates/bitwarden-error/tests/flat.rs +++ b/crates/bitwarden-error/tests/flat.rs @@ -52,23 +52,6 @@ fn variant_for_enum_with_fields() { assert_eq!(baz.error_variant(), "ComplexError::Baz"); } -#[test] -fn variant_for_struct() { - #[derive(Debug)] - #[bitwarden_error(flat)] - struct SimpleStruct; - - impl ToString for SimpleStruct { - fn to_string(&self) -> String { - format!("{:?}", self) - } - } - - let simple = SimpleStruct; - - assert_eq!(simple.error_variant(), "SimpleStruct"); -} - #[test] #[cfg(feature = "wasm")] fn variant_names_for_enum() { @@ -97,30 +80,6 @@ fn variant_names_for_enum() { // ); } -#[test] -#[cfg(feature = "wasm")] -fn variant_names_for_struct() { - #[allow(dead_code)] - #[derive(Debug)] - #[bitwarden_error(flat)] - struct SimpleStruct; - - impl ToString for SimpleStruct { - fn to_string(&self) -> String { - format!("{:?}", self) - } - } - - // TODO: Not sure how to test this yet - // let types = TS_TYPES_SimpleStruct; - // assert_eq!( - // types, - // r#" - // export const TS_TYPES_SimpleStruct = ""; - // "# - // ); -} - #[wasm_bindgen_test] #[cfg(feature = "wasm")] #[allow(dead_code)] // Not actually dead, but rust-analyzer doesn't understand `wasm_bindgen_test` From e4622fb81ac713fc6c2da617c0d0703ab034e354 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Mon, 11 Nov 2024 15:57:23 +0100 Subject: [PATCH 29/53] refactor: simplify flat wasm attributes --- crates/bitwarden-error-macro/src/flat/derive.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/crates/bitwarden-error-macro/src/flat/derive.rs b/crates/bitwarden-error-macro/src/flat/derive.rs index be1d57b9..c8258439 100644 --- a/crates/bitwarden-error-macro/src/flat/derive.rs +++ b/crates/bitwarden-error-macro/src/flat/derive.rs @@ -33,7 +33,8 @@ pub(crate) fn flat_error(item: proc_macro::TokenStream) -> proc_macro::TokenStre } }); - let wasm = flat_error_wasm(&type_identifier, &variant_names.collect::>()); + let wasm = cfg!(feature = "wasm") + .then(|| flat_error_wasm(&type_identifier, &variant_names.collect::>())); quote! { #wasm @@ -55,7 +56,6 @@ pub(crate) fn flat_error(item: proc_macro::TokenStream) -> proc_macro::TokenStre } } -#[cfg(feature = "wasm")] fn flat_error_wasm( type_identifier: &proc_macro2::Ident, variant_names: &[&proc_macro2::Ident], @@ -93,11 +93,3 @@ fn flat_error_wasm( } } } - -#[cfg(not(feature = "wasm"))] -fn flat_error_wasm( - type_identifier: &proc_macro2::Ident, - variant_names: &[&proc_macro2::Ident], -) -> proc_macro2::TokenStream { - quote! {} -} From c2511b3badcc4af719bea9380ba13cb601122cd8 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Mon, 11 Nov 2024 16:08:20 +0100 Subject: [PATCH 30/53] refactor: remove old BitwardenError trait --- crates/bitwarden-error-macro/src/flat/attribute.rs | 4 ---- crates/bitwarden-error/src/lib.rs | 3 --- 2 files changed, 7 deletions(-) diff --git a/crates/bitwarden-error-macro/src/flat/attribute.rs b/crates/bitwarden-error-macro/src/flat/attribute.rs index 51ce1423..0683482a 100644 --- a/crates/bitwarden-error-macro/src/flat/attribute.rs +++ b/crates/bitwarden-error-macro/src/flat/attribute.rs @@ -1,13 +1,9 @@ use quote::quote; pub(crate) fn bitwarden_error_flat(input: &syn::DeriveInput) -> proc_macro::TokenStream { - let type_identifier = &input.ident; - quote! { #[derive(FlatError)] #input - - impl BitwardenError for #type_identifier {} } .into() } diff --git a/crates/bitwarden-error/src/lib.rs b/crates/bitwarden-error/src/lib.rs index edbdf29d..3727d763 100644 --- a/crates/bitwarden-error/src/lib.rs +++ b/crates/bitwarden-error/src/lib.rs @@ -7,11 +7,8 @@ pub mod wasm; pub mod prelude { pub use crate::basic_error; pub use crate::flat_error::FlatError; - pub use crate::BitwardenError; pub use bitwarden_error_macro::*; #[cfg(feature = "wasm")] pub use {crate::wasm::JsError, wasm_bindgen::prelude::*}; } - -pub trait BitwardenError: flat_error::FlatError + ToString {} From 406c845003dd47b4c627bb97f67284ac7922e962 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Mon, 11 Nov 2024 16:08:42 +0100 Subject: [PATCH 31/53] fix: tests not matching new variant formatting --- crates/bitwarden-error/tests/flat.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/bitwarden-error/tests/flat.rs b/crates/bitwarden-error/tests/flat.rs index 8d88ab87..1933c76e 100644 --- a/crates/bitwarden-error/tests/flat.rs +++ b/crates/bitwarden-error/tests/flat.rs @@ -21,9 +21,9 @@ fn variant_for_basic_enum() { let bar = SimpleError::Bar; let baz = SimpleError::Baz; - assert_eq!(foo.error_variant(), "SimpleError::Foo"); - assert_eq!(bar.error_variant(), "SimpleError::Bar"); - assert_eq!(baz.error_variant(), "SimpleError::Baz"); + assert_eq!(foo.error_variant(), "Foo"); + assert_eq!(bar.error_variant(), "Bar"); + assert_eq!(baz.error_variant(), "Baz"); } #[test] @@ -47,9 +47,9 @@ fn variant_for_enum_with_fields() { let bar = ComplexError::Bar { x: 1, y: 2 }; let baz = ComplexError::Baz(true, true); - assert_eq!(foo.error_variant(), "ComplexError::Foo"); - assert_eq!(bar.error_variant(), "ComplexError::Bar"); - assert_eq!(baz.error_variant(), "ComplexError::Baz"); + assert_eq!(foo.error_variant(), "Foo"); + assert_eq!(bar.error_variant(), "Bar"); + assert_eq!(baz.error_variant(), "Baz"); } #[test] From 5c8f5ef0f7aa8ac5b33e9c22e73a9520ccfcb055 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Tue, 12 Nov 2024 10:49:43 +0100 Subject: [PATCH 32/53] refactor: encapsulate macro output --- .../bitwarden-error-macro/src/basic/derive.rs | 21 +++++++++-------- .../bitwarden-error-macro/src/flat/derive.rs | 23 ++++++++++--------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/crates/bitwarden-error-macro/src/basic/derive.rs b/crates/bitwarden-error-macro/src/basic/derive.rs index 524db79e..c109ee1b 100644 --- a/crates/bitwarden-error-macro/src/basic/derive.rs +++ b/crates/bitwarden-error-macro/src/basic/derive.rs @@ -12,7 +12,6 @@ pub(crate) fn basic_error(item: proc_macro::TokenStream) -> proc_macro::TokenStr } fn basic_error_wasm(type_identifier: &proc_macro2::Ident) -> proc_macro2::TokenStream { - let ts_identifier = quote::format_ident!("TS_TYPES_{}", type_identifier); let ts_code_str = format!( r##"r#" export interface {} extends Error {{ @@ -24,16 +23,18 @@ fn basic_error_wasm(type_identifier: &proc_macro2::Ident) -> proc_macro2::TokenS let ts_code: proc_macro2::TokenStream = ts_code_str.parse().unwrap(); quote! { - #[wasm_bindgen(typescript_custom_section)] - const #ts_identifier: &'static str = #ts_code; + const _: () = { + #[wasm_bindgen(typescript_custom_section)] + const TS_APPEND_CONTENT: &'static str = #ts_code; - #[automatically_derived] - impl From<#type_identifier> for JsValue { - fn from(error: #type_identifier) -> Self { - let js_error = JsError::new(error.to_string()); - js_error.set_name(stringify!(#type_identifier).to_owned()); - js_error.into() + #[automatically_derived] + impl From<#type_identifier> for JsValue { + fn from(error: #type_identifier) -> Self { + let js_error = JsError::new(error.to_string()); + js_error.set_name(stringify!(#type_identifier).to_owned()); + js_error.into() + } } - } + }; } } diff --git a/crates/bitwarden-error-macro/src/flat/derive.rs b/crates/bitwarden-error-macro/src/flat/derive.rs index c8258439..56207d3b 100644 --- a/crates/bitwarden-error-macro/src/flat/derive.rs +++ b/crates/bitwarden-error-macro/src/flat/derive.rs @@ -60,7 +60,6 @@ fn flat_error_wasm( type_identifier: &proc_macro2::Ident, variant_names: &[&proc_macro2::Ident], ) -> proc_macro2::TokenStream { - let ts_identifier = quote::format_ident!("TS_TYPES_{}", type_identifier); let ts_code_str = format!( r##"r#" export interface {} extends Error {{ @@ -79,17 +78,19 @@ fn flat_error_wasm( let ts_code: proc_macro2::TokenStream = ts_code_str.parse().unwrap(); quote! { - #[wasm_bindgen(typescript_custom_section)] - const #ts_identifier: &'static str = #ts_code; + const _: () = { + #[wasm_bindgen(typescript_custom_section)] + const TS_APPEND_CONTENT: &'static str = #ts_code; - #[automatically_derived] - impl From<#type_identifier> for JsValue { - fn from(error: #type_identifier) -> Self { - let js_error = JsError::new(error.to_string()); - js_error.set_name(stringify!(#type_identifier).to_owned()); - js_error.set_variant(error.error_variant().to_owned()); - js_error.into() + #[automatically_derived] + impl From<#type_identifier> for JsValue { + fn from(error: #type_identifier) -> Self { + let js_error = JsError::new(error.to_string()); + js_error.set_name(stringify!(#type_identifier).to_owned()); + js_error.set_variant(error.error_variant().to_owned()); + js_error.into() + } } - } + }; } } From d1446116e9b0275e5a52908c725dd8ba8bf4132b Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Tue, 12 Nov 2024 13:07:16 +0100 Subject: [PATCH 33/53] refactor: stop polluting imports with wasm-bindgen re-export --- crates/bitwarden-error-macro/src/basic/derive.rs | 4 +++- crates/bitwarden-error-macro/src/flat/derive.rs | 4 +++- crates/bitwarden-error/Cargo.toml | 2 +- crates/bitwarden-error/src/lib.rs | 2 +- crates/bitwarden-error/src/wasm.rs | 14 +++++++------- crates/bitwarden-error/tests/basic.rs | 7 +++++-- crates/bitwarden-error/tests/flat.rs | 5 ++++- crates/bitwarden-error/tests/full.rs | 5 ++++- crates/bitwarden-wasm-internal/src/client.rs | 1 + 9 files changed, 29 insertions(+), 15 deletions(-) diff --git a/crates/bitwarden-error-macro/src/basic/derive.rs b/crates/bitwarden-error-macro/src/basic/derive.rs index c109ee1b..1cdbf004 100644 --- a/crates/bitwarden-error-macro/src/basic/derive.rs +++ b/crates/bitwarden-error-macro/src/basic/derive.rs @@ -24,13 +24,15 @@ fn basic_error_wasm(type_identifier: &proc_macro2::Ident) -> proc_macro2::TokenS quote! { const _: () = { + use wasm_bindgen::prelude::*; + #[wasm_bindgen(typescript_custom_section)] const TS_APPEND_CONTENT: &'static str = #ts_code; #[automatically_derived] impl From<#type_identifier> for JsValue { fn from(error: #type_identifier) -> Self { - let js_error = JsError::new(error.to_string()); + let js_error = SdkJsError::new(error.to_string()); js_error.set_name(stringify!(#type_identifier).to_owned()); js_error.into() } diff --git a/crates/bitwarden-error-macro/src/flat/derive.rs b/crates/bitwarden-error-macro/src/flat/derive.rs index 56207d3b..b3d9a657 100644 --- a/crates/bitwarden-error-macro/src/flat/derive.rs +++ b/crates/bitwarden-error-macro/src/flat/derive.rs @@ -79,13 +79,15 @@ fn flat_error_wasm( quote! { const _: () = { + use wasm_bindgen::prelude::*; + #[wasm_bindgen(typescript_custom_section)] const TS_APPEND_CONTENT: &'static str = #ts_code; #[automatically_derived] impl From<#type_identifier> for JsValue { fn from(error: #type_identifier) -> Self { - let js_error = JsError::new(error.to_string()); + let js_error = SdkJsError::new(error.to_string()); js_error.set_name(stringify!(#type_identifier).to_owned()); js_error.set_variant(error.error_variant().to_owned()); js_error.into() diff --git a/crates/bitwarden-error/Cargo.toml b/crates/bitwarden-error/Cargo.toml index 7b105a83..6e46e3dc 100644 --- a/crates/bitwarden-error/Cargo.toml +++ b/crates/bitwarden-error/Cargo.toml @@ -10,7 +10,7 @@ license-file.workspace = true keywords.workspace = true [features] -wasm = ["bitwarden-error-macro/wasm", "wasm-bindgen"] +wasm = ["bitwarden-error-macro/wasm", "dep:wasm-bindgen"] [dependencies] bitwarden-error-macro = { version = "1.0.0", path = "../bitwarden-error-macro" } diff --git a/crates/bitwarden-error/src/lib.rs b/crates/bitwarden-error/src/lib.rs index 3727d763..82f99c40 100644 --- a/crates/bitwarden-error/src/lib.rs +++ b/crates/bitwarden-error/src/lib.rs @@ -10,5 +10,5 @@ pub mod prelude { pub use bitwarden_error_macro::*; #[cfg(feature = "wasm")] - pub use {crate::wasm::JsError, wasm_bindgen::prelude::*}; + pub use crate::wasm::SdkJsError; } diff --git a/crates/bitwarden-error/src/wasm.rs b/crates/bitwarden-error/src/wasm.rs index f39eba98..1489631f 100644 --- a/crates/bitwarden-error/src/wasm.rs +++ b/crates/bitwarden-error/src/wasm.rs @@ -3,23 +3,23 @@ use wasm_bindgen::prelude::*; #[cfg_attr(feature = "wasm", wasm_bindgen)] extern "C" { #[wasm_bindgen(js_name = Error)] - pub type JsError; + pub type SdkJsError; #[wasm_bindgen(constructor, js_class = Error)] - pub fn new(message: String) -> JsError; + pub fn new(message: String) -> SdkJsError; #[wasm_bindgen(method, getter, structural)] - pub fn message(this: &JsError) -> String; + pub fn message(this: &SdkJsError) -> String; #[wasm_bindgen(method, getter, structural)] - pub fn name(this: &JsError) -> String; + pub fn name(this: &SdkJsError) -> String; #[wasm_bindgen(method, setter, structural)] - pub fn set_name(this: &JsError, name: String); + pub fn set_name(this: &SdkJsError, name: String); #[wasm_bindgen(method, getter, structural)] - pub fn variant(this: &JsError) -> String; + pub fn variant(this: &SdkJsError) -> String; #[wasm_bindgen(method, setter, structural)] - pub fn set_variant(this: &JsError, variant: String); + pub fn set_variant(this: &SdkJsError, variant: String); } diff --git a/crates/bitwarden-error/tests/basic.rs b/crates/bitwarden-error/tests/basic.rs index 5da811e4..9b112ea9 100644 --- a/crates/bitwarden-error/tests/basic.rs +++ b/crates/bitwarden-error/tests/basic.rs @@ -1,10 +1,13 @@ -use bitwarden_error::prelude::*; +#[cfg(feature = "wasm")] use wasm_bindgen_test::*; #[wasm_bindgen_test] #[cfg(feature = "wasm")] #[allow(dead_code)] // Not actually dead, but rust-analyzer doesn't understand `wasm_bindgen_test` fn converts_to_js_error_using_to_string() { + use bitwarden_error::prelude::*; + use wasm_bindgen::JsValue; + #[derive(Debug, BasicError)] struct SomeError; impl ToString for SomeError { @@ -16,7 +19,7 @@ fn converts_to_js_error_using_to_string() { let simple = SomeError; let js_value: JsValue = simple.into(); - let js_error = JsError::from(js_value); + let js_error = SdkJsError::from(js_value); assert_eq!(js_error.name(), "SomeError"); assert_eq!(js_error.message(), "This is an error"); } diff --git a/crates/bitwarden-error/tests/flat.rs b/crates/bitwarden-error/tests/flat.rs index 1933c76e..fc5d85d7 100644 --- a/crates/bitwarden-error/tests/flat.rs +++ b/crates/bitwarden-error/tests/flat.rs @@ -1,4 +1,5 @@ use bitwarden_error::prelude::*; +#[cfg(feature = "wasm")] use wasm_bindgen_test::*; #[test] @@ -84,6 +85,8 @@ fn variant_names_for_enum() { #[cfg(feature = "wasm")] #[allow(dead_code)] // Not actually dead, but rust-analyzer doesn't understand `wasm_bindgen_test` fn converts_to_js_error() { + use wasm_bindgen::JsValue; + #[derive(Debug, FlatError)] enum SomeError { Foo, @@ -99,7 +102,7 @@ fn converts_to_js_error() { let simple = SomeError::Baz; let js_value: JsValue = simple.into(); - let js_error = JsError::from(js_value); + let js_error = SdkJsError::from(js_value); assert_eq!(js_error.name(), "SomeError"); assert_eq!(js_error.message(), "This is an error"); assert_eq!(js_error.variant(), "Baz"); diff --git a/crates/bitwarden-error/tests/full.rs b/crates/bitwarden-error/tests/full.rs index d367c868..7159faaf 100644 --- a/crates/bitwarden-error/tests/full.rs +++ b/crates/bitwarden-error/tests/full.rs @@ -1,4 +1,4 @@ -use bitwarden_error::prelude::*; +#[cfg(feature = "wasm")] use wasm_bindgen_test::*; #[allow(dead_code)] @@ -7,6 +7,9 @@ use wasm_bindgen_test::*; #[cfg(feature = "wasm")] // `full` errors are just forwarded to TSify and Serde so this is just a smoke test fn converts_to_js() { + use bitwarden_error::prelude::*; + use wasm_bindgen::JsValue; + #[bitwarden_error(full)] enum SomeError { Foo(String), diff --git a/crates/bitwarden-wasm-internal/src/client.rs b/crates/bitwarden-wasm-internal/src/client.rs index 022bc207..c42534df 100644 --- a/crates/bitwarden-wasm-internal/src/client.rs +++ b/crates/bitwarden-wasm-internal/src/client.rs @@ -4,6 +4,7 @@ use std::{fmt::Display, rc::Rc}; use bitwarden_core::{Client, ClientSettings}; use bitwarden_error::prelude::*; use log::{set_max_level, Level}; +use wasm_bindgen::prelude::*; use crate::{vault::ClientVault, ClientCrypto}; From 6843e4a9cc3b32c0599d56580e1d347a61ff9cb5 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Tue, 12 Nov 2024 13:09:56 +0100 Subject: [PATCH 34/53] chore: clean up unusued file --- crates/bitwarden-error/src/basic_error.rs | 1 - crates/bitwarden-error/src/lib.rs | 2 -- 2 files changed, 3 deletions(-) delete mode 100644 crates/bitwarden-error/src/basic_error.rs diff --git a/crates/bitwarden-error/src/basic_error.rs b/crates/bitwarden-error/src/basic_error.rs deleted file mode 100644 index 8b137891..00000000 --- a/crates/bitwarden-error/src/basic_error.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/crates/bitwarden-error/src/lib.rs b/crates/bitwarden-error/src/lib.rs index 82f99c40..72f40d85 100644 --- a/crates/bitwarden-error/src/lib.rs +++ b/crates/bitwarden-error/src/lib.rs @@ -1,11 +1,9 @@ -pub mod basic_error; pub mod flat_error; #[cfg(feature = "wasm")] pub mod wasm; pub mod prelude { - pub use crate::basic_error; pub use crate::flat_error::FlatError; pub use bitwarden_error_macro::*; From cb45b424c12a236c29422192a47dd824a3e43a23 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Tue, 12 Nov 2024 14:26:03 +0100 Subject: [PATCH 35/53] feat: add isError function to basic errors --- Cargo.lock | 1 + Cargo.toml | 1 + crates/bitwarden-core/Cargo.toml | 2 ++ .../bitwarden-error-macro/src/basic/derive.rs | 20 ++++++++++++++----- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f9dc3610..080a25d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -357,6 +357,7 @@ dependencies = [ "chrono", "getrandom", "hmac", + "js-sys", "log", "rand", "rand_chacha", diff --git a/Cargo.toml b/Cargo.toml index ca5d6c1f..aa14ca88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,6 +58,7 @@ uniffi = "=0.28.1" uuid = { version = ">=1.3.3, <2.0", features = ["serde", "v4"] } validator = { version = "0.18.1", features = ["derive"] } wasm-bindgen = { version = ">=0.2.91, <0.3", features = ["serde-serialize"] } +js-sys = { version = ">=0.3.72, <0.4" } wasm-bindgen-futures = "0.4.41" [workspace.lints.clippy] diff --git a/crates/bitwarden-core/Cargo.toml b/crates/bitwarden-core/Cargo.toml index fe1c6a1c..88fb7916 100644 --- a/crates/bitwarden-core/Cargo.toml +++ b/crates/bitwarden-core/Cargo.toml @@ -23,6 +23,7 @@ secrets = [] # Secrets manager API wasm = [ "bitwarden-error/wasm", "dep:wasm-bindgen", + "dep:js-sys", "dep:tsify-next", ] # WASM support @@ -53,6 +54,7 @@ wasm-bindgen = { workspace = true, optional = true } zeroize = { version = ">=1.7.0, <2.0", features = ["derive", "aarch64"] } zxcvbn = { version = ">=3.0.1, <4.0", optional = true } tsify-next = { workspace = true, optional = true } +js-sys = { workspace = true, optional = true } bitwarden-error = { workspace = true } [target.'cfg(not(target_arch="wasm32"))'.dependencies] diff --git a/crates/bitwarden-error-macro/src/basic/derive.rs b/crates/bitwarden-error-macro/src/basic/derive.rs index 1cdbf004..2fa0c4c1 100644 --- a/crates/bitwarden-error-macro/src/basic/derive.rs +++ b/crates/bitwarden-error-macro/src/basic/derive.rs @@ -12,13 +12,16 @@ pub(crate) fn basic_error(item: proc_macro::TokenStream) -> proc_macro::TokenStr } fn basic_error_wasm(type_identifier: &proc_macro2::Ident) -> proc_macro2::TokenStream { + let type_identifier_str = type_identifier.to_string(); + let is_error_function_name = format!("is{}", type_identifier); let ts_code_str = format!( r##"r#" - export interface {} extends Error {{ - name: "{}"; + export interface {type_identifier} extends Error {{ + name: "{type_identifier}"; }}; - "#"##, - type_identifier, type_identifier + + export function {is_error_function_name}(error: any): error is {type_identifier}; + "#"## ); let ts_code: proc_macro2::TokenStream = ts_code_str.parse().unwrap(); @@ -29,11 +32,18 @@ fn basic_error_wasm(type_identifier: &proc_macro2::Ident) -> proc_macro2::TokenS #[wasm_bindgen(typescript_custom_section)] const TS_APPEND_CONTENT: &'static str = #ts_code; + #[wasm_bindgen(js_name = #is_error_function_name, skip_typescript)] + pub fn is_error(error: &JsValue) -> bool { + let name_js_value = js_sys::Reflect::get(&error, &JsValue::from_str("name")).unwrap_or(JsValue::NULL); + let name = name_js_value.as_string().unwrap_or_default(); + name == #type_identifier_str + } + #[automatically_derived] impl From<#type_identifier> for JsValue { fn from(error: #type_identifier) -> Self { let js_error = SdkJsError::new(error.to_string()); - js_error.set_name(stringify!(#type_identifier).to_owned()); + js_error.set_name(#type_identifier_str.to_owned()); js_error.into() } } From 9d3999eff4b3fe51ccdade2baa5bb47df6712042 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Tue, 12 Nov 2024 14:29:04 +0100 Subject: [PATCH 36/53] feat: add isError function to flat errors --- .../bitwarden-error-macro/src/flat/derive.rs | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/crates/bitwarden-error-macro/src/flat/derive.rs b/crates/bitwarden-error-macro/src/flat/derive.rs index b3d9a657..5781c480 100644 --- a/crates/bitwarden-error-macro/src/flat/derive.rs +++ b/crates/bitwarden-error-macro/src/flat/derive.rs @@ -60,20 +60,22 @@ fn flat_error_wasm( type_identifier: &proc_macro2::Ident, variant_names: &[&proc_macro2::Ident], ) -> proc_macro2::TokenStream { + let type_identifier_str = type_identifier.to_string(); + let is_error_function_name = format!("is{}", type_identifier); + let ts_variant_names = variant_names + .iter() + .map(|vn| format!(r#""{vn}""#)) + .collect::>() + .join("|"); let ts_code_str = format!( r##"r#" - export interface {} extends Error {{ - name: "{}"; - variant: {}; + export interface {type_identifier} extends Error {{ + name: "{type_identifier}"; + variant: {ts_variant_names}; }}; + + export function {is_error_function_name}(error: any): error is {type_identifier}; "#"##, - type_identifier, - type_identifier, - variant_names - .iter() - .map(|vn| format!(r#""{vn}""#)) - .collect::>() - .join("|") ); let ts_code: proc_macro2::TokenStream = ts_code_str.parse().unwrap(); @@ -84,6 +86,13 @@ fn flat_error_wasm( #[wasm_bindgen(typescript_custom_section)] const TS_APPEND_CONTENT: &'static str = #ts_code; + #[wasm_bindgen(js_name = #is_error_function_name, skip_typescript)] + pub fn is_error(error: &JsValue) -> bool { + let name_js_value = js_sys::Reflect::get(&error, &JsValue::from_str("name")).unwrap_or(JsValue::NULL); + let name = name_js_value.as_string().unwrap_or_default(); + name == #type_identifier_str + } + #[automatically_derived] impl From<#type_identifier> for JsValue { fn from(error: #type_identifier) -> Self { From 91394936aaba8ba5f94a144b146be58e9a862f35 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Tue, 12 Nov 2024 14:31:21 +0100 Subject: [PATCH 37/53] fix: dependency format --- crates/bitwarden-error/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-error/Cargo.toml b/crates/bitwarden-error/Cargo.toml index 6e46e3dc..f48343e2 100644 --- a/crates/bitwarden-error/Cargo.toml +++ b/crates/bitwarden-error/Cargo.toml @@ -13,7 +13,7 @@ keywords.workspace = true wasm = ["bitwarden-error-macro/wasm", "dep:wasm-bindgen"] [dependencies] -bitwarden-error-macro = { version = "1.0.0", path = "../bitwarden-error-macro" } +bitwarden-error-macro = { workspace = true } wasm-bindgen = { workspace = true, optional = true } [lints] From fa8d913aa3c2b3b9f750d9c786ac0ccbfa582f09 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Tue, 12 Nov 2024 14:35:51 +0100 Subject: [PATCH 38/53] chore: clean up old generic error solution --- crates/bitwarden-core/src/error.rs | 2 + crates/bitwarden-wasm-internal/src/crypto.rs | 2 - crates/bitwarden-wasm-internal/src/error.rs | 40 ------------------- crates/bitwarden-wasm-internal/src/lib.rs | 1 - .../src/vault/folders.rs | 6 +-- 5 files changed, 4 insertions(+), 47 deletions(-) delete mode 100644 crates/bitwarden-wasm-internal/src/error.rs diff --git a/crates/bitwarden-core/src/error.rs b/crates/bitwarden-core/src/error.rs index 697ca6c9..b39319c9 100644 --- a/crates/bitwarden-core/src/error.rs +++ b/crates/bitwarden-core/src/error.rs @@ -4,6 +4,7 @@ use std::{borrow::Cow, fmt::Debug}; use bitwarden_api_api::apis::Error as ApiError; use bitwarden_api_identity::apis::Error as IdentityError; +use bitwarden_error::prelude::*; use log::debug; use reqwest::StatusCode; use thiserror::Error; @@ -12,6 +13,7 @@ use validator::ValidationErrors; #[cfg(feature = "internal")] use crate::client::encryption_settings::EncryptionSettingsError; +#[bitwarden_error(flat)] #[derive(Debug, Error)] pub enum Error { #[error(transparent)] diff --git a/crates/bitwarden-wasm-internal/src/crypto.rs b/crates/bitwarden-wasm-internal/src/crypto.rs index 795024d8..698fca11 100644 --- a/crates/bitwarden-wasm-internal/src/crypto.rs +++ b/crates/bitwarden-wasm-internal/src/crypto.rs @@ -7,8 +7,6 @@ use bitwarden_core::{ }; use wasm_bindgen::prelude::*; -use crate::error::Result; - #[wasm_bindgen] pub struct ClientCrypto(Rc); diff --git a/crates/bitwarden-wasm-internal/src/error.rs b/crates/bitwarden-wasm-internal/src/error.rs deleted file mode 100644 index 137ae81b..00000000 --- a/crates/bitwarden-wasm-internal/src/error.rs +++ /dev/null @@ -1,40 +0,0 @@ -use wasm_bindgen::prelude::*; - -// Importing an error class defined in JavaScript instead of defining it in Rust -// allows us to extend the `Error` class. It also provides much better console output. -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(js_name = Error)] - type JsError; - - #[wasm_bindgen(constructor, js_class = Error)] - fn new(message: String) -> JsError; - - #[wasm_bindgen(method, getter, structural)] - fn name(this: &JsError) -> String; - - #[wasm_bindgen(method, setter, structural)] - fn set_name(this: &JsError, name: String); - - #[wasm_bindgen(method, getter, structural)] - fn variant(this: &JsError) -> String; - - #[wasm_bindgen(method, setter, structural)] - fn set_variant(this: &JsError, variant: String); -} - -pub type Result = std::result::Result; - -pub struct GenericError(pub String); - -impl From for GenericError { - fn from(error: T) -> Self { - GenericError(error.to_string()) - } -} - -impl From for JsValue { - fn from(error: GenericError) -> Self { - JsError::new(error.0).into() - } -} diff --git a/crates/bitwarden-wasm-internal/src/lib.rs b/crates/bitwarden-wasm-internal/src/lib.rs index 6367ff31..6f3516a9 100644 --- a/crates/bitwarden-wasm-internal/src/lib.rs +++ b/crates/bitwarden-wasm-internal/src/lib.rs @@ -1,7 +1,6 @@ mod client; mod crypto; mod custom_types; -mod error; mod vault; pub use client::BitwardenClient; diff --git a/crates/bitwarden-wasm-internal/src/vault/folders.rs b/crates/bitwarden-wasm-internal/src/vault/folders.rs index 65289269..8cb110a6 100644 --- a/crates/bitwarden-wasm-internal/src/vault/folders.rs +++ b/crates/bitwarden-wasm-internal/src/vault/folders.rs @@ -4,8 +4,6 @@ use bitwarden_core::Client; use bitwarden_vault::{ClientVaultExt, Folder, FolderView}; use wasm_bindgen::prelude::*; -use crate::error::Result; - #[wasm_bindgen] pub struct ClientFolders(Rc); @@ -18,7 +16,7 @@ impl ClientFolders { #[wasm_bindgen] impl ClientFolders { /// Decrypt folder - pub fn decrypt(&self, folder: Folder) -> Result { - Ok(self.0.vault().folders().decrypt(folder)?) + pub fn decrypt(&self, folder: Folder) -> Result { + self.0.vault().folders().decrypt(folder) } } From 2017aea5b89f66469cc471350d554d4faf49d44e Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Tue, 12 Nov 2024 14:37:51 +0100 Subject: [PATCH 39/53] fix: lint --- crates/bitwarden-error/src/lib.rs | 2 +- crates/bitwarden-error/tests/full.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/bitwarden-error/src/lib.rs b/crates/bitwarden-error/src/lib.rs index 72f40d85..0eb32f24 100644 --- a/crates/bitwarden-error/src/lib.rs +++ b/crates/bitwarden-error/src/lib.rs @@ -4,9 +4,9 @@ pub mod flat_error; pub mod wasm; pub mod prelude { - pub use crate::flat_error::FlatError; pub use bitwarden_error_macro::*; + pub use crate::flat_error::FlatError; #[cfg(feature = "wasm")] pub use crate::wasm::SdkJsError; } diff --git a/crates/bitwarden-error/tests/full.rs b/crates/bitwarden-error/tests/full.rs index 7159faaf..b036d64b 100644 --- a/crates/bitwarden-error/tests/full.rs +++ b/crates/bitwarden-error/tests/full.rs @@ -20,7 +20,8 @@ fn converts_to_js() { let simple = SomeError::Baz("This is an error".to_string()); let js_value: JsValue = simple.into(); - // Errors only natively support rust -> js and so we use Reflect to get the value straight from the JSValue + // Errors only natively support rust -> js and so we use Reflect to get the value straight from + // the JSValue let value = js_sys::Reflect::get(&js_value, &JsValue::from("Baz")).unwrap(); assert_eq!(value.as_string().unwrap(), "This is an error"); } From 60a5091a5b191f36d8899b92828d2df35716bef1 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Tue, 12 Nov 2024 15:16:07 +0100 Subject: [PATCH 40/53] fix: clippy issues --- crates/bitwarden-error-macro/src/attribute.rs | 2 +- crates/bitwarden-error-macro/src/basic/derive.rs | 6 ++++-- crates/bitwarden-error-macro/src/flat/derive.rs | 6 ++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/crates/bitwarden-error-macro/src/attribute.rs b/crates/bitwarden-error-macro/src/attribute.rs index a521694b..7b2d4158 100644 --- a/crates/bitwarden-error-macro/src/attribute.rs +++ b/crates/bitwarden-error-macro/src/attribute.rs @@ -35,7 +35,7 @@ pub(crate) fn bitwarden_error( let BitwardenErrorArgs { error_type } = match BitwardenErrorArgs::from_list(&attr_args) { Ok(params) => params, Err(error) => { - return proc_macro::TokenStream::from(darling::Error::from(error).write_errors()); + return proc_macro::TokenStream::from(error.write_errors()); } }; diff --git a/crates/bitwarden-error-macro/src/basic/derive.rs b/crates/bitwarden-error-macro/src/basic/derive.rs index 2fa0c4c1..445577de 100644 --- a/crates/bitwarden-error-macro/src/basic/derive.rs +++ b/crates/bitwarden-error-macro/src/basic/derive.rs @@ -4,7 +4,7 @@ pub(crate) fn basic_error(item: proc_macro::TokenStream) -> proc_macro::TokenStr let input = syn::parse_macro_input!(item as syn::DeriveInput); let type_identifier = &input.ident; - let wasm = cfg!(feature = "wasm").then(|| basic_error_wasm(&type_identifier)); + let wasm = cfg!(feature = "wasm").then(|| basic_error_wasm(type_identifier)); quote! { #wasm } @@ -23,7 +23,9 @@ fn basic_error_wasm(type_identifier: &proc_macro2::Ident) -> proc_macro2::TokenS export function {is_error_function_name}(error: any): error is {type_identifier}; "#"## ); - let ts_code: proc_macro2::TokenStream = ts_code_str.parse().unwrap(); + let ts_code: proc_macro2::TokenStream = ts_code_str + .parse() + .expect("Could not generate TypeScript code"); quote! { const _: () = { diff --git a/crates/bitwarden-error-macro/src/flat/derive.rs b/crates/bitwarden-error-macro/src/flat/derive.rs index 5781c480..0e7efd28 100644 --- a/crates/bitwarden-error-macro/src/flat/derive.rs +++ b/crates/bitwarden-error-macro/src/flat/derive.rs @@ -34,7 +34,7 @@ pub(crate) fn flat_error(item: proc_macro::TokenStream) -> proc_macro::TokenStre }); let wasm = cfg!(feature = "wasm") - .then(|| flat_error_wasm(&type_identifier, &variant_names.collect::>())); + .then(|| flat_error_wasm(type_identifier, &variant_names.collect::>())); quote! { #wasm @@ -77,7 +77,9 @@ fn flat_error_wasm( export function {is_error_function_name}(error: any): error is {type_identifier}; "#"##, ); - let ts_code: proc_macro2::TokenStream = ts_code_str.parse().unwrap(); + let ts_code: proc_macro2::TokenStream = ts_code_str + .parse() + .expect("Could not generate TypeScript code"); quote! { const _: () = { From 159f87b1b415bb2f7775552a9a81d4d21e5ffe09 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Tue, 12 Nov 2024 16:08:09 +0100 Subject: [PATCH 41/53] fix: remove `ToString` implementation in tests --- crates/bitwarden-error/tests/basic.rs | 8 +++++--- crates/bitwarden-error/tests/flat.rs | 28 +++++++++++++-------------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/crates/bitwarden-error/tests/basic.rs b/crates/bitwarden-error/tests/basic.rs index 9b112ea9..fb9248a3 100644 --- a/crates/bitwarden-error/tests/basic.rs +++ b/crates/bitwarden-error/tests/basic.rs @@ -5,14 +5,16 @@ use wasm_bindgen_test::*; #[cfg(feature = "wasm")] #[allow(dead_code)] // Not actually dead, but rust-analyzer doesn't understand `wasm_bindgen_test` fn converts_to_js_error_using_to_string() { + use std::fmt::Display; + use bitwarden_error::prelude::*; use wasm_bindgen::JsValue; #[derive(Debug, BasicError)] struct SomeError; - impl ToString for SomeError { - fn to_string(&self) -> String { - "This is an error".to_string() + impl Display for SomeError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "This is an error") } } diff --git a/crates/bitwarden-error/tests/flat.rs b/crates/bitwarden-error/tests/flat.rs index fc5d85d7..5af9f613 100644 --- a/crates/bitwarden-error/tests/flat.rs +++ b/crates/bitwarden-error/tests/flat.rs @@ -1,3 +1,5 @@ +use std::fmt::Display; + use bitwarden_error::prelude::*; #[cfg(feature = "wasm")] use wasm_bindgen_test::*; @@ -12,9 +14,9 @@ fn variant_for_basic_enum() { Baz, } - impl ToString for SimpleError { - fn to_string(&self) -> String { - format!("{:?}", self) + impl Display for SimpleError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "This is an error") } } @@ -37,10 +39,9 @@ fn variant_for_enum_with_fields() { Bar { x: i32, y: i32 }, Baz(bool, bool), } - - impl ToString for ComplexError { - fn to_string(&self) -> String { - format!("{:?}", self) + impl Display for ComplexError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "This is an error") } } @@ -64,10 +65,9 @@ fn variant_names_for_enum() { Bar, Baz, } - - impl ToString for SimpleError { - fn to_string(&self) -> String { - format!("{:?}", self) + impl Display for SimpleError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "This is an error") } } @@ -93,9 +93,9 @@ fn converts_to_js_error() { Bar, Baz, } - impl ToString for SomeError { - fn to_string(&self) -> String { - "This is an error".to_string() + impl Display for SomeError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "This is an error") } } From 1ef0bfe8e0ab97677054420b9ad733ff7febc3e8 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Tue, 12 Nov 2024 16:17:39 +0100 Subject: [PATCH 42/53] fix: various issues in tests --- crates/bitwarden-error/tests/flat.rs | 12 ++++++------ crates/bitwarden-error/tests/full.rs | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/bitwarden-error/tests/flat.rs b/crates/bitwarden-error/tests/flat.rs index 5af9f613..f9d331db 100644 --- a/crates/bitwarden-error/tests/flat.rs +++ b/crates/bitwarden-error/tests/flat.rs @@ -60,12 +60,12 @@ fn variant_names_for_enum() { #[allow(dead_code)] #[derive(Debug)] #[bitwarden_error(flat)] - enum SimpleError { + enum SimpleEnum { Foo, Bar, Baz, } - impl Display for SimpleError { + impl Display for SimpleEnum { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "This is an error") } @@ -88,22 +88,22 @@ fn converts_to_js_error() { use wasm_bindgen::JsValue; #[derive(Debug, FlatError)] - enum SomeError { + enum FlatEnum { Foo, Bar, Baz, } - impl Display for SomeError { + impl Display for FlatEnum { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "This is an error") } } - let simple = SomeError::Baz; + let simple = FlatEnum::Baz; let js_value: JsValue = simple.into(); let js_error = SdkJsError::from(js_value); - assert_eq!(js_error.name(), "SomeError"); + assert_eq!(js_error.name(), "FlatEnum"); assert_eq!(js_error.message(), "This is an error"); assert_eq!(js_error.variant(), "Baz"); } diff --git a/crates/bitwarden-error/tests/full.rs b/crates/bitwarden-error/tests/full.rs index b036d64b..6b6598a6 100644 --- a/crates/bitwarden-error/tests/full.rs +++ b/crates/bitwarden-error/tests/full.rs @@ -22,6 +22,6 @@ fn converts_to_js() { // Errors only natively support rust -> js and so we use Reflect to get the value straight from // the JSValue - let value = js_sys::Reflect::get(&js_value, &JsValue::from("Baz")).unwrap(); - assert_eq!(value.as_string().unwrap(), "This is an error"); + let value = js_sys::Reflect::get(&js_value, &JsValue::from("Baz")).unwrap_or(JsValue::NULL); + assert_eq!(value.as_string().unwrap_or_default(), "This is an error"); } From 1e6b2e7985408bbff2442fbb9ab151d024550be8 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Wed, 13 Nov 2024 12:01:51 +0100 Subject: [PATCH 43/53] refactor: move everything into attribute macro --- .../src/basic/attribute.rs | 48 +++++++- .../bitwarden-error-macro/src/basic/derive.rs | 54 --------- crates/bitwarden-error-macro/src/basic/mod.rs | 1 - .../src/flat/attribute.rs | 106 ++++++++++++++++- .../bitwarden-error-macro/src/flat/derive.rs | 109 ------------------ crates/bitwarden-error-macro/src/flat/mod.rs | 1 - crates/bitwarden-error-macro/src/lib.rs | 10 -- crates/bitwarden-error/tests/basic.rs | 2 +- crates/bitwarden-error/tests/flat.rs | 5 +- 9 files changed, 152 insertions(+), 184 deletions(-) delete mode 100644 crates/bitwarden-error-macro/src/basic/derive.rs delete mode 100644 crates/bitwarden-error-macro/src/flat/derive.rs diff --git a/crates/bitwarden-error-macro/src/basic/attribute.rs b/crates/bitwarden-error-macro/src/basic/attribute.rs index 48c20fa1..b6fcc60b 100644 --- a/crates/bitwarden-error-macro/src/basic/attribute.rs +++ b/crates/bitwarden-error-macro/src/basic/attribute.rs @@ -1,9 +1,55 @@ use quote::quote; pub(crate) fn bitwarden_error_basic(input: &syn::DeriveInput) -> proc_macro::TokenStream { + let type_identifier = &input.ident; + + let wasm = cfg!(feature = "wasm").then(|| basic_error_wasm(type_identifier)); quote! { - #[derive(BasicError)] #input + + #wasm } .into() } + +fn basic_error_wasm(type_identifier: &proc_macro2::Ident) -> proc_macro2::TokenStream { + let type_identifier_str = type_identifier.to_string(); + let is_error_function_name = format!("is{}", type_identifier); + let ts_code_str = format!( + r##"r#" + export interface {type_identifier} extends Error {{ + name: "{type_identifier}"; + }}; + + export function {is_error_function_name}(error: any): error is {type_identifier}; + "#"## + ); + let ts_code: proc_macro2::TokenStream = ts_code_str + .parse() + .expect("Could not generate TypeScript code"); + + quote! { + const _: () = { + use wasm_bindgen::prelude::*; + + #[wasm_bindgen(typescript_custom_section)] + const TS_APPEND_CONTENT: &'static str = #ts_code; + + #[wasm_bindgen(js_name = #is_error_function_name, skip_typescript)] + pub fn is_error(error: &JsValue) -> bool { + let name_js_value = js_sys::Reflect::get(&error, &JsValue::from_str("name")).unwrap_or(JsValue::NULL); + let name = name_js_value.as_string().unwrap_or_default(); + name == #type_identifier_str + } + + #[automatically_derived] + impl From<#type_identifier> for JsValue { + fn from(error: #type_identifier) -> Self { + let js_error = SdkJsError::new(error.to_string()); + js_error.set_name(#type_identifier_str.to_owned()); + js_error.into() + } + } + }; + } +} diff --git a/crates/bitwarden-error-macro/src/basic/derive.rs b/crates/bitwarden-error-macro/src/basic/derive.rs deleted file mode 100644 index 445577de..00000000 --- a/crates/bitwarden-error-macro/src/basic/derive.rs +++ /dev/null @@ -1,54 +0,0 @@ -use quote::quote; - -pub(crate) fn basic_error(item: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input = syn::parse_macro_input!(item as syn::DeriveInput); - let type_identifier = &input.ident; - - let wasm = cfg!(feature = "wasm").then(|| basic_error_wasm(type_identifier)); - quote! { - #wasm - } - .into() -} - -fn basic_error_wasm(type_identifier: &proc_macro2::Ident) -> proc_macro2::TokenStream { - let type_identifier_str = type_identifier.to_string(); - let is_error_function_name = format!("is{}", type_identifier); - let ts_code_str = format!( - r##"r#" - export interface {type_identifier} extends Error {{ - name: "{type_identifier}"; - }}; - - export function {is_error_function_name}(error: any): error is {type_identifier}; - "#"## - ); - let ts_code: proc_macro2::TokenStream = ts_code_str - .parse() - .expect("Could not generate TypeScript code"); - - quote! { - const _: () = { - use wasm_bindgen::prelude::*; - - #[wasm_bindgen(typescript_custom_section)] - const TS_APPEND_CONTENT: &'static str = #ts_code; - - #[wasm_bindgen(js_name = #is_error_function_name, skip_typescript)] - pub fn is_error(error: &JsValue) -> bool { - let name_js_value = js_sys::Reflect::get(&error, &JsValue::from_str("name")).unwrap_or(JsValue::NULL); - let name = name_js_value.as_string().unwrap_or_default(); - name == #type_identifier_str - } - - #[automatically_derived] - impl From<#type_identifier> for JsValue { - fn from(error: #type_identifier) -> Self { - let js_error = SdkJsError::new(error.to_string()); - js_error.set_name(#type_identifier_str.to_owned()); - js_error.into() - } - } - }; - } -} diff --git a/crates/bitwarden-error-macro/src/basic/mod.rs b/crates/bitwarden-error-macro/src/basic/mod.rs index 6e66ace8..8d46249f 100644 --- a/crates/bitwarden-error-macro/src/basic/mod.rs +++ b/crates/bitwarden-error-macro/src/basic/mod.rs @@ -1,2 +1 @@ pub mod attribute; -pub mod derive; diff --git a/crates/bitwarden-error-macro/src/flat/attribute.rs b/crates/bitwarden-error-macro/src/flat/attribute.rs index 0683482a..fff4bb7a 100644 --- a/crates/bitwarden-error-macro/src/flat/attribute.rs +++ b/crates/bitwarden-error-macro/src/flat/attribute.rs @@ -1,9 +1,109 @@ use quote::quote; +use syn::Data; pub(crate) fn bitwarden_error_flat(input: &syn::DeriveInput) -> proc_macro::TokenStream { + let type_identifier = &input.ident; + + match &input.data { + Data::Enum(data) => { + let variant_names = data.variants.iter().map(|variant| &variant.ident); + + let match_arms = data.variants.iter().map(|variant| match variant.fields { + syn::Fields::Unit => { + let variant_ident = &variant.ident; + let variant_name = format!("{}", variant_ident); + quote! { + #type_identifier::#variant_ident => #variant_name + } + } + syn::Fields::Named(_) => { + let variant_ident = &variant.ident; + let variant_name = format!("{}", variant_ident); + quote! { + #type_identifier::#variant_ident { .. } => #variant_name + } + } + syn::Fields::Unnamed(_) => { + let variant_ident = &variant.ident; + let variant_name = format!("{}", variant_ident); + quote! { + #type_identifier::#variant_ident(..) => #variant_name + } + } + }); + + let wasm = cfg!(feature = "wasm") + .then(|| flat_error_wasm(type_identifier, &variant_names.collect::>())); + + quote! { + #input + #wasm + + #[automatically_derived] + impl FlatError for #type_identifier { + fn error_variant(&self) -> &'static str { + match &self { + #(#match_arms), * + } + } + } + } + .into() + } + _ => syn::Error::new_spanned(input, "bitwarden_error can only be used with enums") + .to_compile_error() + .into(), + } +} + +fn flat_error_wasm( + type_identifier: &proc_macro2::Ident, + variant_names: &[&proc_macro2::Ident], +) -> proc_macro2::TokenStream { + let type_identifier_str = type_identifier.to_string(); + let is_error_function_name = format!("is{}", type_identifier); + let ts_variant_names = variant_names + .iter() + .map(|vn| format!(r#""{vn}""#)) + .collect::>() + .join("|"); + let ts_code_str = format!( + r##"r#" + export interface {type_identifier} extends Error {{ + name: "{type_identifier}"; + variant: {ts_variant_names}; + }}; + + export function {is_error_function_name}(error: any): error is {type_identifier}; + "#"##, + ); + let ts_code: proc_macro2::TokenStream = ts_code_str + .parse() + .expect("Could not generate TypeScript code"); + quote! { - #[derive(FlatError)] - #input + const _: () = { + use wasm_bindgen::prelude::*; + + #[wasm_bindgen(typescript_custom_section)] + const TS_APPEND_CONTENT: &'static str = #ts_code; + + #[wasm_bindgen(js_name = #is_error_function_name, skip_typescript)] + pub fn is_error(error: &JsValue) -> bool { + let name_js_value = js_sys::Reflect::get(&error, &JsValue::from_str("name")).unwrap_or(JsValue::NULL); + let name = name_js_value.as_string().unwrap_or_default(); + name == #type_identifier_str + } + + #[automatically_derived] + impl From<#type_identifier> for JsValue { + fn from(error: #type_identifier) -> Self { + let js_error = SdkJsError::new(error.to_string()); + js_error.set_name(stringify!(#type_identifier).to_owned()); + js_error.set_variant(error.error_variant().to_owned()); + js_error.into() + } + } + }; } - .into() } diff --git a/crates/bitwarden-error-macro/src/flat/derive.rs b/crates/bitwarden-error-macro/src/flat/derive.rs deleted file mode 100644 index 0e7efd28..00000000 --- a/crates/bitwarden-error-macro/src/flat/derive.rs +++ /dev/null @@ -1,109 +0,0 @@ -use quote::quote; -use syn::Data; - -pub(crate) fn flat_error(item: proc_macro::TokenStream) -> proc_macro::TokenStream { - let input = syn::parse_macro_input!(item as syn::DeriveInput); - let type_identifier = &input.ident; - - match &input.data { - Data::Enum(data) => { - let variant_names = data.variants.iter().map(|variant| &variant.ident); - - let match_arms = data.variants.iter().map(|variant| match variant.fields { - syn::Fields::Unit => { - let variant_ident = &variant.ident; - let variant_name = format!("{}", variant_ident); - quote! { - #type_identifier::#variant_ident => #variant_name - } - } - syn::Fields::Named(_) => { - let variant_ident = &variant.ident; - let variant_name = format!("{}", variant_ident); - quote! { - #type_identifier::#variant_ident { .. } => #variant_name - } - } - syn::Fields::Unnamed(_) => { - let variant_ident = &variant.ident; - let variant_name = format!("{}", variant_ident); - quote! { - #type_identifier::#variant_ident(..) => #variant_name - } - } - }); - - let wasm = cfg!(feature = "wasm") - .then(|| flat_error_wasm(type_identifier, &variant_names.collect::>())); - - quote! { - #wasm - - #[automatically_derived] - impl FlatError for #type_identifier { - fn error_variant(&self) -> &'static str { - match &self { - #(#match_arms), * - } - } - } - } - .into() - } - _ => syn::Error::new_spanned(input, "bitwarden_error can only be used with enums") - .to_compile_error() - .into(), - } -} - -fn flat_error_wasm( - type_identifier: &proc_macro2::Ident, - variant_names: &[&proc_macro2::Ident], -) -> proc_macro2::TokenStream { - let type_identifier_str = type_identifier.to_string(); - let is_error_function_name = format!("is{}", type_identifier); - let ts_variant_names = variant_names - .iter() - .map(|vn| format!(r#""{vn}""#)) - .collect::>() - .join("|"); - let ts_code_str = format!( - r##"r#" - export interface {type_identifier} extends Error {{ - name: "{type_identifier}"; - variant: {ts_variant_names}; - }}; - - export function {is_error_function_name}(error: any): error is {type_identifier}; - "#"##, - ); - let ts_code: proc_macro2::TokenStream = ts_code_str - .parse() - .expect("Could not generate TypeScript code"); - - quote! { - const _: () = { - use wasm_bindgen::prelude::*; - - #[wasm_bindgen(typescript_custom_section)] - const TS_APPEND_CONTENT: &'static str = #ts_code; - - #[wasm_bindgen(js_name = #is_error_function_name, skip_typescript)] - pub fn is_error(error: &JsValue) -> bool { - let name_js_value = js_sys::Reflect::get(&error, &JsValue::from_str("name")).unwrap_or(JsValue::NULL); - let name = name_js_value.as_string().unwrap_or_default(); - name == #type_identifier_str - } - - #[automatically_derived] - impl From<#type_identifier> for JsValue { - fn from(error: #type_identifier) -> Self { - let js_error = SdkJsError::new(error.to_string()); - js_error.set_name(stringify!(#type_identifier).to_owned()); - js_error.set_variant(error.error_variant().to_owned()); - js_error.into() - } - } - }; - } -} diff --git a/crates/bitwarden-error-macro/src/flat/mod.rs b/crates/bitwarden-error-macro/src/flat/mod.rs index 6e66ace8..8d46249f 100644 --- a/crates/bitwarden-error-macro/src/flat/mod.rs +++ b/crates/bitwarden-error-macro/src/flat/mod.rs @@ -1,2 +1 @@ pub mod attribute; -pub mod derive; diff --git a/crates/bitwarden-error-macro/src/lib.rs b/crates/bitwarden-error-macro/src/lib.rs index ab662a97..9addbc98 100644 --- a/crates/bitwarden-error-macro/src/lib.rs +++ b/crates/bitwarden-error-macro/src/lib.rs @@ -10,13 +10,3 @@ pub fn bitwarden_error( ) -> proc_macro::TokenStream { attribute::bitwarden_error(args, item) } - -#[proc_macro_derive(BasicError)] -pub fn basic_error(item: proc_macro::TokenStream) -> proc_macro::TokenStream { - basic::derive::basic_error(item) -} - -#[proc_macro_derive(FlatError)] -pub fn flat_error(item: proc_macro::TokenStream) -> proc_macro::TokenStream { - flat::derive::flat_error(item) -} diff --git a/crates/bitwarden-error/tests/basic.rs b/crates/bitwarden-error/tests/basic.rs index fb9248a3..6789511a 100644 --- a/crates/bitwarden-error/tests/basic.rs +++ b/crates/bitwarden-error/tests/basic.rs @@ -10,7 +10,7 @@ fn converts_to_js_error_using_to_string() { use bitwarden_error::prelude::*; use wasm_bindgen::JsValue; - #[derive(Debug, BasicError)] + #[bitwarden_error(basic)] struct SomeError; impl Display for SomeError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/crates/bitwarden-error/tests/flat.rs b/crates/bitwarden-error/tests/flat.rs index f9d331db..75e784fe 100644 --- a/crates/bitwarden-error/tests/flat.rs +++ b/crates/bitwarden-error/tests/flat.rs @@ -6,7 +6,6 @@ use wasm_bindgen_test::*; #[test] fn variant_for_basic_enum() { - #[derive(Debug)] #[bitwarden_error(flat)] enum SimpleError { Foo, @@ -32,7 +31,6 @@ fn variant_for_basic_enum() { #[test] fn variant_for_enum_with_fields() { #[allow(dead_code)] - #[derive(Debug)] #[bitwarden_error(flat)] enum ComplexError { Foo(String), @@ -58,7 +56,6 @@ fn variant_for_enum_with_fields() { #[cfg(feature = "wasm")] fn variant_names_for_enum() { #[allow(dead_code)] - #[derive(Debug)] #[bitwarden_error(flat)] enum SimpleEnum { Foo, @@ -87,7 +84,7 @@ fn variant_names_for_enum() { fn converts_to_js_error() { use wasm_bindgen::JsValue; - #[derive(Debug, FlatError)] + #[bitwarden_error(flat)] enum FlatEnum { Foo, Bar, From 5cfa4d032111d3c3cf4854e4ff0265c9f85dad92 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Wed, 13 Nov 2024 13:42:07 +0100 Subject: [PATCH 44/53] feat: allow custom export names this is mostly because of `bitwarden_core::Error` which exports as `Error` and overrides the JS `Error` type. --- crates/bitwarden-error-macro/src/args.rs | 25 +++++++++ crates/bitwarden-error-macro/src/attribute.rs | 51 ++++++++++--------- .../src/basic/attribute.rs | 30 ++++++----- .../src/flat/attribute.rs | 32 +++++++----- .../src/full/attribute.rs | 6 ++- crates/bitwarden-error-macro/src/lib.rs | 1 + crates/bitwarden-error/tests/basic.rs | 25 +++++++++ crates/bitwarden-error/tests/flat.rs | 27 ++++++++++ 8 files changed, 147 insertions(+), 50 deletions(-) create mode 100644 crates/bitwarden-error-macro/src/args.rs diff --git a/crates/bitwarden-error-macro/src/args.rs b/crates/bitwarden-error-macro/src/args.rs new file mode 100644 index 00000000..57fb9fcf --- /dev/null +++ b/crates/bitwarden-error-macro/src/args.rs @@ -0,0 +1,25 @@ +use darling::FromMeta; + +#[derive(FromMeta)] +pub(crate) struct BitwardenErrorArgs { + #[darling(flatten)] + pub error_type: BitwardenErrorType, + + #[darling(default)] + pub export_as: Option, +} + +#[derive(FromMeta)] +pub(crate) enum BitwardenErrorType { + /// The error is going to be converted into a string using the `ToString` trait + #[darling(rename = "basic")] + Basic, + + /// The error is going to be converted into a flat error using the `FlatError` trait + #[darling(rename = "flat")] + Flat, + + /// The entire error stack is going to be made available using `serde` + #[darling(rename = "full")] + Full, +} diff --git a/crates/bitwarden-error-macro/src/attribute.rs b/crates/bitwarden-error-macro/src/attribute.rs index 7b2d4158..a62593af 100644 --- a/crates/bitwarden-error-macro/src/attribute.rs +++ b/crates/bitwarden-error-macro/src/attribute.rs @@ -1,25 +1,7 @@ use darling::{ast::NestedMeta, FromMeta}; +use quote::format_ident; -#[derive(FromMeta)] -struct BitwardenErrorArgs { - #[darling(flatten)] - error_type: BitwardenErrorType, -} - -#[derive(FromMeta)] -enum BitwardenErrorType { - /// The error is going to be converted into a string using the `ToString` trait - #[darling(rename = "basic")] - Basic, - - /// The error is going to be converted into a flat error using the `FlatError` trait - #[darling(rename = "flat")] - Flat, - - /// The entire error stack is going to be made available using `serde` - #[darling(rename = "full")] - Full, -} +use crate::args::{BitwardenErrorArgs, BitwardenErrorType}; pub(crate) fn bitwarden_error( args: proc_macro::TokenStream, @@ -32,7 +14,7 @@ pub(crate) fn bitwarden_error( } }; - let BitwardenErrorArgs { error_type } = match BitwardenErrorArgs::from_list(&attr_args) { + let args = match BitwardenErrorArgs::from_list(&attr_args) { Ok(params) => params, Err(error) => { return proc_macro::TokenStream::from(error.write_errors()); @@ -40,9 +22,28 @@ pub(crate) fn bitwarden_error( }; let input = syn::parse_macro_input!(item as syn::DeriveInput); - match error_type { - BitwardenErrorType::Basic => crate::basic::attribute::bitwarden_error_basic(&input), - BitwardenErrorType::Flat => crate::flat::attribute::bitwarden_error_flat(&input), - BitwardenErrorType::Full => crate::full::attribute::bitwarden_error_full(&input), + let type_identifier = &input.ident; + let export_as_identifier = &args + .export_as + .as_ref() + .map(|export_as| format_ident!("{}", export_as)) + .unwrap_or(input.ident.clone()); + + match args.error_type { + BitwardenErrorType::Basic => crate::basic::attribute::bitwarden_error_basic( + &input, + type_identifier, + export_as_identifier, + ), + BitwardenErrorType::Flat => crate::flat::attribute::bitwarden_error_flat( + &input, + type_identifier, + export_as_identifier, + ), + BitwardenErrorType::Full => crate::full::attribute::bitwarden_error_full( + &input, + type_identifier, + export_as_identifier, + ), } } diff --git a/crates/bitwarden-error-macro/src/basic/attribute.rs b/crates/bitwarden-error-macro/src/basic/attribute.rs index b6fcc60b..b060517f 100644 --- a/crates/bitwarden-error-macro/src/basic/attribute.rs +++ b/crates/bitwarden-error-macro/src/basic/attribute.rs @@ -1,9 +1,12 @@ use quote::quote; -pub(crate) fn bitwarden_error_basic(input: &syn::DeriveInput) -> proc_macro::TokenStream { - let type_identifier = &input.ident; - - let wasm = cfg!(feature = "wasm").then(|| basic_error_wasm(type_identifier)); +pub(crate) fn bitwarden_error_basic( + input: &syn::DeriveInput, + type_identifier: &proc_macro2::Ident, + export_as_identifier: &proc_macro2::Ident, +) -> proc_macro::TokenStream { + let wasm = + cfg!(feature = "wasm").then(|| basic_error_wasm(type_identifier, export_as_identifier)); quote! { #input @@ -12,16 +15,19 @@ pub(crate) fn bitwarden_error_basic(input: &syn::DeriveInput) -> proc_macro::Tok .into() } -fn basic_error_wasm(type_identifier: &proc_macro2::Ident) -> proc_macro2::TokenStream { - let type_identifier_str = type_identifier.to_string(); - let is_error_function_name = format!("is{}", type_identifier); +fn basic_error_wasm( + type_identifier: &proc_macro2::Ident, + export_as_identifier: &proc_macro2::Ident, +) -> proc_macro2::TokenStream { + let export_as_identifier_str = export_as_identifier.to_string(); + let is_error_function_name = format!("is{}", export_as_identifier); let ts_code_str = format!( r##"r#" - export interface {type_identifier} extends Error {{ - name: "{type_identifier}"; + export interface {export_as_identifier} extends Error {{ + name: "{export_as_identifier}"; }}; - export function {is_error_function_name}(error: any): error is {type_identifier}; + export function {is_error_function_name}(error: any): error is {export_as_identifier}; "#"## ); let ts_code: proc_macro2::TokenStream = ts_code_str @@ -39,14 +45,14 @@ fn basic_error_wasm(type_identifier: &proc_macro2::Ident) -> proc_macro2::TokenS pub fn is_error(error: &JsValue) -> bool { let name_js_value = js_sys::Reflect::get(&error, &JsValue::from_str("name")).unwrap_or(JsValue::NULL); let name = name_js_value.as_string().unwrap_or_default(); - name == #type_identifier_str + name == #export_as_identifier_str } #[automatically_derived] impl From<#type_identifier> for JsValue { fn from(error: #type_identifier) -> Self { let js_error = SdkJsError::new(error.to_string()); - js_error.set_name(#type_identifier_str.to_owned()); + js_error.set_name(#export_as_identifier_str.to_owned()); js_error.into() } } diff --git a/crates/bitwarden-error-macro/src/flat/attribute.rs b/crates/bitwarden-error-macro/src/flat/attribute.rs index fff4bb7a..3d05031a 100644 --- a/crates/bitwarden-error-macro/src/flat/attribute.rs +++ b/crates/bitwarden-error-macro/src/flat/attribute.rs @@ -1,9 +1,11 @@ use quote::quote; use syn::Data; -pub(crate) fn bitwarden_error_flat(input: &syn::DeriveInput) -> proc_macro::TokenStream { - let type_identifier = &input.ident; - +pub(crate) fn bitwarden_error_flat( + input: &syn::DeriveInput, + type_identifier: &proc_macro2::Ident, + export_as_identifier: &proc_macro2::Ident, +) -> proc_macro::TokenStream { match &input.data { Data::Enum(data) => { let variant_names = data.variants.iter().map(|variant| &variant.ident); @@ -32,8 +34,13 @@ pub(crate) fn bitwarden_error_flat(input: &syn::DeriveInput) -> proc_macro::Toke } }); - let wasm = cfg!(feature = "wasm") - .then(|| flat_error_wasm(type_identifier, &variant_names.collect::>())); + let wasm = cfg!(feature = "wasm").then(|| { + flat_error_wasm( + type_identifier, + export_as_identifier, + &variant_names.collect::>(), + ) + }); quote! { #input @@ -58,10 +65,11 @@ pub(crate) fn bitwarden_error_flat(input: &syn::DeriveInput) -> proc_macro::Toke fn flat_error_wasm( type_identifier: &proc_macro2::Ident, + export_as_identifier: &proc_macro2::Ident, variant_names: &[&proc_macro2::Ident], ) -> proc_macro2::TokenStream { - let type_identifier_str = type_identifier.to_string(); - let is_error_function_name = format!("is{}", type_identifier); + let export_as_identifier_str = export_as_identifier.to_string(); + let is_error_function_name = format!("is{}", export_as_identifier); let ts_variant_names = variant_names .iter() .map(|vn| format!(r#""{vn}""#)) @@ -69,12 +77,12 @@ fn flat_error_wasm( .join("|"); let ts_code_str = format!( r##"r#" - export interface {type_identifier} extends Error {{ - name: "{type_identifier}"; + export interface {export_as_identifier_str} extends Error {{ + name: "{export_as_identifier_str}"; variant: {ts_variant_names}; }}; - export function {is_error_function_name}(error: any): error is {type_identifier}; + export function {is_error_function_name}(error: any): error is {export_as_identifier_str}; "#"##, ); let ts_code: proc_macro2::TokenStream = ts_code_str @@ -92,14 +100,14 @@ fn flat_error_wasm( pub fn is_error(error: &JsValue) -> bool { let name_js_value = js_sys::Reflect::get(&error, &JsValue::from_str("name")).unwrap_or(JsValue::NULL); let name = name_js_value.as_string().unwrap_or_default(); - name == #type_identifier_str + name == #export_as_identifier_str } #[automatically_derived] impl From<#type_identifier> for JsValue { fn from(error: #type_identifier) -> Self { let js_error = SdkJsError::new(error.to_string()); - js_error.set_name(stringify!(#type_identifier).to_owned()); + js_error.set_name(#export_as_identifier_str.to_owned()); js_error.set_variant(error.error_variant().to_owned()); js_error.into() } diff --git a/crates/bitwarden-error-macro/src/full/attribute.rs b/crates/bitwarden-error-macro/src/full/attribute.rs index 82d7ae2a..3325aa81 100644 --- a/crates/bitwarden-error-macro/src/full/attribute.rs +++ b/crates/bitwarden-error-macro/src/full/attribute.rs @@ -1,6 +1,10 @@ use quote::quote; -pub(crate) fn bitwarden_error_full(input: &syn::DeriveInput) -> proc_macro::TokenStream { +pub(crate) fn bitwarden_error_full( + input: &syn::DeriveInput, + _type_identifier: &proc_macro2::Ident, + _export_as_identifier: &proc_macro2::Ident, +) -> proc_macro::TokenStream { let wasm_attributes = cfg!(feature = "wasm").then(|| { quote! { #[derive(tsify_next::Tsify)] diff --git a/crates/bitwarden-error-macro/src/lib.rs b/crates/bitwarden-error-macro/src/lib.rs index 9addbc98..5844fbf5 100644 --- a/crates/bitwarden-error-macro/src/lib.rs +++ b/crates/bitwarden-error-macro/src/lib.rs @@ -1,3 +1,4 @@ +mod args; mod attribute; mod basic; mod flat; diff --git a/crates/bitwarden-error/tests/basic.rs b/crates/bitwarden-error/tests/basic.rs index 6789511a..f029edd6 100644 --- a/crates/bitwarden-error/tests/basic.rs +++ b/crates/bitwarden-error/tests/basic.rs @@ -25,3 +25,28 @@ fn converts_to_js_error_using_to_string() { assert_eq!(js_error.name(), "SomeError"); assert_eq!(js_error.message(), "This is an error"); } + +#[wasm_bindgen_test] +#[cfg(feature = "wasm")] +#[allow(dead_code)] // Not actually dead, but rust-analyzer doesn't understand `wasm_bindgen_test` +fn outputs_different_name_when_given_export_as() { + use std::fmt::Display; + + use bitwarden_error::prelude::*; + use wasm_bindgen::JsValue; + + #[bitwarden_error(basic, export_as = "SomeOtherError")] + struct SomeError; + impl Display for SomeError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "This is an error") + } + } + + let simple = SomeError; + let js_value: JsValue = simple.into(); + + let js_error = SdkJsError::from(js_value); + assert_eq!(js_error.name(), "SomeOtherError"); + assert_eq!(js_error.message(), "This is an error"); +} diff --git a/crates/bitwarden-error/tests/flat.rs b/crates/bitwarden-error/tests/flat.rs index 75e784fe..e9ed0146 100644 --- a/crates/bitwarden-error/tests/flat.rs +++ b/crates/bitwarden-error/tests/flat.rs @@ -104,3 +104,30 @@ fn converts_to_js_error() { assert_eq!(js_error.message(), "This is an error"); assert_eq!(js_error.variant(), "Baz"); } + +#[wasm_bindgen_test] +#[cfg(feature = "wasm")] +#[allow(dead_code)] // Not actually dead, but rust-analyzer doesn't understand `wasm_bindgen_test` +fn outputs_different_name_when_given_export_as() { + use wasm_bindgen::JsValue; + + #[bitwarden_error(flat, export_as = "SomeOtherEnum")] + enum FlatEnum { + Foo, + Bar, + Baz, + } + impl Display for FlatEnum { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "This is an error") + } + } + + let simple = FlatEnum::Baz; + let js_value: JsValue = simple.into(); + + let js_error = SdkJsError::from(js_value); + assert_eq!(js_error.name(), "SomeOtherEnum"); + assert_eq!(js_error.message(), "This is an error"); + assert_eq!(js_error.variant(), "Baz"); +} From 7b627cb72ef28698853d658a59dfa6edce008ca3 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Wed, 13 Nov 2024 14:10:02 +0100 Subject: [PATCH 45/53] feat: add compilation error for `export_as` with `full` --- Cargo.lock | 87 ++++++++++++++++++- .../src/full/attribute.rs | 11 ++- crates/bitwarden-error/Cargo.toml | 1 + .../tests/compilation_tests/full.rs | 7 ++ .../tests/compilation_tests/full.stderr | 7 ++ crates/bitwarden-error/tests/mod.rs | 6 ++ 6 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 crates/bitwarden-error/tests/compilation_tests/full.rs create mode 100644 crates/bitwarden-error/tests/compilation_tests/full.stderr diff --git a/Cargo.lock b/Cargo.lock index 080a25d5..cc6239ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -422,6 +422,7 @@ dependencies = [ "bitwarden-error-macro", "js-sys", "serde", + "trybuild", "tsify-next", "wasm-bindgen", "wasm-bindgen-test", @@ -3026,6 +3027,15 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -3306,6 +3316,12 @@ dependencies = [ "futures-core", ] +[[package]] +name = "target-triple" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42a4d50cdb458045afc8131fd91b64904da29548bcb63c7236e0844936c13078" + [[package]] name = "tempfile" version = "3.13.0" @@ -3319,6 +3335,15 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + [[package]] name = "textwrap" version = "0.16.1" @@ -3474,6 +3499,40 @@ dependencies = [ "serde", ] +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap 2.6.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tower-service" version = "0.3.3" @@ -3527,6 +3586,21 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "trybuild" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dcd332a5496c026f1e14b7f3d2b7bd98e509660c04239c58b0ba38a12daded4" +dependencies = [ + "glob", + "serde", + "serde_derive", + "serde_json", + "target-triple", + "termcolor", + "toml 0.8.19", +] + [[package]] name = "tsify-next" version = "0.5.4" @@ -3639,7 +3713,7 @@ dependencies = [ "paste", "serde", "textwrap", - "toml", + "toml 0.5.11", "uniffi_meta", "uniffi_udl", ] @@ -3695,7 +3769,7 @@ dependencies = [ "quote", "serde", "syn 2.0.87", - "toml", + "toml 0.5.11", "uniffi_meta", ] @@ -4178,6 +4252,15 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +dependencies = [ + "memchr", +] + [[package]] name = "wiremock" version = "0.6.2" diff --git a/crates/bitwarden-error-macro/src/full/attribute.rs b/crates/bitwarden-error-macro/src/full/attribute.rs index 3325aa81..90acbcf9 100644 --- a/crates/bitwarden-error-macro/src/full/attribute.rs +++ b/crates/bitwarden-error-macro/src/full/attribute.rs @@ -1,10 +1,17 @@ +use darling::Error; use quote::quote; pub(crate) fn bitwarden_error_full( input: &syn::DeriveInput, - _type_identifier: &proc_macro2::Ident, - _export_as_identifier: &proc_macro2::Ident, + type_identifier: &proc_macro2::Ident, + export_as_identifier: &proc_macro2::Ident, ) -> proc_macro::TokenStream { + if type_identifier != export_as_identifier { + return Error::custom("`bitwarden_error(full)` does not currently support `export_as`") + .write_errors() + .into(); + } + let wasm_attributes = cfg!(feature = "wasm").then(|| { quote! { #[derive(tsify_next::Tsify)] diff --git a/crates/bitwarden-error/Cargo.toml b/crates/bitwarden-error/Cargo.toml index f48343e2..0f013def 100644 --- a/crates/bitwarden-error/Cargo.toml +++ b/crates/bitwarden-error/Cargo.toml @@ -22,5 +22,6 @@ workspace = true [dev-dependencies] js-sys = "0.3.72" serde.workspace = true +trybuild = "1.0.101" tsify-next = "0.5.4" wasm-bindgen-test = "0.3.45" diff --git a/crates/bitwarden-error/tests/compilation_tests/full.rs b/crates/bitwarden-error/tests/compilation_tests/full.rs new file mode 100644 index 00000000..9e8d3125 --- /dev/null +++ b/crates/bitwarden-error/tests/compilation_tests/full.rs @@ -0,0 +1,7 @@ +use bitwarden_error::prelude::*; + +/// Full errors do not support changing the name of the error in the generated JS +#[bitwarden_error(full, export_as = "SomeOtherError")] +struct SomeError; + +fn main() {} diff --git a/crates/bitwarden-error/tests/compilation_tests/full.stderr b/crates/bitwarden-error/tests/compilation_tests/full.stderr new file mode 100644 index 00000000..f25ad6ce --- /dev/null +++ b/crates/bitwarden-error/tests/compilation_tests/full.stderr @@ -0,0 +1,7 @@ +error: `bitwarden_error(full)` does not currently support `export_as` + --> tests/compilation_tests/full.rs:4:1 + | +4 | #[bitwarden_error(full, export_as = "SomeOtherError")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the attribute macro `bitwarden_error` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/bitwarden-error/tests/mod.rs b/crates/bitwarden-error/tests/mod.rs index 3a3c2621..b4c7c258 100644 --- a/crates/bitwarden-error/tests/mod.rs +++ b/crates/bitwarden-error/tests/mod.rs @@ -1,3 +1,9 @@ mod basic; mod flat; mod full; + +#[test] +fn compilation_tests() { + let t = trybuild::TestCases::new(); + t.compile_fail("tests/compilation_tests/*.rs"); +} From 5c74f88ca5f4924889e213ec5b0b286bfd48b2a9 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Wed, 13 Nov 2024 14:12:50 +0100 Subject: [PATCH 46/53] feat: change core error export name --- crates/bitwarden-core/src/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-core/src/error.rs b/crates/bitwarden-core/src/error.rs index b39319c9..c627f374 100644 --- a/crates/bitwarden-core/src/error.rs +++ b/crates/bitwarden-core/src/error.rs @@ -13,7 +13,7 @@ use validator::ValidationErrors; #[cfg(feature = "internal")] use crate::client::encryption_settings::EncryptionSettingsError; -#[bitwarden_error(flat)] +#[bitwarden_error(flat, export_as = "CoreError")] #[derive(Debug, Error)] pub enum Error { #[error(transparent)] From e4ac54c9995d677f9d7ca45d3120bc232659fe46 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Fri, 15 Nov 2024 10:01:51 +0100 Subject: [PATCH 47/53] refactor: minimize duplication --- .../src/flat/attribute.rs | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/crates/bitwarden-error-macro/src/flat/attribute.rs b/crates/bitwarden-error-macro/src/flat/attribute.rs index 3d05031a..9d6d058b 100644 --- a/crates/bitwarden-error-macro/src/flat/attribute.rs +++ b/crates/bitwarden-error-macro/src/flat/attribute.rs @@ -9,27 +9,25 @@ pub(crate) fn bitwarden_error_flat( match &input.data { Data::Enum(data) => { let variant_names = data.variants.iter().map(|variant| &variant.ident); + let match_arms = data.variants.iter().map(|variant| { + let variant_ident = &variant.ident; + let variant_str = variant_ident.to_string(); - let match_arms = data.variants.iter().map(|variant| match variant.fields { - syn::Fields::Unit => { - let variant_ident = &variant.ident; - let variant_name = format!("{}", variant_ident); - quote! { - #type_identifier::#variant_ident => #variant_name + match variant.fields { + syn::Fields::Unit => { + quote! { + #type_identifier::#variant_ident => #variant_str + } } - } - syn::Fields::Named(_) => { - let variant_ident = &variant.ident; - let variant_name = format!("{}", variant_ident); - quote! { - #type_identifier::#variant_ident { .. } => #variant_name + syn::Fields::Named(_) => { + quote! { + #type_identifier::#variant_ident { .. } => #variant_str + } } - } - syn::Fields::Unnamed(_) => { - let variant_ident = &variant.ident; - let variant_name = format!("{}", variant_ident); - quote! { - #type_identifier::#variant_ident(..) => #variant_name + syn::Fields::Unnamed(_) => { + quote! { + #type_identifier::#variant_ident(..) => #variant_str + } } } }); From ebaaeefb656b42b2868c0691fcda386d619e5bca Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Fri, 15 Nov 2024 10:03:43 +0100 Subject: [PATCH 48/53] refactor: use qualified trait name --- crates/bitwarden-error-macro/src/flat/attribute.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bitwarden-error-macro/src/flat/attribute.rs b/crates/bitwarden-error-macro/src/flat/attribute.rs index 9d6d058b..b77791fa 100644 --- a/crates/bitwarden-error-macro/src/flat/attribute.rs +++ b/crates/bitwarden-error-macro/src/flat/attribute.rs @@ -45,7 +45,7 @@ pub(crate) fn bitwarden_error_flat( #wasm #[automatically_derived] - impl FlatError for #type_identifier { + impl ::bitwarden_error::prelude::FlatError for #type_identifier { fn error_variant(&self) -> &'static str { match &self { #(#match_arms), * From a3db63fa02a3d026dda4bc80b6554edb95132a41 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Fri, 15 Nov 2024 10:06:41 +0100 Subject: [PATCH 49/53] refactor: reduce rename duplication --- crates/bitwarden-error-macro/src/args.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/bitwarden-error-macro/src/args.rs b/crates/bitwarden-error-macro/src/args.rs index 57fb9fcf..aa70fbfa 100644 --- a/crates/bitwarden-error-macro/src/args.rs +++ b/crates/bitwarden-error-macro/src/args.rs @@ -10,16 +10,14 @@ pub(crate) struct BitwardenErrorArgs { } #[derive(FromMeta)] +#[darling(rename_all = "snake_case")] pub(crate) enum BitwardenErrorType { /// The error is going to be converted into a string using the `ToString` trait - #[darling(rename = "basic")] Basic, /// The error is going to be converted into a flat error using the `FlatError` trait - #[darling(rename = "flat")] Flat, /// The entire error stack is going to be made available using `serde` - #[darling(rename = "full")] Full, } From e1fdbe43289b61759268ae3ffdf83897d56e022b Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Fri, 15 Nov 2024 10:57:05 +0100 Subject: [PATCH 50/53] feat: add documentation to macro --- Cargo.lock | 3 + crates/bitwarden-error-macro/Cargo.toml | 5 ++ crates/bitwarden-error-macro/src/lib.rs | 97 +++++++++++++++++++++++++ 3 files changed, 105 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index cc6239ab..a218683d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -432,10 +432,13 @@ dependencies = [ name = "bitwarden-error-macro" version = "1.0.0" dependencies = [ + "bitwarden-error", "darling", "proc-macro2", "quote", + "serde", "syn 2.0.87", + "thiserror", ] [[package]] diff --git a/crates/bitwarden-error-macro/Cargo.toml b/crates/bitwarden-error-macro/Cargo.toml index 8fed98b3..f4746f0c 100644 --- a/crates/bitwarden-error-macro/Cargo.toml +++ b/crates/bitwarden-error-macro/Cargo.toml @@ -23,3 +23,8 @@ workspace = true [lib] proc-macro = true + +[dev-dependencies] +bitwarden-error.workspace = true +serde.workspace = true +thiserror.workspace = true diff --git a/crates/bitwarden-error-macro/src/lib.rs b/crates/bitwarden-error-macro/src/lib.rs index 5844fbf5..1532c4a1 100644 --- a/crates/bitwarden-error-macro/src/lib.rs +++ b/crates/bitwarden-error-macro/src/lib.rs @@ -4,6 +4,103 @@ mod basic; mod flat; mod full; +/// A procedural macro for generating error types with customizable serialization behavior. +/// +/// # Attributes +/// +/// ## Error type + +/// - `basic`: The error is converted into a string using the `ToString` trait. +/// - `flat`: The error is converted into a flat structure using the `FlatError` trait. +/// - `full`: The entire error stack is made available using `serde`. +/// +/// ## Export as +/// +/// `export_as`: The name of the exported TypeScript type. If not provided, the name of the Rust type is used. +/// Note: This attribute is only available when using the `basic` and `flat` error types. +/// +/// # Examples +/// +/// ## Basic +/// Using the `basic` error type: +/// +/// ```rust +/// use bitwarden_error::prelude::*; +/// +/// #[bitwarden_error(basic)] +/// enum MyError { +/// NotFound, +/// PermissionDenied, +/// } +/// ``` +/// +/// will generate the following TypeScript definition: +/// +/// ```typescript +/// export interface MyError extends Error { +/// name: "MyError"; +/// } +/// ``` +/// +/// ## Flat +/// +/// Using the `flat` error type: +/// +/// ```rust +/// use bitwarden_error::prelude::*; +/// +/// #[bitwarden_error(basic)] +/// enum MyError { +/// NotFound, +/// PermissionDenied, +/// } +/// ``` +/// +/// will generate the following TypeScript definition: +/// +/// ```typescript +/// export interface MyError extends Error { +/// name: "MyError"; +/// variant: "NotFound" | "PermissionDenied"; +/// } +/// ``` +/// +/// Using the `full` error type: +/// +/// ```rust +/// use bitwarden_error::prelude::*; +/// use serde::Serialize; +/// use thiserror::Error; +/// +/// #[bitwarden_error(full)] +/// #[derive(Debug, Error)] +/// #[error("Vault is locked")] +/// struct VaultLocked; +/// +/// #[derive(Debug, Serialize)] +/// struct ExternalError; +/// +/// #[bitwarden_error(full)] +/// #[derive(Debug, Error)] +/// enum MyError { +/// #[error(transparent)] +/// VaultLocked(#[from] VaultLocked), +/// #[error("External error")] +/// ExternalError(ExternalError), +/// } +/// ``` +/// +/// will use tsify_next::Tsify to generate roughly the following TypeScript definition: +/// +/// ```typescript +/// export type CryptoError = +/// | { MissingFieldError: MissingFieldError } +/// | { VaultLocked: VaultLocked }; +/// +/// export interface VaultLocked { } +/// ``` +/// +/// All the general interopability rules apply such as external types needing to be defined as custom types. #[proc_macro_attribute] pub fn bitwarden_error( args: proc_macro::TokenStream, From 7f9c7dce98439c6c247040334ddc156c92c531ab Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Mon, 18 Nov 2024 09:56:44 +0100 Subject: [PATCH 51/53] feat: add tsify as dev dependency to macro lib --- Cargo.lock | 16 +--------------- crates/bitwarden-error-macro/Cargo.toml | 1 + crates/bitwarden-error/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a218683d..30fed94f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -439,6 +439,7 @@ dependencies = [ "serde", "syn 2.0.87", "thiserror", + "tsify-next", ] [[package]] @@ -1523,19 +1524,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" -[[package]] -name = "gloo-utils" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" -dependencies = [ - "js-sys", - "serde", - "serde_json", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "goblin" version = "0.8.2" @@ -3610,10 +3598,8 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f4a645dca4ee0800f5ab60ce166deba2db6a0315de795a2691e138a3d55d756" dependencies = [ - "gloo-utils", "serde", "serde-wasm-bindgen", - "serde_json", "tsify-next-macros", "wasm-bindgen", ] diff --git a/crates/bitwarden-error-macro/Cargo.toml b/crates/bitwarden-error-macro/Cargo.toml index f4746f0c..3731ae10 100644 --- a/crates/bitwarden-error-macro/Cargo.toml +++ b/crates/bitwarden-error-macro/Cargo.toml @@ -28,3 +28,4 @@ proc-macro = true bitwarden-error.workspace = true serde.workspace = true thiserror.workspace = true +tsify-next.workspace = true diff --git a/crates/bitwarden-error/Cargo.toml b/crates/bitwarden-error/Cargo.toml index 0f013def..17fd9fdd 100644 --- a/crates/bitwarden-error/Cargo.toml +++ b/crates/bitwarden-error/Cargo.toml @@ -23,5 +23,5 @@ workspace = true js-sys = "0.3.72" serde.workspace = true trybuild = "1.0.101" -tsify-next = "0.5.4" +tsify-next.workspace = true wasm-bindgen-test = "0.3.45" From 2db85bf0a3160e62cbd5a07bdabbabe3c53f5477 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Mon, 18 Nov 2024 09:57:07 +0100 Subject: [PATCH 52/53] fix: lint --- crates/bitwarden-error-macro/src/lib.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/bitwarden-error-macro/src/lib.rs b/crates/bitwarden-error-macro/src/lib.rs index 1532c4a1..b2992b1a 100644 --- a/crates/bitwarden-error-macro/src/lib.rs +++ b/crates/bitwarden-error-macro/src/lib.rs @@ -16,8 +16,9 @@ mod full; /// /// ## Export as /// -/// `export_as`: The name of the exported TypeScript type. If not provided, the name of the Rust type is used. -/// Note: This attribute is only available when using the `basic` and `flat` error types. +/// `export_as`: The name of the exported TypeScript type. If not provided, the name of the Rust +/// type is used. Note: This attribute is only available when using the `basic` and `flat` error +/// types. /// /// # Examples /// @@ -100,7 +101,8 @@ mod full; /// export interface VaultLocked { } /// ``` /// -/// All the general interopability rules apply such as external types needing to be defined as custom types. +/// All the general interopability rules apply such as external types needing to be defined as +/// custom types. #[proc_macro_attribute] pub fn bitwarden_error( args: proc_macro::TokenStream, From 6347baeae78a96c7d3ff964a09f6fa985591c957 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Mon, 18 Nov 2024 10:09:55 +0100 Subject: [PATCH 53/53] fix: doctest --- Cargo.lock | 2 ++ crates/bitwarden-error-macro/Cargo.toml | 4 +++- crates/bitwarden-error-macro/src/lib.rs | 8 ++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 30fed94f..39ecb28b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -434,12 +434,14 @@ version = "1.0.0" dependencies = [ "bitwarden-error", "darling", + "js-sys", "proc-macro2", "quote", "serde", "syn 2.0.87", "thiserror", "tsify-next", + "wasm-bindgen", ] [[package]] diff --git a/crates/bitwarden-error-macro/Cargo.toml b/crates/bitwarden-error-macro/Cargo.toml index 3731ae10..2cdf79a5 100644 --- a/crates/bitwarden-error-macro/Cargo.toml +++ b/crates/bitwarden-error-macro/Cargo.toml @@ -10,7 +10,7 @@ license-file.workspace = true keywords.workspace = true [features] -wasm = [] +wasm = ["bitwarden-error/wasm"] [dependencies] darling = "0.20.10" @@ -29,3 +29,5 @@ bitwarden-error.workspace = true serde.workspace = true thiserror.workspace = true tsify-next.workspace = true +js-sys.workspace = true +wasm-bindgen.workspace = true diff --git a/crates/bitwarden-error-macro/src/lib.rs b/crates/bitwarden-error-macro/src/lib.rs index b2992b1a..01f8b887 100644 --- a/crates/bitwarden-error-macro/src/lib.rs +++ b/crates/bitwarden-error-macro/src/lib.rs @@ -27,10 +27,14 @@ mod full; /// /// ```rust /// use bitwarden_error::prelude::*; +/// use thiserror::Error; /// +/// #[derive(Debug, Error)] /// #[bitwarden_error(basic)] /// enum MyError { +/// #[error("Not found")] /// NotFound, +/// #[error("Permission denied")] /// PermissionDenied, /// } /// ``` @@ -49,10 +53,14 @@ mod full; /// /// ```rust /// use bitwarden_error::prelude::*; +/// use thiserror::Error; /// +/// #[derive(Debug, Error)] /// #[bitwarden_error(basic)] /// enum MyError { +/// #[error("Not found")] /// NotFound, +/// #[error("Permission denied")] /// PermissionDenied, /// } /// ```