Skip to content

Commit

Permalink
Fix parsing empty oneofs
Browse files Browse the repository at this point in the history
  • Loading branch information
tinrab committed Dec 15, 2023
1 parent dd58282 commit f0bb454
Show file tree
Hide file tree
Showing 14 changed files with 172 additions and 65 deletions.
12 changes: 6 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bomboni"
version = "0.1.28"
version = "0.1.30"
authors = ["Tin Rabzelj <tin@flinect.com>"]
description = "Utility Library for Rust"
repository = "https://github.com/tinrab/bomboni"
Expand Down Expand Up @@ -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 }
2 changes: 1 addition & 1 deletion bomboni_common/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bomboni_common"
version = "0.1.28"
version = "0.1.30"
authors = ["Tin Rabzelj <tin@flinect.com>"]
description = "Common things for Bomboni library."
repository = "https://github.com/tinrab/bomboni"
Expand Down
2 changes: 1 addition & 1 deletion bomboni_prost/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bomboni_prost"
version = "0.1.28"
version = "0.1.30"
authors = ["Tin Rabzelj <tin@flinect.com>"]
description = "Utilities for working with prost. Part of Bomboni library."
repository = "https://github.com/tinrab/bomboni"
Expand Down
4 changes: 2 additions & 2 deletions bomboni_proto/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bomboni_proto"
version = "0.1.28"
version = "0.1.30"
authors = ["Tin Rabzelj <tin@flinect.com>"]
description = "Utilities for working with Protobuf/gRPC. Part of Bomboni library."
repository = "https://github.com/tinrab/bomboni"
Expand Down Expand Up @@ -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"
8 changes: 4 additions & 4 deletions bomboni_request/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bomboni_request"
version = "0.1.28"
version = "0.1.30"
authors = ["Tin Rabzelj <tin@flinect.com>"]
description = "Utilities for working with API requests. Part of Bomboni library."
repository = "https://github.com/tinrab/bomboni"
Expand All @@ -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"] }
Expand All @@ -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 }
67 changes: 67 additions & 0 deletions bomboni_request/src/parse/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
2 changes: 1 addition & 1 deletion bomboni_request_derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bomboni_request_derive"
version = "0.1.28"
version = "0.1.30"
authors = ["Tin Rabzelj <tin@flinect.com>"]
description = "Provides derive implementations for Bomboni library."
repository = "https://github.com/tinrab/bomboni"
Expand Down
7 changes: 2 additions & 5 deletions bomboni_request_derive/src/parse/message/mod.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down
5 changes: 2 additions & 3 deletions bomboni_request_derive/src/parse/message/write.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down
3 changes: 3 additions & 0 deletions bomboni_request_derive/src/parse/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
9 changes: 3 additions & 6 deletions bomboni_request_derive/src/parse/oneof/mod.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
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;

pub fn expand(options: &ParseOptions, variants: &[ParseVariant]) -> syn::Result<TokenStream> {
let mut result = parse::expand(options, variants)?;
if options.write {
result.extend(write::expand(options, variants)?);
result.extend(write::expand(options, variants));
}
Ok(result)
}
72 changes: 49 additions & 23 deletions bomboni_request_derive/src/parse/oneof/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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! {
Expand Down Expand Up @@ -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)?;
Expand Down Expand Up @@ -119,6 +144,19 @@ fn expand_tagged_union(
}

fn expand_parse_variant(variant: &ParseVariant) -> syn::Result<TokenStream> {
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,
Expand All @@ -135,24 +173,12 @@ fn expand_parse_variant(variant: &ParseVariant) -> syn::Result<TokenStream> {
..
} = 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() {
Expand Down
38 changes: 28 additions & 10 deletions bomboni_request_derive/src/parse/oneof/write.rs
Original file line number Diff line number Diff line change
@@ -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<TokenStream> {
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 {
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
Loading

0 comments on commit f0bb454

Please sign in to comment.