Compare commits

..

No commits in common. "a3a82958806ade4a560112c0e98673ccf846d04a" and "d93d3a83d1fbf285a55cb02a591ba21d5320f73b" have entirely different histories.

12 changed files with 35 additions and 200 deletions

View file

@ -14,7 +14,10 @@ pub mod tests {
use super::*; use super::*;
pub async fn get_or_init_temporary_database() -> &'static DatabaseConnection { pub async fn get_or_init_temporary_database() -> &'static DatabaseConnection {
global::GLOBAL.get_or_try_init_temporary_database(migration::Migrator).await.unwrap() global::GLOBAL.get_or_try_init_temporary_database( |x| async {
migration::Migrator::up(&x, None).await?;
Ok(x)
}).await.unwrap()
} }
} }

View file

@ -15,20 +15,16 @@ 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
serde = { version = "1.0.219", features = ["derive"] } serde = { version = "1.0.219", features = ["derive"] }
tempfile = { version = "3.20.0", optional = true } 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"

View file

@ -0,0 +1,13 @@
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,
}

View file

@ -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;

View file

@ -1,30 +0,0 @@
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),
}

View file

@ -1,10 +1,7 @@
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,
@ -13,51 +10,4 @@ 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(())
}
}

View file

@ -1,31 +1,10 @@
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 {
@ -60,7 +39,6 @@ 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);

View file

@ -2,12 +2,6 @@
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),
} }

View file

@ -1,7 +1,6 @@
use std::path::Path; use std::path::Path;
use sea_orm::{ConnectOptions, Database, DbErr, DatabaseConnection}; use sea_orm::{ConnectOptions, Database, DbErr, DatabaseConnection};
use sea_orm_migration::MigratorTrait;
use crate::error::Error; use crate::error::Error;
use tokio::sync::OnceCell; use tokio::sync::OnceCell;
@ -19,25 +18,26 @@ impl Global {
fn get_database(&self) -> Option<&DatabaseConnection> { fn get_database(&self) -> Option<&DatabaseConnection> {
self.database.get() self.database.get()
} }
async fn get_or_try_init_database<T, U>(&self, path: T, _: U) -> Result<&DatabaseConnection, Error> async fn get_or_try_init_database<T, F, Fut>(&self, path: T, migration: F) -> Result<&DatabaseConnection, Error>
where where
T: AsRef<Path>, T: AsRef<Path>,
U: MigratorTrait, F: FnOnce(DatabaseConnection) -> Fut,
Fut: Future<Output = Result<DatabaseConnection, DbErr>>
{ {
let url = "sqlite://".to_string() + path.as_ref().to_str().unwrap() + "?mode=rwc"; let url = "sqlite://".to_string() + path.as_ref().to_str().unwrap() + "?mode=rwc";
Ok(self.database.get_or_try_init(|| async { Ok(self.database.get_or_try_init(|| async {
let db = Database::connect(&url).await?; let db = Database::connect(&url).await?;
U::up(&db, None).await?; Ok::<DatabaseConnection, DbErr>(migration(db).await?)
Ok::<DatabaseConnection, DbErr>(db)
}).await?) }).await?)
} }
#[cfg(any(test, feature="test"))] #[cfg(any(test, feature="test"))]
pub async fn get_or_try_init_temporary_database<T>(&self, migrator: T) -> Result<&DatabaseConnection, Error> pub async fn get_or_try_init_temporary_database<F, Fut>(&self, migration: F) -> Result<&DatabaseConnection, Error>
where where
T: MigratorTrait, F: FnOnce(DatabaseConnection) -> Fut,
Fut: Future<Output = Result<DatabaseConnection, DbErr>>
{ {
self.get_or_try_init_database(&*TEST_DATABASE_URL, migrator).await self.get_or_try_init_database(&*TEST_DATABASE_URL, migration).await
} }
} }
@ -56,7 +56,10 @@ pub mod tests {
use super::*; use super::*;
pub async fn get_or_init_temporary_database() -> &'static DatabaseConnection { pub async fn get_or_init_temporary_database() -> &'static DatabaseConnection {
GLOBAL.get_or_try_init_temporary_database( Migrator).await.unwrap() GLOBAL.get_or_try_init_temporary_database( |x| async {
Migrator::up(&x, None).await?;
Ok(x)
}).await.unwrap()
} }
#[tokio::test] #[tokio::test]

View file

@ -1,70 +1,17 @@
use std::{path::PathBuf, sync::LazyLock}; use crate::config::{ServerConfig};
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 {
pub server_config: OnceCell<ServerConfig>, server_config: OnceCell<ServerConfig>,
pub node_config: OnceCell<NodeConfig>, database: OnceCell<DatabaseConnection>,
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
}
}

View file

@ -3,5 +3,3 @@ 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;

View file

@ -1,17 +0,0 @@
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( &timestamp)
});
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")
});