From 8b3aebb4e9443c1bbc840608bebaec3d22280ed7 Mon Sep 17 00:00:00 2001 From: fluo10 Date: Thu, 3 Jul 2025 08:28:59 +0900 Subject: [PATCH] Add Runnable and Emptiable trait and derive --- lazy-supplements-core/src/data/entity/mod.rs | 12 +-- .../{trusted_peer.rs => trusted_node.rs} | 2 +- .../m20220101_000001_create_main_tables.rs | 8 +- lazy-supplements-core/src/lib.rs | 2 +- lazy-supplements-core/src/message/mod.rs | 2 +- lazy-supplements-core/src/message/node.rs | 6 +- lazy-supplements-core/src/p2p/error.rs | 4 +- .../src/{ => utils}/async_convert.rs | 0 lazy-supplements-core/src/utils/emptiable.rs | 51 +++++++++++ lazy-supplements-core/src/utils/mod.rs | 3 + lazy-supplements-core/src/utils/runnable.rs | 3 + lazy-supplements-macros/Cargo.toml | 1 + lazy-supplements-macros/src/derive.rs | 67 +++++++++++++++ lazy-supplements-macros/src/lib.rs | 86 ++++++++++++++++++- .../tests/derive_emptiable.rs | 26 ++++++ .../tests/derive_runnable.rs | 32 +++++++ 16 files changed, 285 insertions(+), 20 deletions(-) rename lazy-supplements-core/src/data/entity/{trusted_peer.rs => trusted_node.rs} (97%) rename lazy-supplements-core/src/{ => utils}/async_convert.rs (100%) create mode 100644 lazy-supplements-core/src/utils/emptiable.rs create mode 100644 lazy-supplements-core/src/utils/mod.rs create mode 100644 lazy-supplements-core/src/utils/runnable.rs create mode 100644 lazy-supplements-macros/src/derive.rs create mode 100644 lazy-supplements-macros/tests/derive_emptiable.rs create mode 100644 lazy-supplements-macros/tests/derive_runnable.rs diff --git a/lazy-supplements-core/src/data/entity/mod.rs b/lazy-supplements-core/src/data/entity/mod.rs index c515558..29094e7 100644 --- a/lazy-supplements-core/src/data/entity/mod.rs +++ b/lazy-supplements-core/src/data/entity/mod.rs @@ -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::{ diff --git a/lazy-supplements-core/src/data/entity/trusted_peer.rs b/lazy-supplements-core/src/data/entity/trusted_node.rs similarity index 97% rename from lazy-supplements-core/src/data/entity/trusted_peer.rs rename to lazy-supplements-core/src/data/entity/trusted_node.rs index 51647a6..1b257b3 100644 --- a/lazy-supplements-core/src/data/entity/trusted_peer.rs +++ b/lazy-supplements-core/src/data/entity/trusted_node.rs @@ -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, diff --git a/lazy-supplements-core/src/data/migration/m20220101_000001_create_main_tables.rs b/lazy-supplements-core/src/data/migration/m20220101_000001_create_main_tables.rs index 9fa0be0..ca9cc97 100644 --- a/lazy-supplements-core/src/data/migration/m20220101_000001_create_main_tables.rs +++ b/lazy-supplements-core/src/data/migration/m20220101_000001_create_main_tables.rs @@ -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() diff --git a/lazy-supplements-core/src/lib.rs b/lazy-supplements-core/src/lib.rs index 77422f7..2baa9c4 100644 --- a/lazy-supplements-core/src/lib.rs +++ b/lazy-supplements-core/src/lib.rs @@ -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; diff --git a/lazy-supplements-core/src/message/mod.rs b/lazy-supplements-core/src/message/mod.rs index 39933eb..38ced6a 100644 --- a/lazy-supplements-core/src/message/mod.rs +++ b/lazy-supplements-core/src/message/mod.rs @@ -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(&self, writer: W) -> Result<(), ciborium::ser::Error> { diff --git a/lazy-supplements-core/src/message/node.rs b/lazy-supplements-core/src/message/node.rs index c587259..174cbe7 100644 --- a/lazy-supplements-core/src/message/node.rs +++ b/lazy-supplements-core/src/message/node.rs @@ -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 +pub struct ListTrustedNodeResponse { + node: Vec } \ No newline at end of file diff --git a/lazy-supplements-core/src/p2p/error.rs b/lazy-supplements-core/src/p2p/error.rs index 6e2f187..4b58cad 100644 --- a/lazy-supplements-core/src/p2p/error.rs +++ b/lazy-supplements-core/src/p2p/error.rs @@ -1,4 +1,4 @@ -#[derive(thiserror::Error)] +#[derive(Debug, thiserror::Error)] pub enum P2pError { - + } \ No newline at end of file diff --git a/lazy-supplements-core/src/async_convert.rs b/lazy-supplements-core/src/utils/async_convert.rs similarity index 100% rename from lazy-supplements-core/src/async_convert.rs rename to lazy-supplements-core/src/utils/async_convert.rs diff --git a/lazy-supplements-core/src/utils/emptiable.rs b/lazy-supplements-core/src/utils/emptiable.rs new file mode 100644 index 0000000..1535c23 --- /dev/null +++ b/lazy-supplements-core/src/utils/emptiable.rs @@ -0,0 +1,51 @@ +use std::collections::{HashMap, HashSet}; + +pub trait Emptiable{ + fn empty() -> Self; + fn is_empty(&self) -> bool; +} + +impl Emptiable for Vec { + fn empty() -> Self { + Self::new() + } + fn is_empty(&self) -> bool { + self.is_empty() + } +} + +impl Emptiable for Option { + 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 Emptiable for HashMap { + fn empty() -> Self { + HashMap::new() + } + fn is_empty(&self) -> bool { + self.is_empty() + } +} + +impl Emptiable for HashSet { + fn empty() -> Self { + HashSet::new() + } + fn is_empty(&self) -> bool { + self.is_empty() + } +} \ No newline at end of file diff --git a/lazy-supplements-core/src/utils/mod.rs b/lazy-supplements-core/src/utils/mod.rs new file mode 100644 index 0000000..143105f --- /dev/null +++ b/lazy-supplements-core/src/utils/mod.rs @@ -0,0 +1,3 @@ +pub mod async_convert; +pub mod emptiable; +pub mod runnable; diff --git a/lazy-supplements-core/src/utils/runnable.rs b/lazy-supplements-core/src/utils/runnable.rs new file mode 100644 index 0000000..34204f2 --- /dev/null +++ b/lazy-supplements-core/src/utils/runnable.rs @@ -0,0 +1,3 @@ +pub trait Runnable { + async fn run(self); +} \ No newline at end of file diff --git a/lazy-supplements-macros/Cargo.toml b/lazy-supplements-macros/Cargo.toml index 5643f97..41475c9 100644 --- a/lazy-supplements-macros/Cargo.toml +++ b/lazy-supplements-macros/Cargo.toml @@ -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 \ No newline at end of file diff --git a/lazy-supplements-macros/src/derive.rs b/lazy-supplements-macros/src/derive.rs new file mode 100644 index 0000000..8746baf --- /dev/null +++ b/lazy-supplements-macros/src/derive.rs @@ -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(mut source: Vec, 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 = 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() +} diff --git a/lazy-supplements-macros/src/lib.rs b/lazy-supplements-macros/src/lib.rs index cfc1afa..dc35f40 100644 --- a/lazy-supplements-macros/src/lib.rs +++ b/lazy-supplements-macros/src/lib.rs @@ -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.") + + } +} \ No newline at end of file diff --git a/lazy-supplements-macros/tests/derive_emptiable.rs b/lazy-supplements-macros/tests/derive_emptiable.rs new file mode 100644 index 0000000..c3ac639 --- /dev/null +++ b/lazy-supplements-macros/tests/derive_emptiable.rs @@ -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, + text: String, + map: HashMap, + set: HashSet, + opt: Option, +} + +#[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, + }) +} diff --git a/lazy-supplements-macros/tests/derive_runnable.rs b/lazy-supplements-macros/tests/derive_runnable.rs new file mode 100644 index 0000000..77bb119 --- /dev/null +++ b/lazy-supplements-macros/tests/derive_runnable.rs @@ -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; +} + + +