Skip to content

Commit

Permalink
Request serde into
Browse files Browse the repository at this point in the history
  • Loading branch information
tinrab committed Jan 18, 2024
1 parent cf742f8 commit 3e7d14d
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 17 deletions.
4 changes: 3 additions & 1 deletion bomboni_request/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use serde::{Deserialize, Serialize};
use thiserror::Error;

use crate::query::error::QueryError;
Expand Down Expand Up @@ -51,7 +52,8 @@ pub enum PathErrorStep {
Key(String),
}

#[derive(Error, Debug, Clone, PartialEq, Eq)]
#[derive(Error, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "kind")]
pub enum CommonError {
#[error("requested entity was not found")]
ResourceNotFound,
Expand Down
22 changes: 22 additions & 0 deletions bomboni_request/src/parse/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ mod tests {
Int32Value, Int64Value, StringValue, Timestamp, UInt32Value,
};
use bomboni_request_derive::{impl_parse_into_map, parse_resource_name, Parse};
use serde::{Deserialize, Serialize};

use super::*;

Expand Down Expand Up @@ -1924,4 +1925,25 @@ mod tests {
}
);
}

#[test]
fn serde_as() {
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
struct Item {
value: i32,
}

#[derive(Debug, Clone, PartialEq, Parse)]
#[parse(source = Item, write, serde_as)]
struct ParsedItem {
value: i32,
}

let js = serde_json::to_string_pretty(&Item { value: 42 }).unwrap();
assert_eq!(
js,
serde_json::to_string_pretty(&ParsedItem { value: 42 }).unwrap(),
);
assert_eq!(serde_json::from_str::<ParsedItem>(&js).unwrap().value, 42);
}
}
3 changes: 3 additions & 0 deletions bomboni_request_derive/src/parse/message/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ pub fn expand(options: &ParseOptions, fields: &[ParseField]) -> syn::Result<Toke

