From a3a82958806ade4a560112c0e98673ccf846d04a Mon Sep 17 00:00:00 2001 From: fluo10 Date: Fri, 30 May 2025 09:26:47 +0900 Subject: [PATCH] Add node command and default path --- lazy-supplements/Cargo.toml | 5 ++- lazy-supplements/src/cli/connect.rs | 13 ------ lazy-supplements/src/cli/mod.rs | 2 +- lazy-supplements/src/cli/node.rs | 30 ++++++++++++++ lazy-supplements/src/config/mod.rs | 50 +++++++++++++++++++++++ lazy-supplements/src/config/node.rs | 22 +++++++++++ lazy-supplements/src/error.rs | 6 +++ lazy-supplements/src/global/mod.rs | 61 +++++++++++++++++++++++++++-- lazy-supplements/src/lib.rs | 2 + lazy-supplements/src/tests.rs | 17 ++++++++ 10 files changed, 189 insertions(+), 19 deletions(-) delete mode 100644 lazy-supplements/src/cli/connect.rs create mode 100644 lazy-supplements/src/cli/node.rs create mode 100644 lazy-supplements/src/tests.rs diff --git a/lazy-supplements/Cargo.toml b/lazy-supplements/Cargo.toml index 35fc5fa..4eb0b1f 100644 --- a/lazy-supplements/Cargo.toml +++ b/lazy-supplements/Cargo.toml @@ -15,6 +15,8 @@ base64 = "0.22.1" chrono = "0.4.41" chrono-tz = "0.10.3" clap = { version = "4.5.38", features = ["derive"] } +dirs = "6.0.0" +futures = "0.3.31" libp2p.workspace = true sea-orm = { version = "1.1.11", features = ["sqlx-sqlite", "runtime-tokio-native-tls", "macros", "with-chrono", "with-uuid"] } sea-orm-migration.workspace = true @@ -23,9 +25,10 @@ tempfile = { version = "3.20.0", optional = true } thiserror = "2.0.12" tokio = { version = "1.45.0", features = ["macros", "rt"] } toml = "0.8.22" +tracing-subscriber = { version = "0.3.19", features = ["env-filter"] } uuid = { version = "1.17.0", features = ["v4"] } [dev-dependencies] lazy-supplements-migration.workspace = true sea-orm-migration.workspace = true -tempfile = "3.20.0" \ No newline at end of file +tempfile = "3.20.0" diff --git a/lazy-supplements/src/cli/connect.rs b/lazy-supplements/src/cli/connect.rs deleted file mode 100644 index 0bda4d0..0000000 --- a/lazy-supplements/src/cli/connect.rs +++ /dev/null @@ -1,13 +0,0 @@ -use std::{net::IpAddr, path::PathBuf}; - -use clap::Args; - -#[derive(Args, Debug)] -pub struct ConnectArgs { - #[arg(long)] - endpoint: IpAddr, - #[arg(long)] - port: i32, - #[arg(long)] - config: PathBuf, -} \ No newline at end of file diff --git a/lazy-supplements/src/cli/mod.rs b/lazy-supplements/src/cli/mod.rs index 856c57c..3aaadda 100644 --- a/lazy-supplements/src/cli/mod.rs +++ b/lazy-supplements/src/cli/mod.rs @@ -1,7 +1,7 @@ use std::path::PathBuf; -mod connect; mod init; +mod node; mod server; pub use server::ServerArgs; diff --git a/lazy-supplements/src/cli/node.rs b/lazy-supplements/src/cli/node.rs new file mode 100644 index 0000000..a0ba3d4 --- /dev/null +++ b/lazy-supplements/src/cli/node.rs @@ -0,0 +1,30 @@ +use std::{net::IpAddr, path::PathBuf}; + +use clap::{Args, Subcommand}; +use libp2p::PeerId; + +#[derive(Args, Debug)] +pub struct NodeArgs { + #[command(subcommand)] + command: NodeCommand +} + +#[derive(Args, Debug)] +pub struct JoinNodeArgs { + #[arg(long)] + endpoint: IpAddr, + #[arg(long)] + port: i32, + #[arg(long)] + peer_id: String, + #[arg(long)] + config: Option, +} + +#[derive(Debug, Subcommand)] +pub enum NodeCommand { + Ping(JoinNodeArgs), + Join(JoinNodeArgs), +} + + diff --git a/lazy-supplements/src/config/mod.rs b/lazy-supplements/src/config/mod.rs index 4006ea8..c347899 100644 --- a/lazy-supplements/src/config/mod.rs +++ b/lazy-supplements/src/config/mod.rs @@ -1,7 +1,10 @@ mod node; mod server; +use std::path::Path; +use crate::error::Error; pub use node::NodeConfig; +use serde::{Deserialize, Serialize}; pub use server::{ PartialServerConfig, ServerConfig, @@ -10,4 +13,51 @@ pub use server::{ DEFAULT_PARTIAL_SERVER_CONFIG, DEFAULT_SERVER_CONFIG }; +use tokio::{fs::File, io::{AsyncReadExt, AsyncWriteExt}}; +#[derive(Debug, Deserialize, Serialize)] +pub struct PartialConfig { + node: Option, + server: Option, +} + +impl PartialConfig { + pub fn new() -> Self { + PartialConfig { + node: Some(NodeConfig::new()), + server: None, + } + } + pub async fn read_or_create(path: T) -> Result + where + T: AsRef + { + if !path.as_ref().exists() { + Self::new().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: PartialConfig = toml::from_str(&content)?; + Ok(config) + } + pub async fn write_to(&self, path:T) -> Result<(), Error> + where + T: AsRef + { + if !path.as_ref().exists() { + let _ = File::create(&path).await?; + } + let mut file = File::open(&path).await?; + file.write_all(toml::to_string(self)?.as_bytes()).await?; + Ok(()) + } +} + + diff --git a/lazy-supplements/src/config/node.rs b/lazy-supplements/src/config/node.rs index 2437f94..75815cd 100644 --- a/lazy-supplements/src/config/node.rs +++ b/lazy-supplements/src/config/node.rs @@ -1,10 +1,31 @@ +use std::path::PathBuf; + use libp2p::identity::{self, Keypair}; use serde::{Deserialize, Serialize}; +use crate::global::DEFAULT_DATABASE_FILE_PATH; + #[derive(Debug, Deserialize, Serialize)] pub struct NodeConfig { #[serde(with = "keypair")] secret: Keypair, + database_path: Option +} + +impl NodeConfig { + pub fn new() -> Self { + Self { + secret: identity::Keypair::generate_ed25519(), + database_path: None, + } + } + pub fn get_database_path(&self) -> PathBuf { + if let Some(x) = self.database_path.clone() { + x + } else { + DEFAULT_DATABASE_FILE_PATH.clone() + } + } } mod keypair { @@ -39,6 +60,7 @@ mod tests { let keypair = identity::Keypair::generate_ed25519(); let config = NodeConfig { secret: keypair.clone(), + database_path: None, }; let string = toml::to_string(&config).unwrap(); println!("Parsed config: {}", &string); diff --git a/lazy-supplements/src/error.rs b/lazy-supplements/src/error.rs index b90cb94..008fd3a 100644 --- a/lazy-supplements/src/error.rs +++ b/lazy-supplements/src/error.rs @@ -2,6 +2,12 @@ pub enum Error { #[error("DB Error: {0}")] Db(#[from]sea_orm::DbErr), + #[error("IO Error: {0}")] + Io(#[from]std::io::Error), #[error("mandatory config `{0}` is missing")] MissingConfig(String), + #[error("toml deserialization error: {0}")] + TomlDe(#[from] toml::de::Error), + #[error("toml serialization error: {0}")] + TomlSer(#[from] toml::ser::Error), } \ No newline at end of file diff --git a/lazy-supplements/src/global/mod.rs b/lazy-supplements/src/global/mod.rs index b9c6945..8cfe6e7 100644 --- a/lazy-supplements/src/global/mod.rs +++ b/lazy-supplements/src/global/mod.rs @@ -1,17 +1,70 @@ -use crate::config::{ServerConfig}; +use std::{path::PathBuf, sync::LazyLock}; + +use crate::config::{NodeConfig, ServerConfig}; use sea_orm::DatabaseConnection; use tokio::sync::OnceCell; mod database; +pub static PRODUCT_NAME: LazyLock = LazyLock::new(|| { + env!("CARGO_PKG_NAME").to_string() +}); + +pub static DEFAULT_CONFIG_DIR_PATH: LazyLock = LazyLock::new(|| { + let dir = if let Some(x) = dirs::config_local_dir() { + x + } else { + todo!() + }; + + dir.join(&*PRODUCT_NAME) +}); + +pub static DEFAULT_CONFIG_FILE_NAME: LazyLock = LazyLock::new(|| { + PathBuf::from(String::new() + env!("CARGO_PKG_NAME") + ".toml") +}); + +pub static DEFAULT_CONFIG_FILE_PATH: LazyLock = LazyLock::new(|| { + DEFAULT_CONFIG_DIR_PATH.join(&*DEFAULT_CONFIG_FILE_NAME) +}); + +pub static DEFAULT_DATA_DIR_PATH: LazyLock = LazyLock::new(|| { + let dir = if let Some(x) = dirs::data_local_dir() { + x + } else { + todo!() + }; + + dir.join(&*PRODUCT_NAME) +}); + +pub static DEFAULT_DATABASE_FILE_NAME: LazyLock = LazyLock::new(|| { + PathBuf::from(String::new() + env!("CARGO_PKG_NAME") + ".sqlite") +}); + +pub static DEFAULT_DATABASE_FILE_PATH: LazyLock = LazyLock::new(|| { + DEFAULT_DATA_DIR_PATH.join(&*DEFAULT_DATABASE_FILE_NAME) +}); + pub static GLOBAL: Global = Global{ + node_config: OnceCell::const_new(), server_config: OnceCell::const_new(), database: OnceCell::const_new(), }; pub struct Global { - server_config: OnceCell, - database: OnceCell, + pub server_config: OnceCell, + pub node_config: OnceCell, + pub database: OnceCell, } #[cfg(test)] -pub use database::tests::get_or_init_temporary_database; \ No newline at end of file +pub use database::tests::get_or_init_temporary_database; + +impl Global { + pub fn get_node_config(&self) -> Option<&NodeConfig> { + self.node_config.get() + } + pub async fn get_or_try_init_node_config(&self, config: NodeConfig) -> &NodeConfig { + self.node_config.get_or_init(|| async {config}).await + } +} \ No newline at end of file diff --git a/lazy-supplements/src/lib.rs b/lazy-supplements/src/lib.rs index 2f64b69..636048c 100644 --- a/lazy-supplements/src/lib.rs +++ b/lazy-supplements/src/lib.rs @@ -3,3 +3,5 @@ pub mod config; pub mod entity; pub mod error; pub mod global; +#[cfg(any(test, feature="test"))] +pub mod tests; diff --git a/lazy-supplements/src/tests.rs b/lazy-supplements/src/tests.rs new file mode 100644 index 0000000..a13cb7b --- /dev/null +++ b/lazy-supplements/src/tests.rs @@ -0,0 +1,17 @@ +use std::{path::PathBuf, sync::LazyLock}; + +use tempfile::TempDir; + +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) +}); + +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") +});