Skip to content

Commit

Permalink
Add keep
Browse files Browse the repository at this point in the history
  • Loading branch information
tinrab committed Dec 17, 2023
1 parent 29f7871 commit 2c8120b
Show file tree
Hide file tree
Showing 13 changed files with 188 additions and 25 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.31"
version = "0.1.34"
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.31" }
bomboni_common = { path = "bomboni_common", version = "0.1.34" }

bomboni_prost = { path = "bomboni_prost", version = "0.1.31", optional = true }
bomboni_proto = { path = "bomboni_proto", version = "0.1.31", optional = true }
bomboni_request = { path = "bomboni_request", version = "0.1.31", optional = true }
bomboni_template = { path = "bomboni_template", version = "0.1.31", optional = true }
bomboni_prost = { path = "bomboni_prost", version = "0.1.34", optional = true }
bomboni_proto = { path = "bomboni_proto", version = "0.1.34", optional = true }
bomboni_request = { path = "bomboni_request", version = "0.1.34", optional = true }
bomboni_template = { path = "bomboni_template", version = "0.1.34", 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.31"
version = "0.1.34"
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.31"
version = "0.1.34"
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.31"
version = "0.1.34"
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.31" }
bomboni_prost = { path = "../bomboni_prost", version = "0.1.34" }
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.31"
version = "0.1.34"
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.31" }
bomboni_proto = { path = "../bomboni_proto", version = "0.1.31" }
bomboni_common = { path = "../bomboni_common", version = "0.1.34" }
bomboni_proto = { path = "../bomboni_proto", version = "0.1.34" }
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.31", optional = true }
bomboni_request_derive = { path = "../bomboni_request_derive", version = "0.1.34", optional = true }
72 changes: 72 additions & 0 deletions bomboni_request/src/parse/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,11 @@ mod tests {
nested: Option<NestedItem>,
optional_nested: Option<NestedItem>,
default_nested: Option<NestedItem>,
default_default_nested: Option<NestedItem>,
custom_parse: String,
enum_value: i32,
oneof: Option<OneofKind>,
kept_nested: Option<NestedItem>,
}

impl Default for Item {
Expand All @@ -100,9 +102,11 @@ mod tests {
nested: Some(NestedItem {}),
optional_nested: Some(NestedItem {}),
default_nested: Some(NestedItem {}),
default_default_nested: Some(NestedItem {}),
custom_parse: "42".into(),
enum_value: 1,
oneof: Some(OneofKind::String("abc".into())),
kept_nested: Some(NestedItem {}),
}
}
}
Expand All @@ -124,12 +128,16 @@ mod tests {
optional_nested: Option<ParsedNestedItem>,
#[parse(default = NestedItem::default())]
default_nested: ParsedNestedItem,
#[parse(default)]
default_default_nested: ParsedNestedItem,
#[parse(with = custom_parse)]
custom_parse: u64,
#[parse(enumeration)]
enum_value: Enum,
#[parse(oneof)]
oneof: ParsedOneofKind,
#[parse(keep)]
kept_nested: Option<NestedItem>,
#[parse(skip)]
extra: i32,
}
Expand Down Expand Up @@ -292,9 +300,11 @@ mod tests {
nested: Some(NestedItem {}),
optional_nested: Some(NestedItem {}),
default_nested: None,
default_default_nested: None,
custom_parse: "42".into(),
enum_value: 1,
oneof: Some(OneofKind::String("abc".into())),
kept_nested: Some(NestedItem {}),
})
.unwrap(),
ParsedItem {
Expand All @@ -305,9 +315,11 @@ mod tests {
nested: ParsedNestedItem {},
optional_nested: Some(ParsedNestedItem {}),
default_nested: ParsedNestedItem {},
default_default_nested: ParsedNestedItem {},
custom_parse: 42,
enum_value: Enum::A,
oneof: ParsedOneofKind::String("abc".into()),
kept_nested: Some(NestedItem {}),
extra: 0,
}
);
Expand All @@ -321,9 +333,11 @@ mod tests {
nested: ParsedNestedItem {},
optional_nested: Some(ParsedNestedItem {}),
default_nested: ParsedNestedItem {},
default_default_nested: ParsedNestedItem {},
custom_parse: 1337,
enum_value: Enum::B,
oneof: ParsedOneofKind::String("abc".into()),
kept_nested: Some(NestedItem {}),
extra: 0,
}),
Item {
Expand All @@ -334,9 +348,11 @@ mod tests {
nested: Some(NestedItem {}),
optional_nested: Some(NestedItem {}),
default_nested: Some(NestedItem {}),
default_default_nested: Some(NestedItem {}),
custom_parse: "1337".into(),
enum_value: 2,
oneof: Some(OneofKind::String("abc".into())),
kept_nested: Some(NestedItem {}),
}
);
}
Expand Down Expand Up @@ -1181,4 +1197,60 @@ mod tests {
assert_eq!(Union::from(ParsedUnion::B(Some(42))), Union::B(Some(42)));
assert_eq!(Union::from(ParsedUnion::B(None)), Union::B(None));
}

