Add Runnable and Emptiable trait and derive
This commit is contained in:
parent
387c043367
commit
8b3aebb4e9
16 changed files with 285 additions and 20 deletions
|
@ -1,11 +1,11 @@
|
|||
mod trusted_peer;
|
||||
mod trusted_node;
|
||||
mod record_deletion;
|
||||
|
||||
pub use trusted_peer::{
|
||||
ActiveModel as TrustedPeerActiveModel,
|
||||
Column as TrustedPeerColumn,
|
||||
Entity as TrustedPeerEntity,
|
||||
Model as TrustedPeerModel,
|
||||
pub use trusted_node::{
|
||||
ActiveModel as TrustedNodeActiveModel,
|
||||
Column as TrustedNodeColumn,
|
||||
Entity as TrustedNodeEntity,
|
||||
Model as TrustedNodeModel,
|
||||
};
|
||||
|
||||
pub use record_deletion::{
|
||||
|
|
|
@ -9,7 +9,7 @@ use crate::data::value::PeerIdValue;
|
|||
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize)]
|
||||
#[sea_orm(table_name = "trusted_peer")]
|
||||
#[sea_orm(table_name = "trusted_node")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
pub id: Uuid,
|
|
@ -8,20 +8,20 @@ pub struct Migration;
|
|||
#[async_trait::async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
TrustedPeer::up(manager).await?;
|
||||
TrustedNode::up(manager).await?;
|
||||
RecordDeletion::up(manager).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
TrustedPeer::down(manager).await?;
|
||||
TrustedNode::down(manager).await?;
|
||||
RecordDeletion::down(manager).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(DeriveIden)]
|
||||
enum TrustedPeer {
|
||||
enum TrustedNode {
|
||||
Table,
|
||||
Id,
|
||||
CreatedAt,
|
||||
|
@ -33,7 +33,7 @@ enum TrustedPeer {
|
|||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl TableMigration for TrustedPeer {
|
||||
impl TableMigration for TrustedNode {
|
||||
async fn up<'a>(manager: &'a SchemaManager<'a>) -> Result<(), DbErr> {
|
||||
manager.create_table(
|
||||
Table::create()
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
pub mod async_convert;
|
||||
pub mod cache;
|
||||
pub mod config;
|
||||
pub mod data;
|
||||
|
@ -10,3 +9,4 @@ pub mod migration;
|
|||
pub mod p2p;
|
||||
#[cfg(any(test, feature="test"))]
|
||||
pub mod tests;
|
||||
pub mod utils;
|
||||
|
|
|
@ -2,7 +2,7 @@ mod node;
|
|||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{async_convert::{AsyncTryFrom, AsyncTryInto}, error::Error};
|
||||
use crate::{utils::async_convert::{AsyncTryFrom, AsyncTryInto}, error::Error};
|
||||
|
||||
pub trait Message: DeserializeOwned + Sized + Serialize {
|
||||
fn into_writer<W: std::io::Write>(&self, writer: W) -> Result<(), ciborium::ser::Error<std::io::Error>> {
|
||||
|
|
|
@ -2,9 +2,9 @@ use serde::{Deserialize, Serialize};
|
|||
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct ListDeviceRequest;
|
||||
pub struct ListTrustedNodeRequest;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct ListDeviceResponse {
|
||||
node: Vec<crate::data::entity::TrustedNode>
|
||||
pub struct ListTrustedNodeResponse {
|
||||
node: Vec<crate::data::entity::TrustedNodeModel>
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
#[derive(thiserror::Error)]
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum P2pError {
|
||||
|
||||
|
||||
}
|
51
lazy-supplements-core/src/utils/emptiable.rs
Normal file
51
lazy-supplements-core/src/utils/emptiable.rs
Normal file
|
@ -0,0 +1,51 @@
|
|||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
pub trait Emptiable{
|
||||
fn empty() -> Self;
|
||||
fn is_empty(&self) -> bool;
|
||||
}
|
||||
|
||||
impl<T> Emptiable for Vec<T> {
|
||||
fn empty() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
fn is_empty(&self) -> bool {
|
||||
self.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Emptiable for Option<T> {
|
||||
fn empty() -> Self {
|
||||
None
|
||||
}
|
||||
fn is_empty(&self) -> bool {
|
||||
self.is_none()
|
||||
}
|
||||
}
|
||||
|
||||
impl Emptiable for String {
|
||||
fn empty() -> Self {
|
||||
String::new()
|
||||
}
|
||||
fn is_empty(&self) -> bool {
|
||||
self.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> Emptiable for HashMap<T, U> {
|
||||
fn empty() -> Self {
|
||||
HashMap::new()
|
||||
}
|
||||
fn is_empty(&self) -> bool {
|
||||
self.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Emptiable for HashSet<T> {
|
||||
fn empty() -> Self {
|
||||
HashSet::new()
|
||||
}
|
||||
fn is_empty(&self) -> bool {
|
||||
self.is_empty()
|
||||
}
|
||||
}
|
3
lazy-supplements-core/src/utils/mod.rs
Normal file
3
lazy-supplements-core/src/utils/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub mod async_convert;
|
||||
pub mod emptiable;
|
||||
pub mod runnable;
|
3
lazy-supplements-core/src/utils/runnable.rs
Normal file
3
lazy-supplements-core/src/utils/runnable.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub trait Runnable {
|
||||
async fn run(self);
|
||||
}
|
|
@ -19,4 +19,5 @@ syn = { version = "2.0.104", features = ["full"] }
|
|||
chrono.workspace = true
|
||||
lazy-supplements-core.workspace = true
|
||||
sea-orm.workspace = true
|
||||
tokio.workspace = true
|
||||
uuid.workspace = true
|
67
lazy-supplements-macros/src/derive.rs
Normal file
67
lazy-supplements-macros/src/derive.rs
Normal file
|
@ -0,0 +1,67 @@
|
|||
use syn::{DataEnum, DataStruct, Fields, FieldsNamed, Ident, Type};
|
||||
|
||||
fn extract_fields_named_from_data_struct(data: &DataStruct) -> &FieldsNamed {
|
||||
match data.fields {
|
||||
Fields::Named(ref fields) => fields,
|
||||
_ => panic!("all fields must be named.")
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_idents_from_fields_named_with_attribute<'a>(fields: &'a FieldsNamed, attribute: &'static str) -> Vec<&'a Ident>{
|
||||
fields.named.iter()
|
||||
.filter_map(|field| {
|
||||
field.attrs.iter()
|
||||
.find_map(|attr| {
|
||||
if attr.path().is_ident(attribute) {
|
||||
field.ident.as_ref()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}).collect()
|
||||
}
|
||||
|
||||
pub fn extract_idents_and_types_from_data_struct_with_attribute<'a>(data: &'a DataStruct, attribute: &'static str) -> Vec<(Ident, Type)>{
|
||||
let fields = extract_fields_named_from_data_struct(data);
|
||||
fields.named.iter().filter_map(|field| {
|
||||
field.attrs.iter()
|
||||
.find_map(|attr| {
|
||||
if attr.path().is_ident(attribute) {
|
||||
Some((field.ident.clone().unwrap(), field.ty.clone()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}).collect()
|
||||
}
|
||||
|
||||
pub fn extract_idents_and_types_from_data_struct<'a>(data: &'a DataStruct) -> Vec<(Ident, Type)>{
|
||||
let fields = extract_fields_named_from_data_struct(data);
|
||||
fields.named.iter().map(|x| {(x.ident.clone().unwrap(), x.ty.clone())}).collect()
|
||||
}
|
||||
|
||||
pub fn unwrap_vec_or_panic<T>(mut source: Vec<T>, msg: &'static str) -> T {
|
||||
if source.len() == 1 {
|
||||
source.pop().unwrap()
|
||||
} else {
|
||||
panic!("{}", msg)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_idents_and_types_from_enum_struct<'a>(data: &'a DataEnum) -> Vec<(Ident, Type)> {
|
||||
data.variants.iter().map(|variant| {
|
||||
let mut fields: Vec<Type> = match &variant.fields {
|
||||
Fields::Unnamed(fields_unnamed) => {
|
||||
fields_unnamed.unnamed.iter().map(|x| {
|
||||
x.ty.clone()
|
||||
}).collect()
|
||||
},
|
||||
_ => panic!("Fields of enum variant must be unnamed!")
|
||||
};
|
||||
if fields.len() == 1 {
|
||||
(variant.ident.clone(), fields.pop().unwrap())
|
||||
} else {
|
||||
panic!("Fields of enum variant must be single!")
|
||||
}
|
||||
}).collect()
|
||||
}
|
|
@ -1,8 +1,11 @@
|
|||
mod derive;
|
||||
|
||||
use heck::ToUpperCamelCase;
|
||||
use proc_macro::{self, TokenStream};
|
||||
use proc_macro2::Span;
|
||||
use quote::{format_ident, quote, ToTokens};
|
||||
use syn::{parse_macro_input, Data, DeriveInput, Expr, ExprTuple, Field, Fields, FieldsNamed, Ident};
|
||||
use syn::{parse_macro_input, Data, DataStruct, DeriveInput, Expr, ExprTuple, Field, Fields, FieldsNamed, Ident};
|
||||
use derive::*;
|
||||
|
||||
#[proc_macro_derive(SyncableModel, attributes(syncable))]
|
||||
pub fn syncable_model(input: TokenStream) -> TokenStream {
|
||||
|
@ -78,7 +81,7 @@ fn extract_unique_field_ident<'a>(fields: &'a FieldsNamed, attribute_arg: &'stat
|
|||
return fields.pop().unwrap()
|
||||
} else {
|
||||
panic!("Model must need one {} field attribute", attribute_arg);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn extract_field_idents<'a>(fields: &'a FieldsNamed, attribute_arg: &'static str) -> Vec<&'a Ident>{
|
||||
|
@ -132,3 +135,82 @@ fn extract_fields(data: &Data) -> &FieldsNamed {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
#[proc_macro_derive(Emptiable)]
|
||||
pub fn emptiable(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
let type_ident = input.ident;
|
||||
match input.data {
|
||||
Data::Struct(ref data) => {
|
||||
let field_idents = extract_idents_and_types_from_data_struct(data);
|
||||
let is_empty_iter = field_idents.iter().map(|(ident, type_name)| {
|
||||
quote!{
|
||||
<#type_name as Emptiable>::is_empty(&self.#ident)
|
||||
}
|
||||
});
|
||||
let empty_iter = field_idents.iter().map(|(ident, type_name)| {
|
||||
quote!{
|
||||
#ident: <#type_name as Emptiable>::empty(),
|
||||
}
|
||||
});
|
||||
quote!{
|
||||
impl Emptiable for #type_ident {
|
||||
fn empty() -> Self {
|
||||
Self {
|
||||
#(#empty_iter)*
|
||||
}
|
||||
}
|
||||
fn is_empty(&self) -> bool {
|
||||
#(#is_empty_iter)&&*
|
||||
}
|
||||
}
|
||||
}.into()
|
||||
}
|
||||
Data::Enum(ref fields) => {
|
||||
todo!()
|
||||
|
||||
},
|
||||
_ => panic!("struct or enum expected, but got union.")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#[proc_macro_derive(Runnable, attributes(runnable))]
|
||||
pub fn runnable(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
let type_ident = input.ident;
|
||||
match input.data {
|
||||
Data::Struct(ref data) => {
|
||||
let mut idents = extract_idents_and_types_from_data_struct_with_attribute(data, "runnable");
|
||||
let (field_ident, field_type) = unwrap_vec_or_panic(idents, "Runnable struct must have one field with runnable attribute");
|
||||
|
||||
quote!{
|
||||
impl Runnable for #type_ident {
|
||||
async fn run(self) {
|
||||
<#field_type as Runnable>::run(self.#field_ident).await
|
||||
}
|
||||
}
|
||||
}.into()
|
||||
}
|
||||
Data::Enum(ref variants) => {
|
||||
let quote_vec = extract_idents_and_types_from_enum_struct(&variants);
|
||||
let quote_iter = quote_vec.iter().map(|(variant_ident, variant_type)|{
|
||||
quote!{
|
||||
Self::#variant_ident(x) => <#variant_type as Runnable>::run(x).await,
|
||||
}
|
||||
});
|
||||
quote!{
|
||||
impl Runnable for #type_ident {
|
||||
async fn run(self) {
|
||||
match self {
|
||||
#(#quote_iter)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}.into()
|
||||
|
||||
},
|
||||
_ => panic!("struct or enum expected, but got union.")
|
||||
|
||||
}
|
||||
}
|
26
lazy-supplements-macros/tests/derive_emptiable.rs
Normal file
26
lazy-supplements-macros/tests/derive_emptiable.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use lazy_supplements_core::utils::emptiable::Emptiable;
|
||||
use lazy_supplements_macros::Emptiable;
|
||||
|
||||
#[derive(Debug, PartialEq, Emptiable)]
|
||||
struct EmptiableStruct{
|
||||
vec: Vec<u8>,
|
||||
text: String,
|
||||
map: HashMap<u8, u8>,
|
||||
set: HashSet<u8>,
|
||||
opt: Option<u8>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn test() {
|
||||
use std::hash::Hash;
|
||||
let empty = EmptiableStruct::empty();
|
||||
assert_eq!(&empty, &EmptiableStruct{
|
||||
vec: Vec::new(),
|
||||
text: String::new(),
|
||||
map: HashMap::new(),
|
||||
set: HashSet::new(),
|
||||
opt: None,
|
||||
})
|
||||
}
|
32
lazy-supplements-macros/tests/derive_runnable.rs
Normal file
32
lazy-supplements-macros/tests/derive_runnable.rs
Normal file
|
@ -0,0 +1,32 @@
|
|||
use lazy_supplements_core::utils::runnable::Runnable;
|
||||
use lazy_supplements_macros::Runnable;
|
||||
|
||||
struct RunnableStruct1;
|
||||
|
||||
impl Runnable for RunnableStruct1 {
|
||||
async fn run(self) {
|
||||
print!("Run {}", stringify!(RunnableStruct1::run()))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Runnable)]
|
||||
enum RunnableEnum {
|
||||
Struct1(RunnableStruct1),
|
||||
}
|
||||
|
||||
#[derive(Runnable)]
|
||||
struct RunnableStruct2 {
|
||||
#[runnable]
|
||||
runnable: RunnableEnum,
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test() {
|
||||
let runnable = RunnableStruct2{
|
||||
runnable: RunnableEnum::Struct1(RunnableStruct1)
|
||||
};
|
||||
runnable.run().await;
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Add table
Reference in a new issue