diff --git a/core/src/config/mod.rs b/core/src/config/mod.rs index fd084a2..72491b8 100644 --- a/core/src/config/mod.rs +++ b/core/src/config/mod.rs @@ -23,6 +23,24 @@ pub struct Config { pub rpc: RpcConfig, } +impl AsRef for Config { + fn as_ref(&self) -> &StorageConfig { + &self.storage + } +} + +impl AsRef for Config { + fn as_ref(&self) -> &P2pConfig { + &self.p2p + } +} + +impl AsRef for Config { + fn as_ref(&self) -> &RpcConfig { + &self.rpc + } +} + #[cfg_attr(feature="desktop", derive(Args))] #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] pub struct PartialConfig { @@ -96,16 +114,3 @@ impl Emptiable for PartialConfig { self.p2p.is_empty() && self.rpc.is_empty() && self.storage.is_empty() } } - - -#[cfg(test)] -mod tests { - use libp2p::identity; - use super::*; - use crate::{tests::test_toml_serialize_deserialize}; - - #[tokio::test] - async fn test_p2p_config_serialize_deserialize() { - test_toml_serialize_deserialize(PartialConfig::empty()); - } -} \ No newline at end of file diff --git a/core/src/config/p2p.rs b/core/src/config/p2p.rs index ac9ee05..59b9a1a 100644 --- a/core/src/config/p2p.rs +++ b/core/src/config/p2p.rs @@ -151,7 +151,6 @@ impl Mergeable for PartialP2pConfig { mod tests { use libp2p::identity; use super::*; - use crate::{tests::test_toml_serialize_deserialize}; #[tokio::test] @@ -161,9 +160,5 @@ mod tests { assert_eq!(keypair.public(), keypair2.public()); } - #[tokio::test] - async fn test_p2p_config_serialize_deserialize() { - test_toml_serialize_deserialize(PartialP2pConfig::empty()); - test_toml_serialize_deserialize(PartialP2pConfig::default()); - } + } diff --git a/core/src/config/storage.rs b/core/src/config/storage.rs index 7e2f9a1..974bc8d 100644 --- a/core/src/config/storage.rs +++ b/core/src/config/storage.rs @@ -12,9 +12,6 @@ use serde::{Deserialize, Serialize}; static DATA_DATABASE_NAME: &str = "data.sqlite"; static CACHE_DATABASE_NAME: &str = "cache.sqlite"; -#[cfg(any(test, feature="test"))] -use crate::tests::{GlobalTestDefault, TestDefault}; - #[derive(Clone, Debug)] pub struct StorageConfig { pub data_directory: PathBuf, @@ -30,14 +27,6 @@ impl StorageConfig { } } -#[cfg(any(test, feature="test"))] -impl TestDefault for StorageConfig { - fn test_default() -> Self { - - let temp_dir = tempdir().unwrap().keep(); - Self { data_directory: temp_dir.clone(), cache_directory: temp_dir } - } -} impl TryFrom for StorageConfig { type Error = ConfigError; diff --git a/core/src/data/value/peer_id.rs b/core/src/data/value/peer_id.rs index b6558ca..ff106f3 100644 --- a/core/src/data/value/peer_id.rs +++ b/core/src/data/value/peer_id.rs @@ -99,22 +99,3 @@ impl sea_orm::sea_query::Nullable for PeerIdValue { ::null() } } - -#[cfg(test)] -mod tests { - use crate::tests::{test_cbor_serialize_deserialize, test_toml_serialize_deserialize}; - - use super::*; - - #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] - struct PeerIdValueWrapper { - content: PeerIdValue - - } - #[test] - fn test_serialize_deserialize() { - let peer_id= PeerIdValueWrapper{content: PeerIdValue::from(PeerId::random())}; - let x = toml::to_string(&peer_id).unwrap(); - assert_eq!(peer_id.content, toml::from_str::(&x).unwrap().content) - } -} diff --git a/core/src/error.rs b/core/src/error.rs index 2fded2a..fc5047f 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -1,3 +1,5 @@ +use std::ffi::OsString; + #[derive(thiserror::Error, Debug)] pub enum Error { #[error("Base64 decode error: {0}")] @@ -22,6 +24,8 @@ pub enum Error { Multiaddr(#[from] libp2p::multiaddr::Error), #[error("Noise error: {0}")] Noise(#[from] libp2p::noise::Error), + #[error("Parse OsString error: {0:?}")] + OsStringConvert(std::ffi::OsString), #[cfg(feature="desktop")] #[error("Parse args error: {0}")] ParseCommand(#[from] clap::Error), @@ -31,4 +35,10 @@ pub enum Error { TomlSer(#[from] toml::ser::Error), #[error("Transport error: {0}")] Transport(#[from]libp2p::TransportError) +} + +impl From for Error { + fn from(s: OsString) -> Error { + Self::OsStringConvert(s) + } } \ No newline at end of file diff --git a/core/src/global/config.rs b/core/src/global/config.rs index 836f61e..455908d 100644 --- a/core/src/global/config.rs +++ b/core/src/global/config.rs @@ -1,7 +1,8 @@ +#[cfg(any(test,feature="test"))] use tempfile::TempDir; use tokio::sync::OnceCell; -use crate::{config::{Config, PartialP2pConfig, PartialRpcConfig, PartialStorageConfig, StorageConfig}, error::Error, global::GlobalConstant}; +use crate::{config::{Config, ConfigError, PartialP2pConfig, PartialRpcConfig, PartialStorageConfig, StorageConfig}, error::Error}; pub static CONFIG: GlobalConfig = GlobalConfig::const_new(); pub struct GlobalConfig { @@ -14,33 +15,21 @@ impl GlobalConfig { inner: OnceCell::const_new() } } - pub async fn get_or_init(&'static self, source: Config) -> &'static Config { + pub async fn get_or_init(&'static self, config: T) -> &'static Config where + T: AsRef{ self.inner.get_or_init(|| async { - source + config.as_ref().clone() }).await } + pub async fn get_or_try_init(&'static self, config: T) -> Result<&'static Config, >::Error> where + T: TryInto, + { + self.inner.get_or_try_init(|| async { + config.try_into() + }).await + + } pub fn get(&'static self) -> Option<&'static Config> { self.inner.get() } - pub fn get_and_unwrap(&'static self) -> &'static Config { - self.get().expect(&format!("Config is uninitialized!")) - } - #[cfg(any(test, feature=test))] - pub async fn get_or_init_test(&'static self) -> &'static Config { - let temp_dir = TempDir::new().unwrap().keep(); - let mut data_dir = temp_dir.clone(); - data_dir.push("data"); - let mut cache_dir = temp_dir; - cache_dir.push("cache"); - - - self.get_or_init(Config { - p2p: PartialP2pConfig::default().with_new_secret().try_into().unwrap(), - storage: StorageConfig { - data_directory: data_dir, - cache_directory: cache_dir, - }, - rpc: PartialRpcConfig::default().try_into().unwrap(), - }).await - } } \ No newline at end of file diff --git a/core/src/global/database_connection.rs b/core/src/global/database_connection.rs index 72d24f3..f2c6f4d 100644 --- a/core/src/global/database_connection.rs +++ b/core/src/global/database_connection.rs @@ -1,39 +1,67 @@ -use std::path::Path; +use std::path::{Path, PathBuf}; use sea_orm::{ConnectOptions, Database, DbErr, DatabaseConnection}; use sea_orm_migration::MigratorTrait; -use crate::error::Error; +use crate::{config::StorageConfig, error::Error}; use tokio::sync::OnceCell; -pub static DATA_DATABASE_CONNECTION: GlobalDatabaseConnection = GlobalDatabaseConnection::const_new(stringify!(DATA_DATABASE_CONNECTION)); -pub static CACHE_DATABASE_CONNECTION: GlobalDatabaseConnection = GlobalDatabaseConnection::const_new(stringify!(CACHE_DATABASE_CONNECTION)); +enum StorageType { + Data, + Cache, +} + +impl std::fmt::Display for StorageType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f,"{}", match self { + StorageType::Data => "data", + StorageType::Cache => "cache", + }); + Ok(()) + } +} + + +pub static DATA_DATABASE_CONNECTION: GlobalDatabaseConnection = GlobalDatabaseConnection::const_new(StorageType::Data); +pub static CACHE_DATABASE_CONNECTION: GlobalDatabaseConnection = GlobalDatabaseConnection::const_new(StorageType::Cache); pub struct GlobalDatabaseConnection { - name: &'static str, + storage: StorageType, inner: OnceCell } impl GlobalDatabaseConnection { - pub const fn const_new(name: &'static str) -> Self { + pub const fn const_new(storage: StorageType) -> Self { Self { - name: name, + storage: storage, inner: OnceCell::const_new() } } - pub fn get(&'static self) -> &'static DatabaseConnection { - self.inner.get().expect(&format!("{} is uninitialized!", self.name)) + pub fn get(&'static self) -> Option<&'static DatabaseConnection> { + self.inner.get() } - pub async fn get_or_init(&'static self, path: T, _: U) -> &'static DatabaseConnection + fn get_file_path(&self, config: T) -> PathBuf + where + T: AsRef + { + match self.storage { + StorageType::Cache => config.as_ref().cache_directory.join("cache.db"), + StorageType::Data => config.as_ref().data_directory.join("data.db"), + } + } + pub fn get_unchecked(&'static self) -> &'static DatabaseConnection { + self.get().expect("Global database connection should initialized beforehand!") + } + pub async fn get_or_try_init(&'static self, config: T, _: U) -> Result<&'static DatabaseConnection, Error> where - T: AsRef, + T: AsRef, U: MigratorTrait { - let url = "sqlite://".to_string() + path.as_ref().to_str().unwrap() + "?mode=rwc"; - self.inner.get_or_try_init(|| async { + let url = "sqlite://".to_string() + &self.get_file_path(config).into_os_string().into_string()? + "?mode=rwc"; + Ok(self.inner.get_or_try_init(|| async { let db = Database::connect(&url).await?; U::up(&db, None).await?; Ok::(db) - }).await.expect(&format!("Fail to initialize {}!", self.name)) + }).await?) } } @@ -44,12 +72,12 @@ pub use tests::*; #[cfg(test)] mod tests { use super::*; - use crate::{cache::migration::CacheMigrator, data::migration::DataMigrator, global::CONFIG, tests::GlobalTestDefault}; + use crate::{cache::migration::CacheMigrator, data::migration::DataMigrator, global::CONFIG, tests::{TEST_CONFIG}}; pub async fn get_or_init_test_data_database() -> &'static DatabaseConnection{ - DATA_DATABASE_CONNECTION.get_or_init(CONFIG.get_or_init_test_default().await.storage.get_cache_database_path(), DataMigrator).await + DATA_DATABASE_CONNECTION.get_or_try_init(&*TEST_CONFIG, DataMigrator).await.unwrap() } pub async fn get_or_init_test_cache_database() -> &'static DatabaseConnection{ - CACHE_DATABASE_CONNECTION.get_or_init(CONFIG.get_or_init_test_default()await.storage.get_cache_database_path(), CacheMigrator).await + CACHE_DATABASE_CONNECTION.get_or_try_init(&*TEST_CONFIG, CacheMigrator).await.unwrap() } } \ No newline at end of file diff --git a/core/src/global/mod.rs b/core/src/global/mod.rs index 750f1cb..0c9727e 100644 --- a/core/src/global/mod.rs +++ b/core/src/global/mod.rs @@ -1,9 +1,6 @@ use std::{any::type_name, collections::HashMap, net::{IpAddr, Ipv4Addr}, path::{Path, PathBuf}, sync::LazyLock}; use crate::{config::{P2pConfig, PartialP2pConfig, StorageConfig}, error::Error }; -#[cfg(any(test, feature="test"))] -use crate::tests::{GlobalTestDefault, TestDefault}; -use futures::StreamExt; use libp2p::{swarm::SwarmEvent, Multiaddr, PeerId}; use sea_orm::{prelude::*, Database}; use sea_orm_migration::MigratorTrait; @@ -19,83 +16,9 @@ pub fn generate_uuid() -> Uuid { Uuid::new_v7(Timestamp::now(ContextV7::new())) } -pub static PRODUCT_NAME: LazyLock = LazyLock::new(|| { - env!("CARGO_PKG_NAME").to_string() -}); - pub static DEFAULT_LISTEN_IPS: &[IpAddr] = &[IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))]; -pub static DEFAULT_CONFIG_FILE_NAME: LazyLock = LazyLock::new(|| { - PathBuf::from(String::new() + env!("CARGO_PKG_NAME") + ".toml") -}); - -pub static DEFAULT_DATABASE_FILE_NAME: LazyLock = LazyLock::new(|| { - PathBuf::from(String::new() + env!("CARGO_PKG_NAME") + ".sqlite") -}); fn uninitialized_message(var: T) -> String { format!("{} is uninitialized!", &stringify!(var)) } - -pub struct GlobalConstant { - pub name: &'static str, - inner: OnceCell -} - -impl GlobalConstant { - pub const fn const_new(name: &'static str ) -> Self { - Self{ - name: name, - inner: OnceCell::const_new() - } - } - pub async fn get_or_init(&'static self, source: T) -> &'static T { - self.inner.get_or_init(|| async { - source - }).await - } - pub fn get(&'static self) -> Option<&'static T> { - self.inner.get() - } - pub fn get_and_unwrap(&'static self) -> &'static T { - self.get().expect(&format!("{} is uninitialized!", self.name)) - } -} - -#[cfg(any(test, feature="test"))] -impl GlobalTestDefault for GlobalConstant -where - T: TestDefault + 'static -{ - async fn get_or_init_test_default(&'static self) -> &'static T { - self.get_or_init(T::test_default()).await - } -} - -struct GlobalRwLock { - pub name: &'static str, - inner: OnceCell> -} - -impl GlobalRwLock { - pub const fn const_new(name: &'static str) -> Self { - Self{ - name: name, - inner: OnceCell::const_new() - } - } - pub fn get(&'static self) -> &'static RwLock { - self.inner.get().expect(&format!("{} is uninitialized", self.name)) - } - pub async fn write(&'static self) -> RwLockWriteGuard<'_ ,T> { - self.get().write().await - } - pub async fn read(&'static self) -> RwLockReadGuard<'_, T> { - self.get().read().await - } -} - -#[cfg(test)] -mod tests { - -} diff --git a/core/src/lib.rs b/core/src/lib.rs index eec9477..65b33ee 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -11,3 +11,4 @@ pub mod rpc; #[cfg(any(test, feature="test"))] pub mod tests; pub mod utils; +pub mod server; diff --git a/core/src/p2p/mod.rs b/core/src/p2p/mod.rs index eaa5001..6512bfe 100644 --- a/core/src/p2p/mod.rs +++ b/core/src/p2p/mod.rs @@ -69,34 +69,34 @@ impl From for Event { async fn try_get_or_insert_cached_peer(peer_id: &PeerId, peer_addr: &Multiaddr) -> Result<(CachedPeerModel, CachedAddressModel), Error> { match ( - CachedPeerEntity::find().filter(CachedPeerColumn::PeerId.eq(PeerIdValue::from(peer_id.clone()))).one(CACHE_DATABASE_CONNECTION.get()).await?, - CachedAddressEntity::find().filter(CachedAddressColumn::Multiaddress.eq(MultiaddrValue::from(peer_addr.clone()))).one(CACHE_DATABASE_CONNECTION.get()).await?, + CachedPeerEntity::find().filter(CachedPeerColumn::PeerId.eq(PeerIdValue::from(peer_id.clone()))).one(CACHE_DATABASE_CONNECTION.get_unchecked()).await?, + CachedAddressEntity::find().filter(CachedAddressColumn::Multiaddress.eq(MultiaddrValue::from(peer_addr.clone()))).one(CACHE_DATABASE_CONNECTION.get_unchecked()).await?, ) { (Some(x), Some(y) ) => { if x.id == y.cached_peer_id { event!(Level::TRACE, "Known peer: {}, {}", peer_id, peer_addr); let mut addr: CachedAddressActiveModel = y.into(); addr.updated_at = Set(Local::now().to_utc()); - let updated = addr.update(CACHE_DATABASE_CONNECTION.get()).await?; + let updated = addr.update(CACHE_DATABASE_CONNECTION.get_unchecked()).await?; Ok((x, updated)) } else { - y.delete(CACHE_DATABASE_CONNECTION.get()).await?; - Ok((x.clone(), CachedAddressActiveModel::new(x.id, peer_addr.clone()).insert(CACHE_DATABASE_CONNECTION.get()).await?)) + y.delete(CACHE_DATABASE_CONNECTION.get().expect("Cache database should initialized beforehand!")).await?; + Ok((x.clone(), CachedAddressActiveModel::new(x.id, peer_addr.clone()).insert(CACHE_DATABASE_CONNECTION.get_unchecked()).await?)) } } (Some(x), None) => { event!(Level::INFO, "New address {} for {}", peer_addr, peer_id); - Ok((x.clone(),CachedAddressActiveModel::new(x.id, peer_addr.clone()).insert(CACHE_DATABASE_CONNECTION.get()).await?)) + Ok((x.clone(),CachedAddressActiveModel::new(x.id, peer_addr.clone()).insert(CACHE_DATABASE_CONNECTION.get_unchecked()).await?)) }, (None, x) => { event!(Level::INFO, "Add new peer: {}", peer_id); - let inserted = CachedPeerActiveModel::new(peer_id.clone()).insert(CACHE_DATABASE_CONNECTION.get()).await?; + let inserted = CachedPeerActiveModel::new(peer_id.clone()).insert(CACHE_DATABASE_CONNECTION.get_unchecked()).await?; if let Some(y) = x { event!(Level::INFO, "Remove {} from {}", peer_addr, peer_id); - y.delete(CACHE_DATABASE_CONNECTION.get()).await?; + y.delete(CACHE_DATABASE_CONNECTION.get_unchecked()).await?; }; event!(Level::INFO, "Add address {} to {}", peer_addr, peer_id); - Ok((inserted.clone(), CachedAddressActiveModel::new(inserted.id, peer_addr.clone()).insert(CACHE_DATABASE_CONNECTION.get()).await?)) + Ok((inserted.clone(), CachedAddressActiveModel::new(inserted.id, peer_addr.clone()).insert(CACHE_DATABASE_CONNECTION.get_unchecked()).await?)) }, diff --git a/core/src/rpc/service/cached_peer.rs b/core/src/rpc/service/cached_peer.rs index 4f62191..cc5adc0 100644 --- a/core/src/rpc/service/cached_peer.rs +++ b/core/src/rpc/service/cached_peer.rs @@ -16,9 +16,9 @@ impl crate::proto::cached_peer_service_server::CachedPeerService for CachedPeerS println!("Got a request: {:?}", request); let reply = CachedPeerListResponse { - peers: join_all( CachedPeerEntity::find().all(CACHE_DATABASE_CONNECTION.get()).await.or_else(|e| Err(Status::from_error(Box::new(e))))?.iter().map(|x| async move { + peers: join_all( CachedPeerEntity::find().all(CACHE_DATABASE_CONNECTION.get_unchecked()).await.or_else(|e| Err(Status::from_error(Box::new(e))))?.iter().map(|x| async move { let addresses = CachedAddressEntity::find() - .all(CACHE_DATABASE_CONNECTION.get()) + .all(CACHE_DATABASE_CONNECTION.get_unchecked()) .await .or_else(|e| Err(Status::from_error(Box::new(e))))?; Ok::(CachedPeerMessage::from((x, &addresses))) diff --git a/core/src/server.rs b/core/src/server.rs new file mode 100644 index 0000000..6dc9ca4 --- /dev/null +++ b/core/src/server.rs @@ -0,0 +1,13 @@ +use crate::{config::{Config, P2pConfig, RpcConfig}, error::Error}; + +pub trait ServerTrait { + async fn serve_p2p(config: &P2pConfig) -> Result<(), Error>; + async fn serve_rpc(config: &RpcConfig) -> Result<(), Error>; + async fn serve_all(config: &Config) -> Result<(), Error> { + tokio::try_join!( + Self::serve_p2p(&config.p2p), + Self::serve_rpc(&config.rpc) + )?; + Ok(()) + } +} \ No newline at end of file diff --git a/core/src/tests.rs b/core/src/tests.rs index b625771..29e0624 100644 --- a/core/src/tests.rs +++ b/core/src/tests.rs @@ -3,29 +3,22 @@ use std::{path::PathBuf, sync::LazyLock}; use sea_orm::{sea_query::{FromValueTuple, IntoValueTuple, ValueType}, ActiveModelBehavior, ActiveModelTrait, ColumnTrait, Condition, DatabaseConnection, EntityTrait, IntoActiveModel, ModelTrait, PrimaryKeyToColumn, PrimaryKeyTrait, Value}; use sea_orm::QueryFilter; use tempfile::TempDir; -use crate::{ config::PartialConfig, message::Message}; +use crate::{ config::{Config, PartialConfig, PartialP2pConfig, PartialRpcConfig, StorageConfig}, message::Message}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; +pub static TEST_CONFIG: LazyLock = LazyLock::new(|| { + let test_dir = TempDir::new().unwrap().keep(); + let data_dir = test_dir.join("data"); + let cache_dir = test_dir.join("cache"); -pub static TEST_DIR_PATH: LazyLock = LazyLock::new(|| { - let pkg_name = env!("CARGO_PKG_NAME"); - let timestamp = chrono::Local::now().to_rfc3339_opts(chrono::SecondsFormat::Nanos, false); - std::env::temp_dir().join(pkg_name).join( ×tamp) + + Config { + p2p: PartialP2pConfig::default().with_new_secret().try_into().unwrap(), + storage: StorageConfig { + data_directory: data_dir, + cache_directory: cache_dir, + }, + rpc: PartialRpcConfig::default().try_into().unwrap(), + } }); - -pub static TEST_DIR: LazyLock = LazyLock::new(|| { - TempDir::new().unwrap().keep() -}); - -pub static TEST_DATABASE_PATH: std::sync::LazyLock = std::sync::LazyLock::new(|| { - TEST_DIR_PATH.join("lazy-supplements.sqlite") -}); - -pub trait TestDefault { - fn test_default() -> Self; -} - -pub trait GlobalTestDefault { - async fn get_or_init_test_default(&'static self) -> &'static T; -} diff --git a/desktop/src/cli/config/check.rs b/desktop/src/cli/config/check.rs new file mode 100644 index 0000000..005c4af --- /dev/null +++ b/desktop/src/cli/config/check.rs @@ -0,0 +1,15 @@ +use clap::Args; +use crate::utils::runnable::Runnable; +use crate::cli::ConfigArgs; + +#[derive(Debug, Args)] +pub struct ConfigCheckCommandArgs{ + #[command(flatten)] + config: ConfigArgs +} + +impl Runnable for ConfigCheckCommandArgs { + async fn run(self) { + todo!() + } +} \ No newline at end of file diff --git a/desktop/src/cli/config/list.rs b/desktop/src/cli/config/list.rs new file mode 100644 index 0000000..003d09d --- /dev/null +++ b/desktop/src/cli/config/list.rs @@ -0,0 +1,17 @@ +use clap::Args; +use crate::utils::runnable::Runnable; +use crate::cli::ConfigArgs; + +#[derive(Debug, Args)] +pub struct ConfigListCommandArgs{ + #[command(flatten)] + config: ConfigArgs, + #[arg(short,long)] + all: bool +} + +impl Runnable for ConfigListCommandArgs { + async fn run(self) { + todo!() + } +} \ No newline at end of file diff --git a/desktop/src/cli/config/mod.rs b/desktop/src/cli/config/mod.rs new file mode 100644 index 0000000..b8c0408 --- /dev/null +++ b/desktop/src/cli/config/mod.rs @@ -0,0 +1,38 @@ +mod check; +mod list; + +pub use check::*; +pub use list::*; + +use caretta_core::utils::runnable::Runnable; +use clap::{Args, Subcommand}; + + +#[derive(Debug, Args)] +pub struct ConfigCommandArgs { + #[command(subcommand)] + pub command: ConfigSubcommand +} + +impl Runnable for ConfigCommandArgs { + async fn run(self) { + self.command.run().await + } +} + +#[derive(Debug, Subcommand)] +pub enum ConfigSubcommand { + Check(ConfigCheckCommandArgs), + List(ConfigListCommandArgs), +} + +impl Runnable for ConfigSubcommand { + async fn run(self) { + match self { + Self::Check(x) => x.run().await, + Self::List(x) => x.run().await, + } + } +} + + diff --git a/desktop/src/cli/device/list.rs b/desktop/src/cli/device/list.rs index 1e9b575..732cd98 100644 --- a/desktop/src/cli/device/list.rs +++ b/desktop/src/cli/device/list.rs @@ -1,6 +1,6 @@ use clap::Args; use crate::utils::runnable::Runnable; -use crate::cli::{ConfigArgs, RunnableCommand}; +use crate::cli::ConfigArgs; #[derive(Debug, Args)] pub struct DeviceListCommandArgs{ diff --git a/desktop/src/cli/device/remove.rs b/desktop/src/cli/device/remove.rs index f0b84bd..919d25d 100644 --- a/desktop/src/cli/device/remove.rs +++ b/desktop/src/cli/device/remove.rs @@ -1,6 +1,6 @@ use clap::Args; use crate::utils::runnable::Runnable; -use crate::cli::{ConfigArgs, DeviceArgs, RunnableCommand}; +use crate::cli::{ConfigArgs, DeviceArgs}; #[derive(Debug, Args)] pub struct DeviceRemoveCommandArgs{ diff --git a/desktop/src/cli/device/scan.rs b/desktop/src/cli/device/scan.rs index 08683b6..77f0992 100644 --- a/desktop/src/cli/device/scan.rs +++ b/desktop/src/cli/device/scan.rs @@ -1,6 +1,6 @@ use clap::Args; use crate::utils::runnable::Runnable; -use crate::cli::{ConfigArgs, RunnableCommand}; +use crate::cli::ConfigArgs; #[derive(Debug, Args)] pub struct DeviceScanCommandArgs{ diff --git a/desktop/src/cli/logs.rs b/desktop/src/cli/logs.rs index d4d83ea..bc7d8a0 100644 --- a/desktop/src/cli/logs.rs +++ b/desktop/src/cli/logs.rs @@ -1,4 +1,14 @@ +use caretta_core::utils::runnable::Runnable; +use clap::Args; + #[derive(Args, Debug)] pub struct LogsCommandArgs { - + #[arg(short='n', long)] + lines: Option, +} + +impl Runnable for LogsCommandArgs { + async fn run(self) { + todo!() + } } \ No newline at end of file diff --git a/desktop/src/cli/mod.rs b/desktop/src/cli/mod.rs index c6d651d..472b7de 100644 --- a/desktop/src/cli/mod.rs +++ b/desktop/src/cli/mod.rs @@ -1,13 +1,13 @@ use std::path::PathBuf; mod args; +mod config; mod device; +mod logs; mod peer; pub use args::*; +pub use config::*; pub use device::*; -pub use peer::*; - -pub trait RunnableCommand { - async fn run(self); -} \ No newline at end of file +pub use logs::*; +pub use peer::*; \ No newline at end of file diff --git a/desktop/src/cli/peer/info.rs b/desktop/src/cli/peer/info.rs new file mode 100644 index 0000000..115b4c2 --- /dev/null +++ b/desktop/src/cli/peer/info.rs @@ -0,0 +1,17 @@ +use clap::Args; +use crate::utils::runnable::Runnable; +use crate::cli::{ConfigArgs, PeerArgs}; + +#[derive(Debug, Args)] +pub struct PeerInfoCommandArgs{ + #[command(flatten)] + config: ConfigArgs, + #[command(flatten)] + peer: PeerArgs, +} + +impl Runnable for PeerInfoCommandArgs { + async fn run(self) { + todo!() + } +} \ No newline at end of file diff --git a/desktop/src/cli/peer/list.rs b/desktop/src/cli/peer/list.rs index 1e9b575..d88be96 100644 --- a/desktop/src/cli/peer/list.rs +++ b/desktop/src/cli/peer/list.rs @@ -1,14 +1,14 @@ use clap::Args; use crate::utils::runnable::Runnable; -use crate::cli::{ConfigArgs, RunnableCommand}; +use crate::cli::ConfigArgs; #[derive(Debug, Args)] -pub struct DeviceListCommandArgs{ +pub struct PeerListCommandArgs{ #[command(flatten)] config: ConfigArgs } -impl Runnable for DeviceListCommandArgs { +impl Runnable for PeerListCommandArgs { async fn run(self) { todo!() } diff --git a/desktop/src/cli/peer/mod.rs b/desktop/src/cli/peer/mod.rs index e69de29..970048e 100644 --- a/desktop/src/cli/peer/mod.rs +++ b/desktop/src/cli/peer/mod.rs @@ -0,0 +1,42 @@ +mod info; +mod list; +mod ping; + +pub use info::*; +pub use list::*; +pub use ping::*; + +use caretta_core::utils::runnable::Runnable; +use clap::{Args, Subcommand}; + + +#[derive(Debug, Args)] +pub struct PeerCommandArgs { + #[command(subcommand)] + pub command: PeerSubcommand +} + +impl Runnable for PeerCommandArgs { + async fn run(self) { + self.command.run().await + } +} + +#[derive(Debug, Subcommand)] +pub enum PeerSubcommand { + Info(PeerInfoCommandArgs), + List(PeerListCommandArgs), + Ping(PeerPingCommandArgs), +} + +impl Runnable for PeerSubcommand { + async fn run(self) { + match self { + Self::Info(x) => x.run().await, + Self::List(x) => x.run().await, + Self::Ping(x) => x.run().await, + } + } +} + + diff --git a/desktop/src/cli/peer/ping.rs b/desktop/src/cli/peer/ping.rs new file mode 100644 index 0000000..f1214f7 --- /dev/null +++ b/desktop/src/cli/peer/ping.rs @@ -0,0 +1,17 @@ +use clap::Args; +use crate::utils::runnable::Runnable; +use crate::cli::{ConfigArgs, PeerArgs}; + +#[derive(Debug, Args)] +pub struct PeerPingCommandArgs{ + #[command(flatten)] + config: ConfigArgs, + #[command(flatten)] + peer: PeerArgs, +} + +impl Runnable for PeerPingCommandArgs { + async fn run(self) { + todo!() + } +} \ No newline at end of file diff --git a/examples/core/src/lib.rs b/examples/core/src/lib.rs index de6b564..6107810 100644 --- a/examples/core/src/lib.rs +++ b/examples/core/src/lib.rs @@ -1,2 +1,2 @@ pub mod rpc; - +pub mod server; \ No newline at end of file diff --git a/examples/core/src/server.rs b/examples/core/src/server.rs new file mode 100644 index 0000000..0f2c331 --- /dev/null +++ b/examples/core/src/server.rs @@ -0,0 +1,31 @@ +use caretta::{config::P2pConfig, server::ServerTrait}; + +pub struct Server{}; + +impl ServerTrait for Server { + async fn serve_p2p(config: P2pConfig) -> Result<(), caretta::error::Error> { + let mut swarm = libp2p::SwarmBuilder::with_existing_identity(self.secret) + .with_tokio() + .with_tcp( + tcp::Config::default(), + noise::Config::new, + yamux::Config::default, + )? + .with_behaviour(|keypair| p2p::Behaviour::try_from(keypair).unwrap())? + .build(); + swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse()?)?; + loop{ + let swarm_event = swarm.select_next_some().await; + tokio::spawn(async move{ + match swarm_event { + SwarmEvent::NewListenAddr { address, .. } => println!("Listening on {address:?}"), + SwarmEvent::Behaviour(event) => { + println!("{event:?}"); + event.run().await; + }, + _ => {} + } + }); + } + } +} \ No newline at end of file diff --git a/examples/desktop/src/cli/mod.rs b/examples/desktop/src/cli/mod.rs index 5484284..1006c13 100644 --- a/examples/desktop/src/cli/mod.rs +++ b/examples/desktop/src/cli/mod.rs @@ -12,8 +12,9 @@ pub struct Cli { #[derive(Debug, Subcommand)] pub enum CliCommand { - //Config(ConfigCommandArgs), - //Device(DeviceCommandArgs), - //Log(LogCommandArgs), + Config(ConfigCommandArgs), + Device(DeviceCommandArgs), + Logs(LogsCommandArgs), + Peer(PeerSubcommand), Server(ServerCommandArgs), } \ No newline at end of file diff --git a/examples/desktop/src/cli/server.rs b/examples/desktop/src/cli/server.rs index beb2cfa..1c665d9 100644 --- a/examples/desktop/src/cli/server.rs +++ b/examples/desktop/src/cli/server.rs @@ -1,5 +1,5 @@ use clap::Args; -use caretta::{error::Error, utils::runnable::Runnable}; +use caretta::{error::Error, global::CONFIG, utils::runnable::Runnable}; use libp2p::{noise, ping, swarm::{NetworkBehaviour, SwarmEvent}, tcp, yamux, Swarm}; use super::ConfigArgs; @@ -11,9 +11,6 @@ pub struct ServerCommandArgs { } impl Runnable for ServerCommandArgs { async fn run(self) { - let swarm_handler = P2P_CONFIG.get_and_unwrap().clone().launch_swarm(); - let server_handler = caretta_example_core::rpc::server::start_server(); - - let (swarm_result, server_result) = tokio::try_join!(swarm_handler, server_handler).unwrap(); + let config = CONFIG.get_or_init(self.config.try_into()?).await; } } \ No newline at end of file