diff --git a/Cargo.toml b/Cargo.toml index 9cf4a03..c859ae0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bomboni" -version = "0.1.28" +version = "0.1.30" authors = ["Tin Rabzelj "] description = "Utility Library for Rust" repository = "https://github.com/tinrab/bomboni" @@ -38,9 +38,9 @@ tokio = ["bomboni_common/tokio"] tonic = ["bomboni_proto/tonic", "bomboni_request/tonic"] [dependencies] -bomboni_common = { path = "bomboni_common", version = "0.1.28" } +bomboni_common = { path = "bomboni_common", version = "0.1.30" } -bomboni_prost = { path = "bomboni_prost", version = "0.1.28", optional = true } -bomboni_proto = { path = "bomboni_proto", version = "0.1.28", optional = true } -bomboni_request = { path = "bomboni_request", version = "0.1.28", optional = true } -bomboni_template = { path = "bomboni_template", version = "0.1.28", optional = true } +bomboni_prost = { path = "bomboni_prost", version = "0.1.30", optional = true } +bomboni_proto = { path = "bomboni_proto", version = "0.1.30", optional = true } +bomboni_request = { path = "bomboni_request", version = "0.1.30", optional = true } +bomboni_template = { path = "bomboni_template", version = "0.1.30", optional = true } diff --git a/bomboni_common/Cargo.toml b/bomboni_common/Cargo.toml index 754b2ad..514622f 100644 --- a/bomboni_common/Cargo.toml +++ b/bomboni_common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bomboni_common" -version = "0.1.28" +version = "0.1.30" authors = ["Tin Rabzelj "] description = "Common things for Bomboni library." repository = "https://github.com/tinrab/bomboni" diff --git a/bomboni_prost/Cargo.toml b/bomboni_prost/Cargo.toml index 0f43a0b..9421819 100644 --- a/bomboni_prost/Cargo.toml +++ b/bomboni_prost/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bomboni_prost" -version = "0.1.28" +version = "0.1.30" authors = ["Tin Rabzelj "] description = "Utilities for working with prost. Part of Bomboni library." repository = "https://github.com/tinrab/bomboni" diff --git a/bomboni_proto/Cargo.toml b/bomboni_proto/Cargo.toml index 1eb6fe8..8e3d9ce 100644 --- a/bomboni_proto/Cargo.toml +++ b/bomboni_proto/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bomboni_proto" -version = "0.1.28" +version = "0.1.30" authors = ["Tin Rabzelj "] description = "Utilities for working with Protobuf/gRPC. Part of Bomboni library." repository = "https://github.com/tinrab/bomboni" @@ -36,5 +36,5 @@ serde_json = { version = "1.0.108", optional = true } serde_json = "1.0.108" [build-dependencies] -bomboni_prost = { path = "../bomboni_prost", version = "0.1.28" } +bomboni_prost = { path = "../bomboni_prost", version = "0.1.30" } prost-build = "0.12.3" diff --git a/bomboni_request/Cargo.toml b/bomboni_request/Cargo.toml index fcba98d..323400f 100644 --- a/bomboni_request/Cargo.toml +++ b/bomboni_request/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bomboni_request" -version = "0.1.28" +version = "0.1.30" authors = ["Tin Rabzelj "] description = "Utilities for working with API requests. Part of Bomboni library." repository = "https://github.com/tinrab/bomboni" @@ -19,8 +19,8 @@ testing = [] tonic = ["bomboni_proto/tonic", "dep:tonic"] [dependencies] -bomboni_common = { path = "../bomboni_common", version = "0.1.28" } -bomboni_proto = { path = "../bomboni_proto", version = "0.1.28" } +bomboni_common = { path = "../bomboni_common", version = "0.1.30" } +bomboni_proto = { path = "../bomboni_proto", version = "0.1.30" } thiserror = "1.0.50" itertools = "0.12.0" time = { version = "0.3.30", features = ["formatting", "parsing"] } @@ -35,4 +35,4 @@ rand = "0.8.5" regex = "1.10.2" tonic = { version = "0.10.2", optional = true } -bomboni_request_derive = { path = "../bomboni_request_derive", version = "0.1.28", optional = true } +bomboni_request_derive = { path = "../bomboni_request_derive", version = "0.1.30", optional = true } diff --git a/bomboni_request/src/parse/mod.rs b/bomboni_request/src/parse/mod.rs index 12b7137..3421703 100644 --- a/bomboni_request/src/parse/mod.rs +++ b/bomboni_request/src/parse/mod.rs @@ -1042,4 +1042,71 @@ mod tests { } ); } + + #[test] + fn parse_oneof() { + #[derive(Debug, Clone, PartialEq)] + enum Item { + String(String), + Data(Data), + Null(()), + Empty, + Dropped(i32), + } + + impl Item { + pub fn get_variant_name(&self) -> &'static str { + match self { + Self::String(_) => "string", + Self::Data(_) => "data", + Self::Null(_) => "null", + Self::Empty => "empty", + Self::Dropped(_) => "dropped", + } + } + } + + #[derive(Debug, Clone, Default, PartialEq)] + struct Data { + value: i32, + } + + #[derive(Debug, Clone, PartialEq, Parse)] + #[parse(source= Item, write)] + enum ParsedItem { + String(String), + Data(ParsedData), + Null(()), + #[parse(source_empty)] + Empty, + Dropped, + } + + #[derive(Debug, Clone, Default, PartialEq, Parse)] + #[parse(source= Data, write)] + struct ParsedData { + value: i32, + } + + assert_eq!( + ParsedItem::parse(Item::String("abc".into())).unwrap(), + ParsedItem::String("abc".into()) + ); + assert_eq!( + Item::from(ParsedItem::String("abc".into())), + Item::String("abc".into()) + ); + + assert_eq!( + ParsedItem::parse(Item::Data(Data { value: 42 })).unwrap(), + ParsedItem::Data(ParsedData { value: 42 }) + ); + assert_eq!( + Item::from(ParsedItem::Data(ParsedData { value: 42 })), + Item::Data(Data { value: 42 }) + ); + + assert_eq!(ParsedItem::parse(Item::Empty).unwrap(), ParsedItem::Empty); + assert_eq!(Item::from(ParsedItem::Empty), Item::Empty); + } } diff --git a/bomboni_request_derive/Cargo.toml b/bomboni_request_derive/Cargo.toml index 4eabd4b..0429f6b 100644 --- a/bomboni_request_derive/Cargo.toml +++ b/bomboni_request_derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bomboni_request_derive" -version = "0.1.28" +version = "0.1.30" authors = ["Tin Rabzelj "] description = "Provides derive implementations for Bomboni library." repository = "https://github.com/tinrab/bomboni" diff --git a/bomboni_request_derive/src/parse/message/mod.rs b/bomboni_request_derive/src/parse/message/mod.rs index 892f7b6..43486ae 100644 --- a/bomboni_request_derive/src/parse/message/mod.rs +++ b/bomboni_request_derive/src/parse/message/mod.rs @@ -1,9 +1,6 @@ -use darling::FromMeta; -use proc_macro2::{Ident, Literal, TokenStream}; -use quote::{quote, ToTokens}; +use proc_macro2::TokenStream; -use crate::parse::{ParseOptions, ParseTaggedUnion, ParseVariant}; -use crate::utility::{get_proto_type_info, ProtoTypeInfo}; +use crate::parse::ParseOptions; use super::ParseField; diff --git a/bomboni_request_derive/src/parse/message/write.rs b/bomboni_request_derive/src/parse/message/write.rs index 155abd5..45abd56 100644 --- a/bomboni_request_derive/src/parse/message/write.rs +++ b/bomboni_request_derive/src/parse/message/write.rs @@ -1,9 +1,8 @@ use darling::FromMeta; -use itertools::Itertools; -use proc_macro2::{Ident, Literal, TokenStream}; +use proc_macro2::{Ident, TokenStream}; use quote::{quote, ToTokens}; -use crate::parse::{DeriveOptions, ParseField, ParseOptions}; +use crate::parse::{ParseField, ParseOptions}; use crate::utility::{get_proto_type_info, ProtoTypeInfo}; pub fn expand(options: &ParseOptions, fields: &[ParseField]) -> TokenStream { diff --git a/bomboni_request_derive/src/parse/mod.rs b/bomboni_request_derive/src/parse/mod.rs index 4c1c177..abe4088 100644 --- a/bomboni_request_derive/src/parse/mod.rs +++ b/bomboni_request_derive/src/parse/mod.rs @@ -99,6 +99,9 @@ pub struct ParseVariant { /// True if the source variant should be dereferenced from a `Box` type. #[darling(default)] pub source_box: bool, + /// True if the source is an empty unit variant. + #[darling(default)] + pub source_empty: bool, /// Parses enum value from `i32`. #[darling(default)] pub enumeration: bool, diff --git a/bomboni_request_derive/src/parse/oneof/mod.rs b/bomboni_request_derive/src/parse/oneof/mod.rs index 6793093..ad17fa3 100644 --- a/bomboni_request_derive/src/parse/oneof/mod.rs +++ b/bomboni_request_derive/src/parse/oneof/mod.rs @@ -1,9 +1,6 @@ -use darling::FromMeta; -use proc_macro2::{Ident, Literal, TokenStream}; -use quote::{quote, ToTokens}; +use proc_macro2::TokenStream; -use crate::parse::{ParseOptions, ParseTaggedUnion, ParseVariant}; -use crate::utility::{get_proto_type_info, ProtoTypeInfo}; +use crate::parse::{ParseOptions, ParseVariant}; mod parse; mod write; @@ -11,7 +8,7 @@ mod write; pub fn expand(options: &ParseOptions, variants: &[ParseVariant]) -> syn::Result { let mut result = parse::expand(options, variants)?; if options.write { - result.extend(write::expand(options, variants)?); + result.extend(write::expand(options, variants)); } Ok(result) } diff --git a/bomboni_request_derive/src/parse/oneof/parse.rs b/bomboni_request_derive/src/parse/oneof/parse.rs index cb144dc..89ec726 100644 --- a/bomboni_request_derive/src/parse/oneof/parse.rs +++ b/bomboni_request_derive/src/parse/oneof/parse.rs @@ -30,14 +30,30 @@ fn expand_parse(options: &ParseOptions, variants: &[ParseVariant]) -> syn::Resul }; let target_variant_ident = &variant.ident; - let parse_variant = expand_parse_variant(variant)?; - parse_variants.extend(quote! { - #source::#source_variant_ident(source) => { - #ident::#target_variant_ident({ - #parse_variant - }) - } - }); + if variant.fields.is_empty() { + parse_variants.extend(if variant.source_empty { + quote! { + #source::#source_variant_ident => { + #ident::#target_variant_ident + } + } + } else { + quote! { + #source::#source_variant_ident(_) => { + #ident::#target_variant_ident + } + } + }); + } else { + let parse_variant = expand_parse_variant(variant)?; + parse_variants.extend(quote! { + #source::#source_variant_ident(source) => { + #ident::#target_variant_ident({ + #parse_variant + }) + } + }); + } } Ok(quote! { @@ -82,9 +98,18 @@ fn expand_tagged_union( let target_variant_ident = &variant.ident; if variant.fields.is_empty() { - // Handle unit variants - parse_variants.extend(quote! { - #oneof_ident::#source_variant_ident(_) => #ident::#target_variant_ident, + parse_variants.extend(if variant.source_empty { + quote! { + #oneof_ident::#source_variant_ident => { + #ident::#target_variant_ident + } + } + } else { + quote! { + #oneof_ident::#source_variant_ident(_) => { + #ident::#target_variant_ident + } + } }); } else { let parse_variant = expand_parse_variant(variant)?; @@ -119,6 +144,19 @@ fn expand_tagged_union( } fn expand_parse_variant(variant: &ParseVariant) -> syn::Result { + if (variant.with.is_some() || variant.parse_with.is_some()) && variant.regex.is_some() { + return Err(syn::Error::new_spanned( + &variant.ident, + "some of these options cannot be used alongside `with`", + )); + } + if variant.with.is_some() && (variant.parse_with.is_some() || variant.write_with.is_some()) { + return Err(syn::Error::new_spanned( + &variant.ident, + "custom `parse_with` and `write_with` functions cannot be used alongside `with`", + )); + } + if variant.fields.len() != 1 { return Err(syn::Error::new_spanned( &variant.ident, @@ -135,24 +173,12 @@ fn expand_parse_variant(variant: &ParseVariant) -> syn::Result { .. } = get_proto_type_info(variant_type); - if (variant.with.is_some() || variant.parse_with.is_some()) && variant.regex.is_some() { - return Err(syn::Error::new_spanned( - &variant.ident, - "some of these options cannot be used alongside `with`", - )); - } if variant.regex.is_some() && !is_string { return Err(syn::Error::new_spanned( &variant.ident, "regex can only be used with string variants", )); } - if variant.with.is_some() && (variant.parse_with.is_some() || variant.write_with.is_some()) { - return Err(syn::Error::new_spanned( - &variant.ident, - "custom `parse_with` and `write_with` functions cannot be used alongside `with`", - )); - } let parse_source = if variant.with.is_some() || variant.parse_with.is_some() { let parse_with = if let Some(with) = variant.with.as_ref() { diff --git a/bomboni_request_derive/src/parse/oneof/write.rs b/bomboni_request_derive/src/parse/oneof/write.rs index 858f593..6221bb9 100644 --- a/bomboni_request_derive/src/parse/oneof/write.rs +++ b/bomboni_request_derive/src/parse/oneof/write.rs @@ -1,16 +1,16 @@ use darling::FromMeta; -use proc_macro2::{Ident, Literal, TokenStream}; +use proc_macro2::{Ident, TokenStream}; use quote::{quote, ToTokens}; use crate::parse::{ParseOptions, ParseTaggedUnion, ParseVariant}; use crate::utility::{get_proto_type_info, ProtoTypeInfo}; -pub fn expand(options: &ParseOptions, variants: &[ParseVariant]) -> syn::Result { - Ok(if let Some(tagged_union) = options.tagged_union.as_ref() { +pub fn expand(options: &ParseOptions, variants: &[ParseVariant]) -> TokenStream { + if let Some(tagged_union) = options.tagged_union.as_ref() { expand_write_tagged_union(options, variants, tagged_union) } else { expand_write(options, variants) - }) + } } fn expand_write(options: &ParseOptions, variants: &[ParseVariant]) -> TokenStream { @@ -32,9 +32,18 @@ fn expand_write(options: &ParseOptions, variants: &[ParseVariant]) -> TokenStrea let target_variant_ident = &variant.ident; if variant.fields.is_empty() { - // Handle unit variants - write_variants.extend(quote! { - #ident::#target_variant_ident => #source::#source_variant_ident(Default::default()), + write_variants.extend(if variant.source_empty { + quote! { + #ident::#target_variant_ident => { + #source::#source_variant_ident + } + } + } else { + quote! { + #ident::#target_variant_ident => { + #source::#source_variant_ident(Default::default()) + } + } }); } else { let write_variant = expand_write_variant(variant); @@ -84,9 +93,18 @@ fn expand_write_tagged_union( let target_variant_ident = &variant.ident; if variant.fields.is_empty() { - // Handle unit variants - write_variants.extend(quote! { - #ident::#target_variant_ident => #oneof_ident::#source_variant_ident(Default::default()), + write_variants.extend(if variant.source_empty { + quote! { + #ident::#target_variant_ident => { + #oneof_ident::#source_variant_ident + } + } + } else { + quote! { + #ident::#target_variant_ident => { + #oneof_ident::#source_variant_ident(Default::default()) + } + } }); } else { let write_variant = expand_write_variant(variant); diff --git a/bomboni_template/Cargo.toml b/bomboni_template/Cargo.toml index 1693ea1..bb57530 100644 --- a/bomboni_template/Cargo.toml +++ b/bomboni_template/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bomboni_template" -version = "0.1.28" +version = "0.1.30" authors = ["Tin Rabzelj "] description = "Utilities for working Handlebars templates. Part of Bomboni library." repository = "https://github.com/tinrab/bomboni" @@ -17,8 +17,8 @@ path = "src/lib.rs" testing = [] [dependencies] -bomboni_common = { path = "../bomboni_common", version = "0.1.28" } -bomboni_proto = { version = "0.1.28", path = "../bomboni_proto", features = [ +bomboni_common = { path = "../bomboni_common", version = "0.1.30" } +bomboni_proto = { version = "0.1.30", path = "../bomboni_proto", features = [ "json", ] } thiserror = "1.0.50"