diff --git a/macros/src/from_json.rs b/macros/src/from_json.rs index 4964eff0..924d3c8f 100644 --- a/macros/src/from_json.rs +++ b/macros/src/from_json.rs @@ -4,7 +4,17 @@ use quote::quote; use syn::{parse_macro_input, Data, DeriveInput, Field, Fields, FieldsNamed, FieldsUnnamed, Ident}; pub fn derive(input: TokenStream) -> TokenStream { - let DeriveInput { ident, data, .. } = parse_macro_input!(input); + let DeriveInput { + ident, data, attrs, .. + } = parse_macro_input!(input); + let isomdl_path = Ident::new( + &attrs + .iter() + .filter_map(super::crate_path) + .next() + .unwrap_or_else(|| "isomdl".to_owned()), + Span::call_site(), + ); let struct_data = match data { Data::Struct(s) => s, Data::Enum(_) => { @@ -22,8 +32,8 @@ pub fn derive(input: TokenStream) -> TokenStream { }; match struct_data.fields { - Fields::Named(f) => named_fields(ident, f), - Fields::Unnamed(f) => unnamed_fields(ident, f), + Fields::Named(f) => named_fields(isomdl_path, ident, f), + Fields::Unnamed(f) => unnamed_fields(isomdl_path, ident, f), Fields::Unit => quote! { compile_error!("cannot derive FromJson for unit struct"); } @@ -31,7 +41,7 @@ pub fn derive(input: TokenStream) -> TokenStream { } } -fn named_fields(ident: Ident, input: FieldsNamed) -> TokenStream { +fn named_fields(isomdl_path: Ident, ident: Ident, input: FieldsNamed) -> TokenStream { let mut conversions = quote! {}; let mut fields = quote! {}; @@ -83,7 +93,7 @@ fn named_fields(ident: Ident, input: FieldsNamed) -> TokenStream { mod #mod_name { use serde_json::Value; use super::*; - use crate::definitions::traits::{FromJson, FromJsonError, FromJsonMap}; + use #isomdl_path::definitions::traits::{FromJson, FromJsonError, FromJsonMap}; impl FromJson for #ident { fn from_json(value: &Value) -> Result<#ident, FromJsonError> { let map = match value { @@ -115,7 +125,7 @@ fn named_fields(ident: Ident, input: FieldsNamed) -> TokenStream { output.into() } -fn unnamed_fields(ident: Ident, mut input: FieldsUnnamed) -> TokenStream { +fn unnamed_fields(isomdl_path: Ident, ident: Ident, mut input: FieldsUnnamed) -> TokenStream { let field_type = match input.unnamed.pop() { Some(pair) => pair.into_value().ty, @@ -140,7 +150,7 @@ fn unnamed_fields(ident: Ident, mut input: FieldsUnnamed) -> TokenStream { let output = quote! { mod #mod_name { use super::*; - use crate::definitions::traits::{FromJson, FromJsonError}; + use #isomdl_path::definitions::traits::{FromJson, FromJsonError}; use serde_json::Value; impl FromJson for #ident { fn from_json(value: &Value) -> Result<#ident, FromJsonError> { diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 76272c3c..e778830d 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -56,6 +56,32 @@ fn is_optional(ty: &Type) -> bool { } } +// Attribute for setting the path to the isomdl crate, mostly for use +// internally in isomdl to refer to itself as 'crate'. +fn crate_path(attr: &Attribute) -> Option { + get_isomdl_attributes(attr)? + .filter_map(|nested_meta| { + let meta = match nested_meta { + NestedMeta::Meta(meta) => meta, + _ => return None, + }; + match meta { + Meta::NameValue(pair) => { + if !pair.path.is_ident("crate") { + return None; + } + if let Lit::Str(s) = pair.lit { + Some(s.value()) + } else { + None + } + } + _ => None, + } + }) + .next() +} + fn rename(attr: &Attribute) -> Option { get_isomdl_attributes(attr)? .filter_map(|nested_meta| { diff --git a/macros/src/to_cbor.rs b/macros/src/to_cbor.rs index f1749d5f..c2ec7d84 100644 --- a/macros/src/to_cbor.rs +++ b/macros/src/to_cbor.rs @@ -4,7 +4,17 @@ use quote::quote; use syn::{parse_macro_input, Data, DeriveInput, Field, Fields, FieldsNamed, FieldsUnnamed, Ident}; pub fn derive(input: TokenStream) -> TokenStream { - let DeriveInput { ident, data, .. } = parse_macro_input!(input); + let DeriveInput { + ident, data, attrs, .. + } = parse_macro_input!(input); + let isomdl_path = Ident::new( + &attrs + .iter() + .filter_map(super::crate_path) + .next() + .unwrap_or_else(|| "isomdl".to_owned()), + Span::call_site(), + ); let struct_data = match data { Data::Struct(s) => s, Data::Enum(_) => { @@ -22,8 +32,8 @@ pub fn derive(input: TokenStream) -> TokenStream { }; match struct_data.fields { - Fields::Named(f) => named_fields(ident, f), - Fields::Unnamed(f) => unnamed_fields(ident, f), + Fields::Named(f) => named_fields(isomdl_path, ident, f), + Fields::Unnamed(f) => unnamed_fields(isomdl_path, ident, f), Fields::Unit => quote! { compile_error!("cannot derive ToCbor for unit struct"); } @@ -31,7 +41,7 @@ pub fn derive(input: TokenStream) -> TokenStream { } } -fn named_fields(ident: Ident, input: FieldsNamed) -> TokenStream { +fn named_fields(isomdl_path: Ident, ident: Ident, input: FieldsNamed) -> TokenStream { let mut conversions = quote! {}; input.named.into_iter().for_each( @@ -78,7 +88,7 @@ fn named_fields(ident: Ident, input: FieldsNamed) -> TokenStream { mod #mod_name { use serde_cbor::Value; use super::*; - use crate::definitions::traits::{ToCbor, ToNamespaceMap}; + use #isomdl_path::definitions::traits::{ToCbor, ToNamespaceMap}; impl ToNamespaceMap for #ident { fn to_ns_map(self) -> std::collections::BTreeMap { let mut map = std::collections::BTreeMap::default(); @@ -103,7 +113,7 @@ fn named_fields(ident: Ident, input: FieldsNamed) -> TokenStream { output.into() } -fn unnamed_fields(ident: Ident, mut input: FieldsUnnamed) -> TokenStream { +fn unnamed_fields(isomdl_path: Ident, ident: Ident, mut input: FieldsUnnamed) -> TokenStream { let field_type = match input.unnamed.pop() { Some(pair) => pair.into_value().ty, None => { @@ -129,7 +139,7 @@ fn unnamed_fields(ident: Ident, mut input: FieldsUnnamed) -> TokenStream { let output = quote! { mod #mod_name { use super::*; - use crate::definitions::traits::{ToCbor, ToCborError}; + use #isomdl_path::definitions::traits::{ToCbor, ToCborError}; use serde_cbor::Value; impl ToCbor for #ident { fn to_cbor(self) -> Value { diff --git a/src/definitions/namespaces/org_iso_18013_5_1/driving_privileges.rs b/src/definitions/namespaces/org_iso_18013_5_1/driving_privileges.rs index 8d7b3005..f78aa656 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/driving_privileges.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/driving_privileges.rs @@ -11,6 +11,7 @@ use strum_macros::{AsRefStr, EnumString, EnumVariantNames}; /// `driving_privileges` in the org.iso.18013.5.1 namespace. #[derive(Clone, Debug, FromJson)] +#[isomdl(crate = "crate")] pub struct DrivingPrivileges(Vec); impl From for Cbor { @@ -71,6 +72,7 @@ impl crate::definitions::traits::FromJson for VehicleCategoryCode { } #[derive(Clone, Debug, FromJson, ToCbor)] +#[isomdl(crate = "crate")] pub struct DrivingPrivilege { pub vehicle_category_code: VehicleCategoryCode, pub issue_date: Option, @@ -79,6 +81,7 @@ pub struct DrivingPrivilege { } #[derive(Clone, Debug, FromJson)] +#[isomdl(crate = "crate")] pub struct Codes(NonEmptyVec); impl From for Cbor { @@ -88,6 +91,7 @@ impl From for Cbor { } #[derive(Clone, Debug, FromJson, ToCbor)] +#[isomdl(crate = "crate")] pub struct Code { pub code: String, pub sign: Option, diff --git a/src/definitions/namespaces/org_iso_18013_5_1/mod.rs b/src/definitions/namespaces/org_iso_18013_5_1/mod.rs index da7e8d8c..e5bd0e0d 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/mod.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/mod.rs @@ -29,6 +29,7 @@ use crate::{ /// The `org.iso.18013.5.1` namespace. #[derive(Debug, Clone, FromJson, ToCbor)] +#[isomdl(crate = "crate")] pub struct OrgIso1801351 { pub family_name: Latin1, pub given_name: Latin1, diff --git a/src/definitions/namespaces/org_iso_18013_5_1_aamva/domestic_driving_privileges.rs b/src/definitions/namespaces/org_iso_18013_5_1_aamva/domestic_driving_privileges.rs index 9adb4614..0ea100fd 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1_aamva/domestic_driving_privileges.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1_aamva/domestic_driving_privileges.rs @@ -8,6 +8,7 @@ use serde_cbor::Value as Cbor; /// `domestic_driving_privileges` in the org.iso.18013.5.1.aamva namespace, as per the AAMVA mDL Implementation /// Guidelines (Version 1.0). #[derive(Clone, Debug, FromJson)] +#[isomdl(crate = "crate")] pub struct DomesticDrivingPrivileges(Vec); impl ToCbor for DomesticDrivingPrivileges { @@ -17,6 +18,7 @@ impl ToCbor for DomesticDrivingPrivileges { } #[derive(Clone, Debug, FromJson, ToCbor)] +#[isomdl(crate = "crate")] pub struct DomesticDrivingPrivilege { pub domestic_vehicle_class: Option, pub domestic_vehicle_restrictions: Option, @@ -24,6 +26,7 @@ pub struct DomesticDrivingPrivilege { } #[derive(Clone, Debug, FromJson, ToCbor)] +#[isomdl(crate = "crate")] pub struct DomesticVehicleClass { pub domestic_vehicle_class_code: String, pub domestic_vehicle_class_description: String, @@ -32,6 +35,7 @@ pub struct DomesticVehicleClass { } #[derive(Clone, Debug, FromJson)] +#[isomdl(crate = "crate")] pub struct DomesticVehicleRestrictions(NonEmptyVec); impl ToCbor for DomesticVehicleRestrictions { @@ -47,12 +51,14 @@ impl ToCbor for DomesticVehicleRestrictions { } #[derive(Clone, Debug, FromJson, ToCbor)] +#[isomdl(crate = "crate")] pub struct DomesticVehicleRestriction { pub domestic_vehicle_restriction_code: Option, pub domestic_vehicle_restriction_description: String, } #[derive(Clone, Debug, FromJson)] +#[isomdl(crate = "crate")] pub struct DomesticVehicleEndorsements(NonEmptyVec); impl ToCbor for DomesticVehicleEndorsements { @@ -68,6 +74,7 @@ impl ToCbor for DomesticVehicleEndorsements { } #[derive(Clone, Debug, FromJson, ToCbor)] +#[isomdl(crate = "crate")] pub struct DomesticVehicleEndorsement { pub domestic_vehicle_endorsement_code: Option, pub domestic_vehicle_endorsement_description: String, diff --git a/src/definitions/namespaces/org_iso_18013_5_1_aamva/mod.rs b/src/definitions/namespaces/org_iso_18013_5_1_aamva/mod.rs index 21d2a114..8b556933 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1_aamva/mod.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1_aamva/mod.rs @@ -26,6 +26,7 @@ use crate::macros::{FromJson, ToCbor}; /// `org.iso.18013.5.1.aamva` namespace, as per the AAMVA mDL Implementation /// Guidelines (Version 1.2). #[derive(Debug, Clone, FromJson, ToCbor)] +#[isomdl(crate = "crate")] pub struct OrgIso1801351Aamva { pub domestic_driving_privileges: DomesticDrivingPrivileges, pub name_suffix: Option, diff --git a/src/definitions/traits/from_json.rs b/src/definitions/traits/from_json.rs index 74a63118..4241845d 100644 --- a/src/definitions/traits/from_json.rs +++ b/src/definitions/traits/from_json.rs @@ -179,6 +179,7 @@ mod tests { use serde_json::{json, Value}; #[derive(FromJson)] + #[isomdl(crate = "crate")] struct S { a: Option, } diff --git a/tests/common.rs b/tests/common.rs index b7b6b65e..c47b25c3 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] use anyhow::{anyhow, Context, Result}; use signature::Signer; use uuid::Uuid; diff --git a/tests/namespace.rs b/tests/namespace.rs new file mode 100644 index 00000000..aff0356c --- /dev/null +++ b/tests/namespace.rs @@ -0,0 +1,18 @@ +use isomdl::{ + definitions::traits::FromJson, + macros::{FromJson, ToCbor}, +}; + +#[derive(FromJson, ToCbor)] +pub struct NewNamespace { + field: String, +} + +#[test] +fn new_namespace() { + let json = serde_json::json!({ + "field": "value" + }); + + NewNamespace::from_json(&json).unwrap(); +}