diff --git a/lazy-supplements-core/src/config/mod.rs b/lazy-supplements-core/src/config/mod.rs index 7e2800f..62e1c4d 100644 --- a/lazy-supplements-core/src/config/mod.rs +++ b/lazy-supplements-core/src/config/mod.rs @@ -3,31 +3,30 @@ mod storage; mod p2p; use std::path::Path; -use crate::error::Error; +use crate::{error::Error, utils::{emptiable::Emptiable, mergeable::Mergeable}}; pub use error::ConfigError; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use tokio::{fs::File, io::{AsyncReadExt, AsyncWriteExt}}; pub use storage::{StorageConfig, PartialStorageConfig}; pub use p2p::{P2pConfig, PartialP2pConfig}; -pub trait PartialConfig: Serialize + Sized + DeserializeOwned -{ - fn default() -> Self; - fn empty() -> Self; - fn merge(&mut self, other: Self); +pub trait Config: TryFrom{ + type PartialConfig: PartialConfig; +} +pub trait PartialConfig: Emptiable + From + Mergeable { + type Config: Config; + +} + +pub trait PartialCoreConfig: DeserializeOwned + Serialize { + fn new() -> Self; fn from_toml(s: &str) -> Result { toml::from_str(s) } fn into_toml(&self) -> Result { toml::to_string(self) } - fn is_empty(&self) -> bool; -} - -pub trait PartialConfigRoot: DeserializeOwned + Serialize { - fn new() -> Self; - async fn read_or_create(path: T) -> Result where T: AsRef @@ -67,7 +66,7 @@ pub trait PartialConfigRoot: DeserializeOwned + Serialize { mod tests { use serde::{Deserialize, Serialize}; - use crate::tests::test_toml_serialize_deserialize; + use crate::{tests::test_toml_serialize_deserialize, utils::{emptiable::Emptiable, mergeable::Mergeable}}; use super::{p2p::{P2pConfig, PartialP2pConfig}, PartialConfig}; @@ -77,13 +76,14 @@ mod tests { p2p: Option } - impl PartialConfig for TestConfig { + impl Default for TestConfig { fn default() -> Self { Self { p2p: Some(PartialP2pConfig::default()), } } - + } + impl Emptiable for TestConfig { fn empty() -> Self { Self { p2p: None, @@ -93,7 +93,8 @@ mod tests { fn is_empty(&self) -> bool { self.p2p.is_none() } - + } + impl Mergeable for TestConfig { fn merge(&mut self, other: Self) { if let Some(p2p) = other.p2p { self.p2p = Some(p2p); diff --git a/lazy-supplements-core/src/config/p2p.rs b/lazy-supplements-core/src/config/p2p.rs index fdf96bf..3fa82dc 100644 --- a/lazy-supplements-core/src/config/p2p.rs +++ b/lazy-supplements-core/src/config/p2p.rs @@ -11,7 +11,7 @@ use tracing_subscriber::EnvFilter; use crate::{ config::PartialConfig, - error::Error, p2p + error::Error, p2p, utils::emptiable::Emptiable }; static DEFAULT_P2P_LISTEN_IPS: &[IpAddr] = &[IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))]; @@ -86,6 +86,7 @@ mod keypair_parser { } #[cfg_attr(feature="desktop",derive(Args))] +#[cfg_attr(feature="macros", derive(Emptiable))] #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] pub struct PartialP2pConfig { #[cfg_attr(feature="desktop",arg(long))] @@ -96,44 +97,10 @@ pub struct PartialP2pConfig { pub port: Option, } impl PartialP2pConfig { - pub fn with_new_secret(mut self) -> Self { self.secret = Some(keypair_to_base64(&Keypair::generate_ed25519())); self } - pub async fn read_or_create(path: T) -> Result - where - T: AsRef - { - if !path.as_ref().exists() { - Self::empty().write_to(&path).await?; - } - Self::read_from(&path).await - } - pub async fn read_from(path:T) -> Result - where - T: AsRef - { - let mut file = File::open(path.as_ref()).await?; - let mut content = String::new(); - file.read_to_string(&mut content).await?; - let config: Self = toml::from_str(&content)?; - Ok(config) - } - pub async fn write_to(&self, path:T) -> Result<(), Error> - where - T: AsRef - { - if !path.as_ref().exists() { - if let Some(x) = path.as_ref().parent() { - std::fs::create_dir_all(x)?; - }; - let _ = File::create(&path).await?; - } - let mut file = File::create(&path).await?; - file.write_all(toml::to_string(self)?.as_bytes()).await?; - Ok(()) - } } impl From for PartialP2pConfig { @@ -146,29 +113,7 @@ impl From for PartialP2pConfig { } } -impl PartialConfig for PartialP2pConfig { - fn empty() -> Self { - Self { - secret: None, - listen_ips: None, - port: None, - } - } - fn is_empty(&self) -> bool { - self.secret.is_none() && self.listen_ips.is_none() && self.port.is_none() - } - fn merge(&mut self, another: Self) { - if let Some(x) = another.secret { - self.secret = Some(x); - }; - if let Some(x) = another.listen_ips { - self.listen_ips = Some(x); - }; - if let Some(x) = another.port { - self.port = Some(x); - }; - } - +impl Default for PartialP2pConfig { fn default() -> Self { Self { secret: None, diff --git a/lazy-supplements-core/src/config/storage.rs b/lazy-supplements-core/src/config/storage.rs index 86ce924..5a0071b 100644 --- a/lazy-supplements-core/src/config/storage.rs +++ b/lazy-supplements-core/src/config/storage.rs @@ -5,7 +5,7 @@ use clap::Args; #[cfg(any(test, feature="test"))] use tempfile::tempdir; -use crate::{config::{ConfigError, PartialConfig}}; +use crate::{config::{ConfigError, PartialConfig}, utils::emptiable::Emptiable}; use libp2p::mdns::Config; use serde::{Deserialize, Serialize}; @@ -50,6 +50,7 @@ impl TryFrom for StorageConfig { } } #[cfg_attr(feature="desktop", derive(Args))] +#[cfg_attr(feature="macros", derive(Emptiable))] #[derive(Clone, Debug, Deserialize, Serialize)] pub struct PartialStorageConfig { #[cfg_attr(feature="desktop", arg(long))] @@ -65,27 +66,4 @@ impl From for PartialStorageConfig { cache_directory: Some(config.cache_directory), } } -} - -impl PartialConfig for PartialStorageConfig { - fn empty() -> Self { - Self{ - data_directory: None, - cache_directory: None, - } - } - fn is_empty(&self) -> bool { - self.data_directory.is_none() && self.cache_directory.is_none() - } - fn default() -> Self { - todo!() - } - fn merge(&mut self, other: Self) { - if let Some(x) = other.data_directory { - self.data_directory = Some(x); - } - if let Some(x) = other.cache_directory { - self.cache_directory = Some(x); - } - } } \ No newline at end of file diff --git a/lazy-supplements-core/src/utils/emptiable.rs b/lazy-supplements-core/src/utils/emptiable.rs index 1535c23..ec5d1c3 100644 --- a/lazy-supplements-core/src/utils/emptiable.rs +++ b/lazy-supplements-core/src/utils/emptiable.rs @@ -1,4 +1,6 @@ use std::collections::{HashMap, HashSet}; +#[cfg(feature="macros")] +pub use lazy_supplements_macros::Emptiable; pub trait Emptiable{ fn empty() -> Self; diff --git a/lazy-supplements-core/src/utils/mergeable.rs b/lazy-supplements-core/src/utils/mergeable.rs new file mode 100644 index 0000000..c7b614c --- /dev/null +++ b/lazy-supplements-core/src/utils/mergeable.rs @@ -0,0 +1,14 @@ +pub trait Mergeable: Sized { + fn merge(&mut self, other: Self); +} + +impl Mergeable for Option { + fn merge(&mut self, mut other: Self) { + match other.take() { + Some(x) => { + let _ = self.insert(x); + }, + None => {} + }; + } +} \ No newline at end of file diff --git a/lazy-supplements-core/src/utils/mod.rs b/lazy-supplements-core/src/utils/mod.rs index 143105f..1233bee 100644 --- a/lazy-supplements-core/src/utils/mod.rs +++ b/lazy-supplements-core/src/utils/mod.rs @@ -1,3 +1,4 @@ pub mod async_convert; pub mod emptiable; +pub mod mergeable; pub mod runnable; diff --git a/lazy-supplements-core/src/utils/runnable.rs b/lazy-supplements-core/src/utils/runnable.rs index 34204f2..45b9ecb 100644 --- a/lazy-supplements-core/src/utils/runnable.rs +++ b/lazy-supplements-core/src/utils/runnable.rs @@ -1,3 +1,6 @@ +#[cfg(feature="macros")] +pub use lazy_supplements_macros::Runnable; + pub trait Runnable { async fn run(self); } \ No newline at end of file diff --git a/lazy-supplements-desktop/src/cli/device/add.rs b/lazy-supplements-desktop/src/cli/device/add.rs index 0314479..b44aa21 100644 --- a/lazy-supplements-desktop/src/cli/device/add.rs +++ b/lazy-supplements-desktop/src/cli/device/add.rs @@ -1,6 +1,7 @@ use clap::Args; +use crate::utils::runnable::Runnable; -use crate::cli::{ConfigArgs, RunnableCommand}; +use crate::cli::ConfigArgs; use crate::cli::PeerArgs; @@ -14,7 +15,7 @@ pub struct DeviceAddCommandArgs { config: ConfigArgs } -impl RunnableCommand for DeviceAddCommandArgs { +impl Runnable for DeviceAddCommandArgs { async fn run(self) { todo!() } diff --git a/lazy-supplements-desktop/src/cli/device/list.rs b/lazy-supplements-desktop/src/cli/device/list.rs index c802d5c..1e9b575 100644 --- a/lazy-supplements-desktop/src/cli/device/list.rs +++ b/lazy-supplements-desktop/src/cli/device/list.rs @@ -1,5 +1,5 @@ use clap::Args; - +use crate::utils::runnable::Runnable; use crate::cli::{ConfigArgs, RunnableCommand}; #[derive(Debug, Args)] @@ -8,7 +8,7 @@ pub struct DeviceListCommandArgs{ config: ConfigArgs } -impl RunnableCommand for DeviceListCommandArgs { +impl Runnable for DeviceListCommandArgs { async fn run(self) { todo!() } diff --git a/lazy-supplements-desktop/src/cli/device/mod.rs b/lazy-supplements-desktop/src/cli/device/mod.rs index 64c2747..8632c0f 100644 --- a/lazy-supplements-desktop/src/cli/device/mod.rs +++ b/lazy-supplements-desktop/src/cli/device/mod.rs @@ -5,6 +5,7 @@ mod remove; mod scan; pub use add::DeviceAddCommandArgs; +use crate::utils::runnable::Runnable; use libp2p::{Multiaddr, PeerId}; pub use list::DeviceListCommandArgs; pub use ping::DevicePingCommandArgs; @@ -20,13 +21,14 @@ use crate::{cli::ServerArgs, error::Error}; use super::ConfigArgs; -#[derive(Debug, Args)] +#[derive(Debug, Args, Runnable)] pub struct DeviceCommandArgs { #[command(subcommand)] + #[runnable] pub command: DeviceSubcommand } -#[derive(Debug, Subcommand)] +#[derive(Debug, Subcommand, Runnable)] pub enum DeviceSubcommand { Add(DeviceAddCommandArgs), List(DeviceListCommandArgs), diff --git a/lazy-supplements-desktop/src/cli/device/ping.rs b/lazy-supplements-desktop/src/cli/device/ping.rs index af5ac67..d7df432 100644 --- a/lazy-supplements-desktop/src/cli/device/ping.rs +++ b/lazy-supplements-desktop/src/cli/device/ping.rs @@ -1,6 +1,6 @@ use clap::Args; - -use crate::cli::{ConfigArgs, PeerArgs, RunnableCommand}; +use crate::utils::runnable::Runnable; +use crate::cli::{ConfigArgs, PeerArgs}; #[derive(Debug, Args)] pub struct DevicePingCommandArgs{ @@ -10,7 +10,7 @@ pub struct DevicePingCommandArgs{ config: ConfigArgs } -impl RunnableCommand for DevicePingCommandArgs { +impl Runnable for DevicePingCommandArgs { async fn run(self) { todo!() } diff --git a/lazy-supplements-desktop/src/cli/device/remove.rs b/lazy-supplements-desktop/src/cli/device/remove.rs index d72db46..f0b84bd 100644 --- a/lazy-supplements-desktop/src/cli/device/remove.rs +++ b/lazy-supplements-desktop/src/cli/device/remove.rs @@ -1,5 +1,5 @@ use clap::Args; - +use crate::utils::runnable::Runnable; use crate::cli::{ConfigArgs, DeviceArgs, RunnableCommand}; #[derive(Debug, Args)] @@ -10,7 +10,7 @@ pub struct DeviceRemoveCommandArgs{ config: ConfigArgs } -impl RunnableCommand for DeviceRemoveCommandArgs { +impl Runnable for DeviceRemoveCommandArgs { async fn run(self) { todo!() } diff --git a/lazy-supplements-desktop/src/cli/device/scan.rs b/lazy-supplements-desktop/src/cli/device/scan.rs index 1277c6e..08683b6 100644 --- a/lazy-supplements-desktop/src/cli/device/scan.rs +++ b/lazy-supplements-desktop/src/cli/device/scan.rs @@ -1,5 +1,5 @@ use clap::Args; - +use crate::utils::runnable::Runnable; use crate::cli::{ConfigArgs, RunnableCommand}; #[derive(Debug, Args)] @@ -8,7 +8,7 @@ pub struct DeviceScanCommandArgs{ config: ConfigArgs } -impl RunnableCommand for DeviceScanCommandArgs { +impl Runnable for DeviceScanCommandArgs { async fn run(self) { todo!() } diff --git a/lazy-supplements-desktop/src/lib.rs b/lazy-supplements-desktop/src/lib.rs index bfa056c..f2c7e13 100644 --- a/lazy-supplements-desktop/src/lib.rs +++ b/lazy-supplements-desktop/src/lib.rs @@ -2,6 +2,7 @@ pub mod cli; pub mod config; pub mod global; pub mod ipc; +pub mod utils; pub use lazy_supplements_core::{ cache, data, diff --git a/lazy-supplements-desktop/src/utils.rs b/lazy-supplements-desktop/src/utils.rs new file mode 100644 index 0000000..2996a43 --- /dev/null +++ b/lazy-supplements-desktop/src/utils.rs @@ -0,0 +1 @@ +pub use lazy_supplements_core::utils::*; \ No newline at end of file diff --git a/lazy-supplements-macros/src/lib.rs b/lazy-supplements-macros/src/lib.rs index dc35f40..4cbebd2 100644 --- a/lazy-supplements-macros/src/lib.rs +++ b/lazy-supplements-macros/src/lib.rs @@ -135,7 +135,6 @@ fn extract_fields(data: &Data) -> &FieldsNamed { } } - #[proc_macro_derive(Emptiable)] pub fn emptiable(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); @@ -166,11 +165,32 @@ pub fn emptiable(input: TokenStream) -> TokenStream { } }.into() } - Data::Enum(ref fields) => { - todo!() + _ => panic!("struct or expected, but got other type.") - }, - _ => panic!("struct or enum expected, but got union.") + } +} + +#[proc_macro_derive(Mergeable)] +pub fn mergeable(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 merge_iter = field_idents.iter().map(|(ident, type_name)| { + quote!{ + <#type_name as Mergeable>::merge(&mut self.#ident, other.#ident); + } + }); + quote!{ + impl Mergeable for #type_ident { + fn merge(&mut self, mut other: Self){ + #(#merge_iter)* + } + } + }.into() + } + _ => panic!("struct expected, but got other type.") } } diff --git a/lazy-supplements-macros/tests/derive_mergeable.rs b/lazy-supplements-macros/tests/derive_mergeable.rs new file mode 100644 index 0000000..4e71ef1 --- /dev/null +++ b/lazy-supplements-macros/tests/derive_mergeable.rs @@ -0,0 +1,32 @@ +use std::collections::{HashMap, HashSet}; + +use lazy_supplements_core::utils::mergeable::Mergeable; +use lazy_supplements_macros::Mergeable; + +#[derive(Clone, Debug, PartialEq, Mergeable)] +struct MergeableStruct { + opt: Option, +} + +#[cfg(test)] +fn test() { + let zero = MergeableStruct{ + opt: Some(0), + }; + let one = MergeableStruct { + opt: Some(1), + }; + let none = MergeableStruct{ + opt: None, + }; + let mut zero_with_one = zero.clone(); + zero_with_one.merge(one.clone()); + let mut none_with_zero = none.clone(); + none_with_zero.merge(zero.clone()); + let mut zero_with_none = zero.clone(); + zero_with_none.merge(none.clone()); + assert_eq!(zero_with_one.clone(), one.clone()); + assert_eq!(none_with_zero, zero.clone()); + assert_eq!(zero_with_none, zero.clone()); + +}