Skip to content

Commit

Permalink
Improve doc & rename has_same_schema_as to has_same_type_as
Browse files Browse the repository at this point in the history
  • Loading branch information
Ten0 committed Mar 10, 2024
1 parent 290a5b0 commit e9fbe05
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 13 deletions.
2 changes: 1 addition & 1 deletion serde_avro_derive/tests/derive_schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ struct LogicalTypes<'a> {
uuid: &'a str,
#[avro_schema(logical_type = r#"decimal"#, scale = 1, precision = 4)]
decimal: f64,
#[avro_schema(logical_type = r#"custom-logical-type"#, has_same_schema_as = "String")]
#[avro_schema(logical_type = r#"custom-logical-type"#, has_same_type_as = "String")]
custom: MyCustomString,
}
struct MyCustomString(String);
Expand Down
24 changes: 12 additions & 12 deletions serde_avro_derive_macros/src/build_schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ pub(crate) struct SchemaDeriveField {
scale: Option<WithMetaPath<syn::LitInt>>,
precision: Option<WithMetaPath<syn::LitInt>>,

has_same_schema_as: Option<syn::Type>,
has_same_type_as: Option<syn::Type>,
}

pub(crate) fn schema_impl(input: SchemaDeriveInput) -> Result<TokenStream, Error> {
Expand All @@ -53,7 +53,7 @@ pub(crate) fn schema_impl(input: SchemaDeriveInput) -> Result<TokenStream, Error
.iter()
.map(|field| {
// Choose type
let mut ty = field.has_same_schema_as.as_ref().unwrap_or(&field.ty);
let mut ty = field.has_same_type_as.as_ref().unwrap_or(&field.ty);
while let syn::Type::Reference(reference) = ty {
// This allows not requiring the user to specify that T: 'a
// as an explicit where predicate, and simplifies the calls
Expand Down Expand Up @@ -114,6 +114,16 @@ pub(crate) fn schema_impl(input: SchemaDeriveInput) -> Result<TokenStream, Error
) }
};
if logical_type_str_pascal == "Decimal" {
if inferred_decimal_logical_type {
ty = Cow::Owned(
// "A `decimal` logical type annotates Avro `bytes` or `fixed`
// types". Because we need to choose an arbitrary one as we
// picked `decimal` because the type was named `Decimal`, we'll
// choose Bytes as we have no information to accurately decide
// the attributes we would give to a `Fixed`.
parse_quote_spanned!(logical_type_litstr.span() => Vec<u8>),
);
}
let zero = parse_quote!(0);
let mut error = |missing_field: &str| {
errors.extend(
Expand Down Expand Up @@ -141,16 +151,6 @@ pub(crate) fn schema_impl(input: SchemaDeriveInput) -> Result<TokenStream, Error
});
} else {
match logical_type_str_pascal.as_str() {
_ if inferred_decimal_logical_type => {
ty = Cow::Owned(
// "A `decimal` logical type annotates Avro `bytes` or `fixed`
// types". Because we need to choose an arbitrary one as we
// picked `decimal` because the type was named `Decimal`, we'll
// choose Bytes as we have no information to accurately decide
// the attributes we would give to a `Fixed`.
parse_quote_spanned!(logical_type_litstr.span() => Vec<u8>),
);
}
"TimestampMillis" | "TimestampMicros" | "TimeMicros" => {
if !matches!(&*ty, syn::Type::Path(p) if p.path.is_ident("i64")) {
ty = Cow::Owned(
Expand Down
73 changes: 73 additions & 0 deletions serde_avro_derive_macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,79 @@ use darling::FromDeriveInput;
/// # let actual_schema = serde_json::to_string_pretty(&Foo::schema_mut()).unwrap();
/// # assert_eq!(actual_schema, schema_str);
/// ```
///
/// # Customize field schema
///
/// Field attributes can be used to specify logical type or override the
/// schema that a given field will produce:
/// ```
/// use serde_avro_derive::BuildSchema;
///
/// #[derive(BuildSchema)]
/// #[allow(unused)]
/// struct LogicalTypes<'a> {
/// #[avro_schema(logical_type = "Uuid")]
/// uuid: &'a str,
/// #[avro_schema(logical_type = "decimal", scale = 1, precision = 4)]
/// decimal: f64,
/// #[avro_schema(scale = 1, precision = 4)]
/// implicit_decimal: Decimal, // logical type is inferred because of the name of the type
/// #[avro_schema(logical_type = "custom-logical-type", has_same_type_as = "String")]
/// custom: MyCustomString,
/// }
/// struct MyCustomString(String);
/// struct Decimal {
/// _repr: (),
/// }
///
/// let expected_schema = r#"{
/// "type": "record",
/// "name": "rust_out.LogicalTypes",
/// "fields": [
/// {
/// "name": "uuid",
/// "type": {
/// "logicalType": "uuid",
/// "type": "string"
/// }
/// },
/// {
/// "name": "decimal",
/// "type": {
/// "logicalType": "decimal",
/// "type": "double",
/// "scale": 1,
/// "precision": 4
/// }
/// },
/// {
/// "name": "implicit_decimal",
/// "type": {
/// "logicalType": "decimal",
/// "type": "bytes",
/// "scale": 1,
/// "precision": 4
/// }
/// },
/// {
/// "name": "custom",
/// "type": {
/// "logicalType": "custom-logical-type",
/// "type": "string"
/// }
/// }
/// ]
/// }"#;
///
/// # let actual_schema = serde_json::to_string_pretty(&LogicalTypes::schema_mut()).unwrap();
/// assert_eq!(actual_schema, expected_schema);
/// ```
///
/// # Generics
///
/// Generics are supported - see
/// [the `tests` module](https://github.com/Ten0/serde_avro_fast/blob/master/serde_avro_derive/tests/derive_schema.rs)
/// for more advanced examples
pub fn build_schema_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let derive_input = syn::parse_macro_input!(input as syn::DeriveInput);

Expand Down

0 comments on commit e9fbe05

Please sign in to comment.