#[test]
fn parse_keep() {
#[derive(Debug, Clone, PartialEq, Default)]
struct Item {
item: Option<NestedItem>,
default_item: Option<NestedItem>,
default_item_custom: Option<NestedItem>,
}

#[derive(Debug, Clone, PartialEq, Default)]
struct NestedItem {
value: i32,
}

#[derive(Debug, Clone, PartialEq, Default, Parse)]
#[parse(source = Item, write)]
struct ParsedItem {
#[parse(keep)]
item: NestedItem,
#[parse(keep, default)]
default_item: NestedItem,
#[parse(keep, default = get_default_item())]
default_item_custom: NestedItem,
}

fn get_default_item() -> NestedItem {
NestedItem { value: 42 }
}

assert_eq!(
ParsedItem::parse(Item {
item: Some(NestedItem { value: 42 }),
default_item: None,
default_item_custom: None,
})
.unwrap(),
ParsedItem {
item: NestedItem { value: 42 },
default_item: NestedItem { value: 0 },
default_item_custom: NestedItem { value: 42 },
}
);
assert_eq!(
Item::from(ParsedItem {
item: NestedItem { value: 42 },
default_item: NestedItem { value: 0 },
default_item_custom: NestedItem { value: 42 },
}),
Item {
item: Some(NestedItem { value: 42 }),
default_item: Some(NestedItem { value: 0 }),
default_item_custom: Some(NestedItem { value: 42 }),
}
);
}
}
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.31"
version = "0.1.34"
authors = ["Tin Rabzelj <tin@flinect.com>"]
description = "Provides derive implementations for Bomboni library."
repository = "https://github.com/tinrab/bomboni"
Expand Down
29 changes: 27 additions & 2 deletions bomboni_request_derive/src/parse/message/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ fn expand_parse_field(field: &ParseField) -> syn::Result<TokenStream> {
if field.regex.is_some() && !is_string {
return Err(syn::Error::new_spanned(
&field.ident,
"regex can only be used with string fields",
"`regex` can only be used with string fields",
));
}
if field.with.is_some() && (field.parse_with.is_some() || field.write_with.is_some()) {
Expand All @@ -135,8 +135,33 @@ fn expand_parse_field(field: &ParseField) -> syn::Result<TokenStream> {
"wrapper fields cannot be casted",
));
}
if field.keep
&& (field.skip
|| field.wrapper
|| field.enumeration
|| field.oneof
|| field.regex.is_some()
|| field.source_try_from.is_some()
|| field.with.is_some()
|| field.parse_with.is_some()
|| field.write_with.is_some()
|| field.resource.is_some())
{
return Err(syn::Error::new_spanned(
&field.ident,
"some of these options cannot be used alongside `keep`",
));
}

let mut parse_source = if field.with.is_some() || field.parse_with.is_some() {
let mut parse_source = if field.keep {
if is_box || field.source_box {
quote! {
let target = *target;
}
} else {
quote!()
}
} else if field.with.is_some() || field.parse_with.is_some() {
let parse_with = if let Some(with) = field.with.as_ref() {
quote! {
#with::parse
Expand Down
10 changes: 9 additions & 1 deletion bomboni_request_derive/src/parse/message/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,15 @@ fn expand_write_field(field: &ParseField) -> TokenStream {
..
} = get_proto_type_info(field_type);

let mut write_target = if field.with.is_some() || field.write_with.is_some() {
let mut write_target = if field.keep {
if is_box {
quote! {
let source = *source;
}
} else {
quote!()
}
} else if field.with.is_some() || field.write_with.is_some() {
let write_with = if let Some(with) = field.with.as_ref() {
quote! {
#with::write
Expand Down
31 changes: 29 additions & 2 deletions bomboni_request_derive/src/parse/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use darling::util::parse_expr;
use darling::{ast, FromDeriveInput, FromField, FromMeta, FromVariant};
use proc_macro2::{Ident, TokenStream};
use quote::quote;
use syn::{self, DeriveInput, Expr, ExprArray, ExprPath, Meta, MetaNameValue, Path, Type};

mod message;
Expand Down Expand Up @@ -61,11 +62,14 @@ pub struct ParseField {
/// Parses Protobuf's well-known wrapper type.
#[darling(default)]
pub wrapper: bool,
/// True if the source and target types are the same.
#[darling(default)]
pub keep: bool,
/// Parse resource fields into this field.
#[darling(default)]
pub resource: Option<ResourceOptions>,
/// Custom expression that returns the default value.
#[darling(with = parse_expr::parse_str_literal, map = Some)]
#[darling(with = parse_default_expr, map = Some)]
pub default: Option<Expr>,
/// String value will be checked against this regex.
#[darling(with = parse_expr::preserve_str_literal, map = Some)]
Expand Down Expand Up @@ -114,8 +118,11 @@ pub struct ParseVariant {
/// Parses Protobuf's well-known wrapper type.
#[darling(default)]
pub wrapper: bool,
/// True if the source and target use the same nested type.
#[darling(default)]
pub keep: bool,
/// Custom expression that returns the default value.
#[darling(with = parse_expr::parse_str_literal, map = Some)]
#[darling(with = parse_default_expr, map = Some)]
pub default: Option<Expr>,
/// String value will be checked against this regex.
#[darling(with = parse_expr::preserve_str_literal, map = Some)]
Expand Down Expand Up @@ -172,6 +179,26 @@ pub fn expand(input: DeriveInput) -> syn::Result<TokenStream> {
}
}

pub fn parse_default_expr(meta: &Meta) -> darling::Result<Expr> {
match meta {
Meta::Path(path) => {
if matches!(path.get_ident(), Some(ident) if ident == "default") {
Ok(syn::parse2(quote! { Default::default() })?)
} else {
Err(darling::Error::unsupported_format("path").with_span(meta))
}
}
Meta::List(_) => Err(darling::Error::unsupported_format("list").with_span(meta)),
Meta::NameValue(nv) => {
if let Expr::Lit(expr_lit) = &nv.value {
Expr::from_value(&expr_lit.lit)
} else {
Ok(nv.value.clone())
}
}
}
}

impl FromMeta for DeriveOptions {
fn from_expr(expr: &Expr) -> darling::Result<Self> {
Ok(match expr {
Expand Down
Loading

0 comments on commit 2c8120b

Please sign in to comment.