Add node command and default path
This commit is contained in:
parent
d2b63140fd
commit
a3a8295880
10 changed files with 189 additions and 19 deletions
|
@ -15,6 +15,8 @@ base64 = "0.22.1"
|
||||||
chrono = "0.4.41"
|
chrono = "0.4.41"
|
||||||
chrono-tz = "0.10.3"
|
chrono-tz = "0.10.3"
|
||||||
clap = { version = "4.5.38", features = ["derive"] }
|
clap = { version = "4.5.38", features = ["derive"] }
|
||||||
|
dirs = "6.0.0"
|
||||||
|
futures = "0.3.31"
|
||||||
libp2p.workspace = true
|
libp2p.workspace = true
|
||||||
sea-orm = { version = "1.1.11", features = ["sqlx-sqlite", "runtime-tokio-native-tls", "macros", "with-chrono", "with-uuid"] }
|
sea-orm = { version = "1.1.11", features = ["sqlx-sqlite", "runtime-tokio-native-tls", "macros", "with-chrono", "with-uuid"] }
|
||||||
sea-orm-migration.workspace = true
|
sea-orm-migration.workspace = true
|
||||||
|
@ -23,9 +25,10 @@ tempfile = { version = "3.20.0", optional = true }
|
||||||
thiserror = "2.0.12"
|
thiserror = "2.0.12"
|
||||||
tokio = { version = "1.45.0", features = ["macros", "rt"] }
|
tokio = { version = "1.45.0", features = ["macros", "rt"] }
|
||||||
toml = "0.8.22"
|
toml = "0.8.22"
|
||||||
|
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
|
||||||
uuid = { version = "1.17.0", features = ["v4"] }
|
uuid = { version = "1.17.0", features = ["v4"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
lazy-supplements-migration.workspace = true
|
lazy-supplements-migration.workspace = true
|
||||||
sea-orm-migration.workspace = true
|
sea-orm-migration.workspace = true
|
||||||
tempfile = "3.20.0"
|
tempfile = "3.20.0"
|
||||||
|
|
|
@ -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,
|
|
||||||
}
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
mod connect;
|
|
||||||
mod init;
|
mod init;
|
||||||
|
mod node;
|
||||||
mod server;
|
mod server;
|
||||||
|
|
||||||
pub use server::ServerArgs;
|
pub use server::ServerArgs;
|
||||||
|
|
30
lazy-supplements/src/cli/node.rs
Normal file
30
lazy-supplements/src/cli/node.rs
Normal file
|
@ -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<PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Subcommand)]
|
||||||
|
pub enum NodeCommand {
|
||||||
|
Ping(JoinNodeArgs),
|
||||||
|
Join(JoinNodeArgs),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
mod node;
|
mod node;
|
||||||
mod server;
|
mod server;
|
||||||
|
|
||||||
|
use std::path::Path;
|
||||||
|
use crate::error::Error;
|
||||||
pub use node::NodeConfig;
|
pub use node::NodeConfig;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
pub use server::{
|
pub use server::{
|
||||||
PartialServerConfig,
|
PartialServerConfig,
|
||||||
ServerConfig,
|
ServerConfig,
|
||||||
|
@ -10,4 +13,51 @@ pub use server::{
|
||||||
DEFAULT_PARTIAL_SERVER_CONFIG,
|
DEFAULT_PARTIAL_SERVER_CONFIG,
|
||||||
DEFAULT_SERVER_CONFIG
|
DEFAULT_SERVER_CONFIG
|
||||||
};
|
};
|
||||||
|
use tokio::{fs::File, io::{AsyncReadExt, AsyncWriteExt}};
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub struct PartialConfig {
|
||||||
|
node: Option<NodeConfig>,
|
||||||
|
server: Option<ServerConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialConfig {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
PartialConfig {
|
||||||
|
node: Some(NodeConfig::new()),
|
||||||
|
server: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub async fn read_or_create<T>(path: T) -> Result<Self, Error>
|
||||||
|
where
|
||||||
|
T: AsRef<Path>
|
||||||
|
{
|
||||||
|
if !path.as_ref().exists() {
|
||||||
|
Self::new().write_to(&path).await?;
|
||||||
|
}
|
||||||
|
Self::read_from(&path).await
|
||||||
|
}
|
||||||
|
pub async fn read_from<T>(path:T) -> Result<Self, Error>
|
||||||
|
where
|
||||||
|
T: AsRef<Path>
|
||||||
|
{
|
||||||
|
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<T>(&self, path:T) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
T: AsRef<Path>
|
||||||
|
{
|
||||||
|
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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,31 @@
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use libp2p::identity::{self, Keypair};
|
use libp2p::identity::{self, Keypair};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::global::DEFAULT_DATABASE_FILE_PATH;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct NodeConfig {
|
pub struct NodeConfig {
|
||||||
#[serde(with = "keypair")]
|
#[serde(with = "keypair")]
|
||||||
secret: Keypair,
|
secret: Keypair,
|
||||||
|
database_path: Option<PathBuf>
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
mod keypair {
|
||||||
|
@ -39,6 +60,7 @@ mod tests {
|
||||||
let keypair = identity::Keypair::generate_ed25519();
|
let keypair = identity::Keypair::generate_ed25519();
|
||||||
let config = NodeConfig {
|
let config = NodeConfig {
|
||||||
secret: keypair.clone(),
|
secret: keypair.clone(),
|
||||||
|
database_path: None,
|
||||||
};
|
};
|
||||||
let string = toml::to_string(&config).unwrap();
|
let string = toml::to_string(&config).unwrap();
|
||||||
println!("Parsed config: {}", &string);
|
println!("Parsed config: {}", &string);
|
||||||
|
|
|
@ -2,6 +2,12 @@
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("DB Error: {0}")]
|
#[error("DB Error: {0}")]
|
||||||
Db(#[from]sea_orm::DbErr),
|
Db(#[from]sea_orm::DbErr),
|
||||||
|
#[error("IO Error: {0}")]
|
||||||
|
Io(#[from]std::io::Error),
|
||||||
#[error("mandatory config `{0}` is missing")]
|
#[error("mandatory config `{0}` is missing")]
|
||||||
MissingConfig(String),
|
MissingConfig(String),
|
||||||
|
#[error("toml deserialization error: {0}")]
|
||||||
|
TomlDe(#[from] toml::de::Error),
|
||||||
|
#[error("toml serialization error: {0}")]
|
||||||
|
TomlSer(#[from] toml::ser::Error),
|
||||||
}
|
}
|
|
@ -1,17 +1,70 @@
|
||||||
use crate::config::{ServerConfig};
|
use std::{path::PathBuf, sync::LazyLock};
|
||||||
|
|
||||||
|
use crate::config::{NodeConfig, ServerConfig};
|
||||||
use sea_orm::DatabaseConnection;
|
use sea_orm::DatabaseConnection;
|
||||||
use tokio::sync::OnceCell;
|
use tokio::sync::OnceCell;
|
||||||
|
|
||||||
mod database;
|
mod database;
|
||||||
|
|
||||||
|
pub static PRODUCT_NAME: LazyLock<String> = LazyLock::new(|| {
|
||||||
|
env!("CARGO_PKG_NAME").to_string()
|
||||||
|
});
|
||||||
|
|
||||||
|
pub static DEFAULT_CONFIG_DIR_PATH: LazyLock<PathBuf> = 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<PathBuf> = LazyLock::new(|| {
|
||||||
|
PathBuf::from(String::new() + env!("CARGO_PKG_NAME") + ".toml")
|
||||||
|
});
|
||||||
|
|
||||||
|
pub static DEFAULT_CONFIG_FILE_PATH: LazyLock<PathBuf> = LazyLock::new(|| {
|
||||||
|
DEFAULT_CONFIG_DIR_PATH.join(&*DEFAULT_CONFIG_FILE_NAME)
|
||||||
|
});
|
||||||
|
|
||||||
|
pub static DEFAULT_DATA_DIR_PATH: LazyLock<PathBuf> = 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<PathBuf> = LazyLock::new(|| {
|
||||||
|
PathBuf::from(String::new() + env!("CARGO_PKG_NAME") + ".sqlite")
|
||||||
|
});
|
||||||
|
|
||||||
|
pub static DEFAULT_DATABASE_FILE_PATH: LazyLock<PathBuf> = LazyLock::new(|| {
|
||||||
|
DEFAULT_DATA_DIR_PATH.join(&*DEFAULT_DATABASE_FILE_NAME)
|
||||||
|
});
|
||||||
|
|
||||||
pub static GLOBAL: Global = Global{
|
pub static GLOBAL: Global = Global{
|
||||||
|
node_config: OnceCell::const_new(),
|
||||||
server_config: OnceCell::const_new(),
|
server_config: OnceCell::const_new(),
|
||||||
database: OnceCell::const_new(),
|
database: OnceCell::const_new(),
|
||||||
};
|
};
|
||||||
pub struct Global {
|
pub struct Global {
|
||||||
server_config: OnceCell<ServerConfig>,
|
pub server_config: OnceCell<ServerConfig>,
|
||||||
database: OnceCell<DatabaseConnection>,
|
pub node_config: OnceCell<NodeConfig>,
|
||||||
|
pub database: OnceCell<DatabaseConnection>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub use database::tests::get_or_init_temporary_database;
|
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
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,3 +3,5 @@ pub mod config;
|
||||||
pub mod entity;
|
pub mod entity;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod global;
|
pub mod global;
|
||||||
|
#[cfg(any(test, feature="test"))]
|
||||||
|
pub mod tests;
|
||||||
|
|
17
lazy-supplements/src/tests.rs
Normal file
17
lazy-supplements/src/tests.rs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
use std::{path::PathBuf, sync::LazyLock};
|
||||||
|
|
||||||
|
use tempfile::TempDir;
|
||||||
|
|
||||||
|
pub static TEST_DIR_PATH: LazyLock<PathBuf> = 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<PathBuf> = LazyLock::new(|| {
|
||||||
|
TempDir::new().unwrap().keep()
|
||||||
|
});
|
||||||
|
|
||||||
|
pub static TEST_DATABASE_PATH: std::sync::LazyLock<PathBuf> = std::sync::LazyLock::new(|| {
|
||||||
|
TEST_DIR_PATH.join("lazy-supplements.sqlite")
|
||||||
|
});
|
Loading…
Add table
Reference in a new issue