Ok(if options.search_query.is_some() {
quote! {
#[automatically_derived]
impl #ident #type_generics #where_clause {
#[allow(clippy::ignored_unit_patterns)]
pub fn parse_search_query<P: PageTokenBuilder #query_token_type >(
Expand All @@ -145,6 +146,7 @@ pub fn expand(options: &ParseOptions, fields: &[ParseField]) -> syn::Result<Toke
}
} else if options.list_query.is_some() {
quote! {
#[automatically_derived]
impl #ident #type_generics #where_clause {
#[allow(clippy::ignored_unit_patterns)]
pub fn parse_list_query<P: PageTokenBuilder #query_token_type >(
Expand All @@ -157,6 +159,7 @@ pub fn expand(options: &ParseOptions, fields: &[ParseField]) -> syn::Result<Toke
}
} else {
quote! {
#[automatically_derived]
impl #impl_generics RequestParse<#source> for #ident #type_generics #where_clause {
#[allow(clippy::ignored_unit_patterns)]
fn parse(source: #source) -> RequestResult<Self> {
Expand Down
1 change: 1 addition & 0 deletions bomboni_request_derive/src/parse/message/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ pub fn expand(options: &ParseOptions, fields: &[ParseField]) -> TokenStream {
let (impl_generics, type_generics, where_clause) = options.generics.split_for_impl();

quote! {
#[automatically_derived]
impl #impl_generics From<#ident #type_generics> for #source #where_clause {
#[allow(clippy::needless_update)]
fn from(value: #ident #type_generics) -> Self {
Expand Down
25 changes: 21 additions & 4 deletions bomboni_request_derive/src/parse/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ mod message;
mod oneof;
pub mod parse_into_map;
pub mod parse_resource_name;
mod serde;

#[derive(Debug, FromDeriveInput)]
#[darling(attributes(parse))]
Expand All @@ -22,6 +23,18 @@ pub struct ParseOptions {
/// Set to true to implement `From` trait for converting parsed type back into source proto type.
#[darling(default)]
pub write: bool,
/// Implement `serde::Serialize` from source type.
#[darling(default)]
pub serialize_as: bool,
/// Implement `serde::Deserialize` from source type.
#[darling(default)]
pub deserialize_as: bool,
/// Implement `serde::Serialize` and `serde::Deserialize` from source type.
#[darling(default)]
pub serde_as: bool,
/// Custom serde crate.
#[darling(default)]
pub serde_crate: Option<Path>,
/// Used to create tagged unions.
#[darling(default)]
pub tagged_union: Option<ParseTaggedUnion>,
Expand Down Expand Up @@ -208,10 +221,14 @@ pub fn expand(input: DeriveInput) -> syn::Result<TokenStream> {
}
};

match &options.data {
ast::Data::Struct(fields) => message::expand(&options, &fields.fields),
ast::Data::Enum(variants) => oneof::expand(&options, variants),
}
let mut result = match &options.data {
ast::Data::Struct(fields) => message::expand(&options, &fields.fields)?,
ast::Data::Enum(variants) => oneof::expand(&options, variants)?,
};

result.extend(serde::expand(&options)?);

Ok(result)
}

pub fn parse_default_expr(meta: &Meta) -> darling::Result<Expr> {
Expand Down
73 changes: 73 additions & 0 deletions bomboni_request_derive/src/parse/serde.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
use crate::parse::ParseOptions;
use proc_macro2::TokenStream;
use quote::quote;

pub fn expand(options: &ParseOptions) -> syn::Result<TokenStream> {
if !options.serde_as && !options.serialize_as && !options.deserialize_as {
return Ok(quote!());
}

let mut result = if let Some(path) = options.serde_crate.as_ref() {
quote! {
use #path as _serde;
}
} else {
quote! {
#[allow(unused_extern_crates, clippy::useless_attribute)]
extern crate serde as _serde;
}
};

if options.serde_as || options.serialize_as {
if !options.write {
return Err(syn::Error::new_spanned(
&options.ident,
"Cannot use `serde_as` or `serialize_as` without `write`",
));
}

let source = &options.source;
let ident = &options.ident;
let (impl_generics, type_generics, where_clause) = options.generics.split_for_impl();

result.extend(quote! {
#[automatically_derived]
impl #impl_generics _serde::Serialize for #ident #type_generics #where_clause {
fn serialize<__S>(&self, serializer: __S) -> _serde::__private::Result<__S::Ok, __S::Error>
where
__S: _serde::Serializer,
{
#source::serialize(&self.clone().into(), serializer)
}
}
});
}

if options.serde_as || options.deserialize_as {
let source = &options.source;
let ident = &options.ident;
let (impl_generics, type_generics, where_clause) = options.generics.split_for_impl();

result.extend(quote! {
#[automatically_derived]
impl<'de> #impl_generics _serde::Deserialize<'de> for #ident #type_generics #where_clause {
fn deserialize<__D>(deserializer: __D) -> _serde::__private::Result<Self, __D::Error>
where
__D: _serde::Deserializer<'de>,
{
#source::deserialize(deserializer)?
.parse_into()
.map_err(_serde::de::Error::custom)
}
}
});
}

Ok(quote! {
#[doc(hidden)]
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
const _ : () = {
#result
};
})
}
11 changes: 0 additions & 11 deletions bomboni_wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ pub mod utility;
pub trait Wasm {
type JsType: JsCast;

// const DECL: &'static str;

fn to_js(&self) -> Result<Self::JsType, serde_wasm_bindgen::Error>
where
Self: serde::Serialize,
Expand Down Expand Up @@ -90,13 +88,6 @@ mod tests {
value: i32,
}

// #[derive(Serialize, Deserialize, Wasm)]
// #[repr(i32)]
// pub enum CStyle {
// A = 1,
// B = 2,
// }

assert_eq!(
ExternalTag::DECL,
"export type ExternalTag = {\n String: string;\n Number?: null;\n} | {\n Number: number;\n String?: null;\n};"
Expand All @@ -110,7 +101,5 @@ mod tests {
InternalTag::DECL,
"export type InternalTag = {\n kind: \"String\";\n value: string;\n} | ({\n kind: \"Item\";\n} & InternalItem);"
);

// println!("{}", serde_json::to_string_pretty(&CStyle::A).unwrap());
}
}
2 changes: 1 addition & 1 deletion bomboni_wasm_derive/src/wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ fn derive_proxy(proxy: &ProxyWasm, options: &WasmOptions) -> TokenStream {
#[inline]
fn from(value: #ident #type_generics) -> Self {
let proxy: #proxy_ident = #proxy_into(value);
proxy.to_js().unwrap().into()
proxy.to_js().unwrap_throw().into()
}
}
});
Expand Down

0 comments on commit 3e7d14d

Please sign in to comment.