c7d9baaf66
When trying to reduce the number of dependencies we use in the engine, I was blocked by a few `.clone()` calls that, on inspection, turned out to be completely unnecessary. I have replaced those with passing by reference, and then gone on a pedant spree. I enabled the `needless_pass_by_value` Clippy warning and fixed it everywhere that it highlighted. In most places, this meant adding `&`, but I also marked some types as `Copy`, which makes pass-by-value the right move. In one place, I replaced calls to `async_map` with `if` and `else`, to avoid constructing closures that capture across async boundaries. This means I could just delete `async_map`. V3_GIT_ORIGIN_REV_ID: 6ff71f0c553b707889d89552eff3e8c001e898cc |
||
---|---|---|
.. | ||
src | ||
Cargo.toml | ||
README.md |
Table Of Contents
Derive Macros for OpenDd
Trait
This crate provides a derive macro for implementing the OpenDd
trait from the open-dds
crate. To utilize it, simply add
opendds-derive
as a dependency in your Cargo.toml
file, alongside the open-dds
crate. Then, include opendds_derive::OpenDd
in the #[derive(..)]
attributes list. Please note that at present, only struct
and enum
types are supported for derivation.
Use #[opendd()]
to specify type level and field or variant level attributes
The Trait
The OpenDd
trait is defined as follows,
pub trait OpenDd: Sized {
fn deserialize(json: serde_json::Value) -> Result<Self, OpenDdDeserializeError>;
fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema;
fn _schema_name() -> String;
fn _schema_is_referenceable() -> bool {
false
}
}
All types used to express the open data domain specification need to have the OpenDd
trait implemented. At present, the OpenDd
trait
exposes methods for deserializing the type from a JSON value and generating a json schema for the type.
Struct
Limitations:
- Only structs with named fields and single unnamed field (newtype) are allowed to use the derive macro
#[derive(opendds_derive::OpenDd)]
struct NamedFieldStruct {
named_field_1: Type1,
named_field_2: Type2,
}
#[derive(opendds_derive::OpenDd)]
struct NewTypeStruct(Field);
- Structs with multiple unnamed fields or no fields are not supported.
struct UnitStruct;
struct UnnamedFieldStruct(Type1, Type2);
Common Behavior:
- All fields of struct are deserialized with their camel-cased fields in the JSON object.
- Having unknown fields in the object results in parse error.
Attributes
Type Level
Only json schema related attributes applicable here.
Field Level
-
#[opendd(default)]
If the value is not present when deserializing, use the
Default::default()
. -
#[opendd(rename = "name")]
Deserialize this field with the given name instead of camel-cased Rust field name.
Enum
Limitations:
- All variants should carry exactly one unnamed field.
#[derive(opendds_derive::OpenDd)]
enum MyEnum {
VariantOne(TypeOne),
VariantTwo(TypeTwo),
}
- Variants with no fields, multiple unnamed fields or named fields are not supported
enum Unsupported {
NoFields,
MultipleUnnamed(TypeOne, TypeTwo),
Named{field_1: TypeOne, field_1: TypeTwo},
}
Attributes
Type Level
Enum types are deserialized from JSON objects using a specific key-value pair. The key, also referred to as the tag, must be a
String
value. It determines the variant of the enum to deserialize from the rest of the JSON object's content. The following
type-level attributes provide various ways for deserializing the JSON object along with json schema
related attributes.
-
#[opendd(as_versioned_internally_tagged)]
Using
version
key as tag. The tag value is matched with camel-cased variant name. Rest of the object's content is deserialized as the variant value.Example:
#[derive(opendds_derive::OpenDd)] #[opendd(as_versioned_internally_tagged)] enum VersionedEnum { V1(VersionOne), V2(VersionTwo), } #[derive(opendds_derive::OpenDd)] struct VersionOne { #[opendd(use_serde_json)] field_one: String }
The following object is parsed into
V1(VersionOne)
{ "version": "v1", "fieldOne": "some_value" }
-
#[opendd(as_versioned_with_definition)]
Using
version
key as tag. The tag value is matched with camel-cased variant name. The variant value is deserialized with thedefinition
key value from the object.Example:
#[derive(opendds_derive::OpenDd)] #[opendd(as_versioned_with_definition)] enum VersionedEnum { V1(VersionOne), V2(VersionTwo), } #[derive(opendds_derive::OpenDd)] struct VersionTwo { #[opendd(use_serde_json)] field_two: String }
The following object is parsed into
V2(VersionTwo)
{ "version": "v2", "definition": { "fieldTwo": "some_value" } }
-
#[opendd(as_kind)]
Using
kind
key as tag. The tag value is matched with the exact variant name. Rest of the object's content is deserialized as the variant value.Example:
#[derive(opendds_derive::OpenDd)] #[opendd(as_kind)] enum KindEnum { KindOne(KindOneStruct), KindTwo(KindTwoStruct), } #[derive(opendds_derive::OpenDd)] #[opendd(use_serde_json)] // All field values are deserialized using serde_path_to_error::deserialize() struct KindOneStruct{ field_one: i32, field_two: bool, field_three: String, }
The following object is parsed into
KindOne(KindOneStruct)
{ "kind": "KindOne", "fieldOne": 111, "fieldTwo": false, "fieldThree": "three" }
-
#[opendd(untagged_with_kind)]
The JSON object is not tagged with any variant. Each variant should hold a enum type with
#[opendd(as_kind)]
(see above) implementation. Usingkind
as tag and its value is matched with internal enum variants. The internal enum variants need to havestrum_macros::EnumVariantNames
implementation.Example:
#[derive(opendds_derive::OpenDd)] #[opendd(as_kind)] enum KindEnumOne { VariantOne(OneStruct), VaraintTwo(TwoStruct), } #[derive(opendds_derive::OpenDd)] #[opendd(as_kind)] enum KindEnumTwo { VariantThree(ThreeStruct), VaraintFour(FourStruct) } #[derive(opendds_derive::OpenDd)] #[opendd(untagged_with_kind)] enum UntaggedEnum { KindOne(KindEnumOne), KindTwo(KindEnumTwo), } #[derive(opendds_derive::OpenDd)] struct FourStruct { #[opendd(use_serde_json)] field_four: String, }
The following object is parsed into
UntaggedEnum::KindTwo(KindEnumTwo::VariantFour(FourStruct{field_four: "four"}))
{ "kind": "VariantFour", "fieldFour": "four" }
Variant Level
-
#[opendd(rename = "name")]
Deserialize this variant with the given name and use it to generate enum value for the tag in the json schema.
-
#[opendd(alias = "name")]
Deserialize this variant from the given name or from derived Rust name.
Common JSON Schema attributes
The following json schema related attributes are applicable for both structs and enums.
Type Level
-
#[opendd(json_schema(rename = "rename string value"))]
Use the given name in the generated schema instead of the Rust name.
-
#[opendd(json_schema(title = "title string value"))]
Set the generated schema's title.
-
#[opendd(json_schema(example = "some::function"))]
Include the result of the given function in the generated schema's
examples
.
Field Level
-
#[opendd(json_schema(default_exp = "some::function()"))]
To be used in conjuction with #[opendd(default)]. The given function should return a json value which is included in the generated schema's
default
. Not needed when the field type hasserde::Serialize
trait implemented. The default JSON value will be inferred usingserde_json::json!(Default::default())
.
Notes
- Please make sure the following crates/modules are accessible in the module where the derive macro is used.
opendds
- as module or crate withderive
in the path. For eg. the macro refers the trait withopendds::derive::OpenDd
strum
- to accessstrum::VariantNames