feat: init
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
extern crate proc_macro;
|
||||
|
||||
mod method;
|
||||
|
||||
use crate::method::{
|
||||
generate_native_model_decode_body, generate_native_model_decode_upgrade_body,
|
||||
generate_native_model_encode_body, generate_native_model_encode_downgrade_body,
|
||||
generate_native_model_id, generate_native_model_version,
|
||||
};
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::meta::ParseNestedMeta;
|
||||
use syn::parse::{Parse, Result};
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::token;
|
||||
use syn::{parse_macro_input, DeriveInput, LitInt, Path, Token};
|
||||
|
||||
// Inspiration: https://docs.rs/syn/2.0.29/syn/meta/fn.parser.html#example-1
|
||||
#[derive(Default)]
|
||||
pub(crate) struct ModelAttributes {
|
||||
pub(crate) id: Option<LitInt>,
|
||||
pub(crate) version: Option<LitInt>,
|
||||
// type
|
||||
pub(crate) from: Option<Path>,
|
||||
// (type, try_from::Error type)
|
||||
pub(crate) try_from: Option<(Path, Path)>,
|
||||
}
|
||||
|
||||
impl ModelAttributes {
|
||||
fn parse(&mut self, meta: ParseNestedMeta) -> Result<()> {
|
||||
if meta.path.is_ident("id") {
|
||||
self.id = Some(meta.value()?.parse()?);
|
||||
} else if meta.path.is_ident("version") {
|
||||
self.version = Some(meta.value()?.parse()?);
|
||||
} else if meta.path.is_ident("from") {
|
||||
self.from = Some(meta.value()?.parse()?);
|
||||
} else if meta.path.is_ident("try_from") {
|
||||
let tuple_try_from: TupleTryFrom = meta.value()?.parse()?;
|
||||
let mut fields = tuple_try_from.fields.into_iter();
|
||||
self.try_from.replace((
|
||||
fields.next().unwrap().clone(),
|
||||
fields.next().unwrap().clone(),
|
||||
));
|
||||
} else {
|
||||
panic!(
|
||||
"Unknown attribute: {}",
|
||||
meta.path.get_ident().unwrap().to_string()
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct TupleTryFrom {
|
||||
pub(crate) _parent_token: token::Paren,
|
||||
pub(crate) fields: Punctuated<Path, Token![,]>,
|
||||
}
|
||||
|
||||
impl Parse for TupleTryFrom {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let content;
|
||||
Ok(TupleTryFrom {
|
||||
_parent_token: syn::parenthesized!(content in input),
|
||||
fields: content.parse_terminated(Path::parse, Token![,])?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Macro which add identity and version to your rust type.
|
||||
///
|
||||
/// Attributes:
|
||||
/// - `id = u32`: The unique identifier of the model.
|
||||
/// - `version = u32`: The version of the model.
|
||||
/// - `from = type`: Optional, the previous version of the model.
|
||||
/// - `type`: The previous version of the model that you use for the From implementation.
|
||||
/// - `try_from = (type, error)`: Optional, the previous version of the model with error handling.
|
||||
/// - `type`: The previous version of the model that you use for the TryFrom implementation.
|
||||
/// - `error`: The error type that you use for the TryFrom implementation.
|
||||
///
|
||||
/// See examples:
|
||||
/// - [Setup your data model](https://github.com/vincent-herlemont/native_model_private#setup-your-data-model).
|
||||
/// - other [examples](https://github.com/vincent-herlemont/native_model/tree/master/tests/example)
|
||||
#[proc_macro_attribute]
|
||||
pub fn native_model(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let ast = parse_macro_input!(input as DeriveInput);
|
||||
let struct_name = &ast.ident;
|
||||
|
||||
let mut attrs = ModelAttributes::default();
|
||||
let model_attributes_parser = syn::meta::parser(|meta| attrs.parse(meta));
|
||||
parse_macro_input!(args with model_attributes_parser);
|
||||
|
||||
let native_model_id_fn = generate_native_model_id(&attrs);
|
||||
let native_model_version_fn = generate_native_model_version(&attrs);
|
||||
let native_model_encode_body_fn = generate_native_model_encode_body();
|
||||
let native_model_encode_downgrade_body_fn = generate_native_model_encode_downgrade_body(&attrs);
|
||||
let native_model_decode_body_fn = generate_native_model_decode_body();
|
||||
let native_model_decode_upgrade_body_fn = generate_native_model_decode_upgrade_body(&attrs);
|
||||
|
||||
let gen = quote! {
|
||||
#ast
|
||||
|
||||
impl native_model::Model for #struct_name {
|
||||
#native_model_id_fn
|
||||
#native_model_version_fn
|
||||
#native_model_encode_body_fn
|
||||
#native_model_encode_downgrade_body_fn
|
||||
#native_model_decode_body_fn
|
||||
#native_model_decode_upgrade_body_fn
|
||||
}
|
||||
};
|
||||
|
||||
gen.into()
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
|
||||
pub(crate) fn generate_native_model_decode_body() -> TokenStream {
|
||||
let gen = quote! {
|
||||
fn native_model_decode_body(data: Vec<u8>) -> Result<Self, native_model::DecodeBodyError> {
|
||||
native_model_decode_body(data).map_err(|e| native_model::DecodeBodyError {
|
||||
msg: format!("{}", e),
|
||||
source: e.into(),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
gen.into()
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
use crate::ModelAttributes;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
|
||||
pub(crate) fn generate_native_model_decode_upgrade_body(attrs: &ModelAttributes) -> TokenStream {
|
||||
let native_model_from = attrs.from.clone();
|
||||
let native_model_try_from = attrs.try_from.clone();
|
||||
|
||||
let model_from_or_try_from = if let Some(from) = native_model_from {
|
||||
quote! {
|
||||
#from::native_model_decode_upgrade_body(data, x).map(|a| a.into())
|
||||
}
|
||||
} else if let Some((try_from, error_try_from)) = native_model_try_from {
|
||||
quote! {
|
||||
let result = #try_from::native_model_decode_upgrade_body(data, x).map(|b| {
|
||||
b.try_into()
|
||||
.map_err(|e: #error_try_from| native_model::UpgradeError {
|
||||
msg: format!("{}", e),
|
||||
source: e.into(),
|
||||
})
|
||||
})??;
|
||||
Ok(result)
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
Err(native_model::Error::UpgradeNotSupported {
|
||||
from: x,
|
||||
to: Self::native_model_version(),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
let gen = quote! {
|
||||
fn native_model_decode_upgrade_body(data: Vec<u8>, x: u32) -> native_model::Result<Self> {
|
||||
if x == Self::native_model_version() {
|
||||
let result = Self::native_model_decode_body(data)?;
|
||||
Ok(result)
|
||||
} else if x < Self::native_model_version() {
|
||||
#model_from_or_try_from
|
||||
} else {
|
||||
Err(native_model::Error::UpgradeNotSupported {
|
||||
from: x,
|
||||
to: Self::native_model_version(),
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
gen
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
|
||||
pub(crate) fn generate_native_model_encode_body() -> TokenStream {
|
||||
let gen = quote! {
|
||||
fn native_model_encode_body(&self) -> Result<Vec<u8>, native_model::EncodeBodyError> {
|
||||
native_model_encode_body(self).map_err(|e| native_model::EncodeBodyError {
|
||||
msg: format!("{}", e),
|
||||
source: e.into(),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
gen.into()
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
use crate::ModelAttributes;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
|
||||
pub(crate) fn generate_native_model_encode_downgrade_body(attrs: &ModelAttributes) -> TokenStream {
|
||||
let native_model_from = attrs.from.clone();
|
||||
let native_model_try_from = attrs.try_from.clone();
|
||||
|
||||
let model_from_or_try_from = if let Some(from) = native_model_from {
|
||||
quote! {
|
||||
#from::native_model_encode_downgrade_body(self.into(), version)
|
||||
}
|
||||
} else if let Some((try_from, error_try_from)) = native_model_try_from {
|
||||
quote! {
|
||||
let result = #try_from::native_model_encode_downgrade_body(
|
||||
self.try_into()
|
||||
.map_err(|e: #error_try_from| native_model::DowngradeError {
|
||||
msg: format!("{}", e),
|
||||
source: e.into(),
|
||||
})?,
|
||||
version,
|
||||
)?;
|
||||
Ok(result)
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
Err(native_model::Error::DowngradeNotSupported {
|
||||
from: version,
|
||||
to: Self::native_model_version(),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
let gen = quote! {
|
||||
fn native_model_encode_downgrade_body(self, version: u32) -> native_model::Result<Vec<u8>> {
|
||||
if version == Self::native_model_version() {
|
||||
let result = self.native_model_encode_body()?;
|
||||
Ok(result)
|
||||
} else if version < Self::native_model_version() {
|
||||
#model_from_or_try_from
|
||||
} else {
|
||||
Err(native_model::Error::DowngradeNotSupported {
|
||||
from: version,
|
||||
to: Self::native_model_version(),
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
gen
|
||||
}
|
||||
|
||||
// #[error("Wrong type id expected: {}, actual: {}", expected, actual)]
|
||||
// WrongTypeId { expected: u32, actual: u32 },
|
||||
@@ -0,0 +1,13 @@
|
||||
use crate::ModelAttributes;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
|
||||
pub(crate) fn generate_native_model_id(model_attributes: &ModelAttributes) -> TokenStream {
|
||||
let native_model_id = model_attributes.id.clone().unwrap();
|
||||
let gen = quote! {
|
||||
fn native_model_id() -> u32 {
|
||||
#native_model_id
|
||||
}
|
||||
};
|
||||
gen
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
mod decode_body;
|
||||
mod decode_upgrade_body;
|
||||
mod encode_body;
|
||||
mod encode_downgrade_body;
|
||||
mod id;
|
||||
mod version;
|
||||
|
||||
pub(crate) use decode_body::*;
|
||||
pub(crate) use decode_upgrade_body::*;
|
||||
pub(crate) use encode_body::*;
|
||||
pub(crate) use encode_downgrade_body::*;
|
||||
pub(crate) use id::*;
|
||||
pub(crate) use version::*;
|
||||
@@ -0,0 +1,13 @@
|
||||
use crate::ModelAttributes;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
|
||||
pub(crate) fn generate_native_model_version(model_attributes: &ModelAttributes) -> TokenStream {
|
||||
let native_model_version = model_attributes.version.clone().unwrap();
|
||||
let gen = quote! {
|
||||
fn native_model_version() -> u32 {
|
||||
#native_model_version
|
||||
}
|
||||
};
|
||||
gen
|
||||
}
|
||||
Reference in New Issue
Block a user