748bb3abab
`cargo machete` is a very useful tool that figures out when you aren't using a dependency. I have run this locally to remove unused dependencies. I've also added a CI job to make sure we catch these in the future. Sometimes it reports false positives, e.g. when a dependency isn't used directly but in macro-generated code (e.g. with `strum`). I have added `"ignored"` clauses to the `Cargo.toml` files where appropriate. V3_GIT_ORIGIN_REV_ID: ed015089b695cec8eeb03ce455d6dd3cd312a016 |
||
---|---|---|
.. | ||
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