Fix global config

This commit is contained in:
fluo10 2025-08-18 08:22:20 +09:00
parent e24ff770a5
commit 2cb1f50f57
15 changed files with 106 additions and 95 deletions

View file

@ -18,37 +18,37 @@ use clap::Args;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Config { pub struct Config {
p2p: P2pConfig, pub p2p: P2pConfig,
storage: StorageConfig, pub storage: StorageConfig,
rpc: RpcConfig, pub rpc: RpcConfig,
} }
#[cfg_attr(feature="desktop", derive(Args))] #[cfg_attr(feature="desktop", derive(Args))]
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
pub struct PartialConfig { pub struct PartialConfig {
#[cfg_attr(feature="desktop", command(flatten))] #[cfg_attr(feature="desktop", command(flatten))]
p2p: PartialP2pConfig, pub p2p: PartialP2pConfig,
#[cfg_attr(feature="desktop", command(flatten))] #[cfg_attr(feature="desktop", command(flatten))]
storage: PartialStorageConfig, pub storage: PartialStorageConfig,
#[cfg_attr(feature="desktop", command(flatten))] #[cfg_attr(feature="desktop", command(flatten))]
rpc: PartialRpcConfig, pub rpc: PartialRpcConfig,
} }
impl PartialConfig { impl PartialConfig {
fn new() -> Self { pub fn new() -> Self {
Self { Self {
p2p : PartialP2pConfig::empty().with_new_secret(), p2p : PartialP2pConfig::empty().with_new_secret(),
storage: PartialStorageConfig::empty(), storage: PartialStorageConfig::empty(),
rpc: PartialRpcConfig::empty(), rpc: PartialRpcConfig::empty(),
} }
} }
fn from_toml(s: &str) -> Result<Self, toml::de::Error> { pub fn from_toml(s: &str) -> Result<Self, toml::de::Error> {
toml::from_str(s) toml::from_str(s)
} }
fn into_toml(&self) -> Result<String, toml::ser::Error> { pub fn into_toml(&self) -> Result<String, toml::ser::Error> {
toml::to_string(self) toml::to_string(self)
} }
async fn read_or_create<T>(path: T) -> Result<Self, ConfigError> pub async fn read_or_create<T>(path: T) -> Result<Self, ConfigError>
where where
T: AsRef<Path> T: AsRef<Path>
{ {
@ -57,7 +57,7 @@ impl PartialConfig {
} }
Self::read_from(&path).await Self::read_from(&path).await
} }
async fn read_from<T>(path:T) -> Result<Self, ConfigError> pub async fn read_from<T>(path:T) -> Result<Self, ConfigError>
where where
T: AsRef<Path> T: AsRef<Path>
{ {
@ -67,7 +67,7 @@ impl PartialConfig {
let config: Self = toml::from_str(&content)?; let config: Self = toml::from_str(&content)?;
Ok(config) Ok(config)
} }
async fn write_to<T>(&self, path:T) -> Result<(), ConfigError> pub async fn write_to<T>(&self, path:T) -> Result<(), ConfigError>
where where
T: AsRef<Path> T: AsRef<Path>
{ {

View file

@ -1,15 +1,46 @@
use crate::{config::{P2pConfig, StorageConfig}, error::Error, global::GlobalConstant}; use tempfile::TempDir;
use tokio::sync::OnceCell;
pub static STORAGE_CONFIG: GlobalConstant<StorageConfig> = GlobalConstant::const_new(stringify!(STORAGE_CONFIG)); use crate::{config::{Config, PartialP2pConfig, PartialRpcConfig, PartialStorageConfig, StorageConfig}, error::Error, global::GlobalConstant};
pub static P2P_CONFIG: GlobalConstant<P2pConfig> = GlobalConstant::const_new(stringify!(P2P_CONFIG));
#[cfg(test)] pub static CONFIG: GlobalConfig = GlobalConfig::const_new();
mod tests { pub struct GlobalConfig {
use crate::global::{config::P2P_CONFIG, STORAGE_CONFIG}; inner: OnceCell<Config>
}
#[test] impl GlobalConfig {
fn test_global_constant_names() { pub const fn const_new() -> Self {
assert_eq!(STORAGE_CONFIG.name, stringify!(STORAGE_CONFIG)); Self{
assert_eq!(P2P_CONFIG.name, stringify!(P2P_CONFIG)); inner: OnceCell::const_new()
}
}
pub async fn get_or_init(&'static self, source: Config) -> &'static Config {
self.inner.get_or_init(|| async {
source
}).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
} }
} }

View file

@ -44,12 +44,12 @@ pub use tests::*;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::{cache::migration::CacheMigrator, data::migration::DataMigrator, global::STORAGE_CONFIG, tests::GlobalTestDefault}; use crate::{cache::migration::CacheMigrator, data::migration::DataMigrator, global::CONFIG, tests::GlobalTestDefault};
pub async fn get_or_init_test_data_database() -> &'static DatabaseConnection{ pub async fn get_or_init_test_data_database() -> &'static DatabaseConnection{
DATA_DATABASE_CONNECTION.get_or_init(STORAGE_CONFIG.get_or_init_test_default().await.get_data_database_path(), DataMigrator).await DATA_DATABASE_CONNECTION.get_or_init(CONFIG.get_or_init_test_default().await.storage.get_cache_database_path(), DataMigrator).await
} }
pub async fn get_or_init_test_cache_database() -> &'static DatabaseConnection{ pub async fn get_or_init_test_cache_database() -> &'static DatabaseConnection{
CACHE_DATABASE_CONNECTION.get_or_init(STORAGE_CONFIG.get_or_init_test_default().await.get_cache_database_path(), CacheMigrator).await CACHE_DATABASE_CONNECTION.get_or_init(CONFIG.get_or_init_test_default()await.storage.get_cache_database_path(), CacheMigrator).await
} }
} }

View file

@ -58,7 +58,7 @@ impl<T> GlobalConstant<T> {
self.inner.get() self.inner.get()
} }
pub fn get_and_unwrap(&'static self) -> &'static T { pub fn get_and_unwrap(&'static self) -> &'static T {
self.get().expect(&format!("{} is uninitialized!", &stringify!(self))) self.get().expect(&format!("{} is uninitialized!", self.name))
} }
} }

View file

@ -29,20 +29,3 @@ pub trait TestDefault {
pub trait GlobalTestDefault<T: 'static> { pub trait GlobalTestDefault<T: 'static> {
async fn get_or_init_test_default(&'static self) -> &'static T; async fn get_or_init_test_default(&'static self) -> &'static T;
} }
pub fn test_cbor_serialize_deserialize<T>(src: T)
where T: DeserializeOwned + Serialize + PartialEq + std::fmt::Debug
{
let mut buf: Vec<u8> = Vec::new();
ciborium::into_writer(&src, &mut buf).unwrap();
let dst: T = ciborium::from_reader(buf.as_slice()).unwrap();
assert_eq!(src, dst);
}
pub fn test_toml_serialize_deserialize<T>(src: T)
where T: DeserializeOwned + Serialize + PartialEq + std::fmt::Debug
{
let buf = toml::to_string(&src).unwrap();
let dst: T = toml::from_str(&buf).unwrap();
assert_eq!(src, dst);
}

View file

@ -1,8 +1,7 @@
use std::{net::IpAddr, path::PathBuf}; use std::{net::IpAddr, path::PathBuf};
use clap::Args; use clap::Args;
use caretta_core::config::{BaseConfig, ConfigError}; use caretta_core::config::{PartialConfig,PartialP2pConfig, PartialStorageConfig, ConfigError};
use crate::config::{PartialP2pConfig, PartialStorageConfig};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -13,9 +12,9 @@ pub struct ConfigArgs {
#[arg(short = 'c', long = "config")] #[arg(short = 'c', long = "config")]
pub file_path: Option<PathBuf>, pub file_path: Option<PathBuf>,
#[arg(skip)] #[arg(skip)]
pub file_content: Option<BaseConfig>, pub file_content: Option<PartialConfig>,
#[command(flatten)] #[command(flatten)]
pub args: BaseConfig, pub args: PartialConfig,
} }
@ -23,9 +22,9 @@ impl ConfigArgs {
pub fn get_file_path_or_default(&self) -> PathBuf { pub fn get_file_path_or_default(&self) -> PathBuf {
self.file_path.clone().unwrap_or((*DEFAULT_CONFIG_FILE_PATH).clone()) self.file_path.clone().unwrap_or((*DEFAULT_CONFIG_FILE_PATH).clone())
} }
pub async fn get_or_read_file_content(&mut self) -> &mut BaseConfig { pub async fn get_or_read_file_content(&mut self) -> &mut PartialConfig {
self.file_content.get_or_insert( self.file_content.get_or_insert(
BaseConfig::read_from(self.get_file_path_or_default()).await.unwrap() PartialConfig::read_from(self.get_file_path_or_default()).await.unwrap()
) )
} }
} }

View file

@ -2,11 +2,11 @@ use std::path::PathBuf;
mod args; mod args;
mod device; mod device;
mod server; mod peer;
pub use args::*; pub use args::*;
pub use device::*; pub use device::*;
pub use server::*; pub use peer::*;
pub trait RunnableCommand { pub trait RunnableCommand {
async fn run(self); async fn run(self);

View file

@ -1,18 +0,0 @@
use clap::Args;
use caretta_core::utils::runnable::Runnable;
use libp2p::{noise, ping, swarm::{NetworkBehaviour, SwarmEvent}, tcp, yamux, Swarm};
use crate::{error::Error, global::P2P_CONFIG};
use super::ConfigArgs;
#[derive(Args, Debug)]
pub struct ServerCommandArgs {
#[command(flatten)]
config: ConfigArgs,
}
impl Runnable for ServerCommandArgs {
async fn run(self) {
P2P_CONFIG.get_and_unwrap().clone().launch_swarm();
}
}

View file

@ -1,5 +1,5 @@
[package] [package]
name = "caretta-examples-core" name = "caretta-example-core"
edition.workspace = true edition.workspace = true
version.workspace = true version.workspace = true
description.workspace = true description.workspace = true
@ -7,4 +7,4 @@ license.workspace = true
repository.workspace = true repository.workspace = true
[dependencies] [dependencies]
caretta.path = "../.." caretta.path = "../.."

View file

@ -1,5 +1,5 @@
[package] [package]
name = "caretta-examples-desktop" name = "caretta-example-desktop"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
@ -7,12 +7,8 @@ edition = "2021"
[dependencies] [dependencies]
clap.workspace = true clap.workspace = true
dioxus.workspace = true caretta = { path = "../..", features = ["desktop"] }
caretta-desktop.path = "../../desktop" caretta-example-core.path = "../core"
caretta-examples-core.path = "../core" libp2p.workspace = true
tokio.workspace = true
[features]
default = ["desktop"]
web = ["dioxus/web"]
desktop = ["dioxus/desktop"]
mobile = ["dioxus/mobile"]

View file

@ -1,5 +1,8 @@
mod server;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use caretta_desktop::cli::*; use caretta::cli::*;
pub use server::*;
#[derive(Debug, Parser)] #[derive(Debug, Parser)]
pub struct Cli { pub struct Cli {

View file

@ -0,0 +1,19 @@
use clap::Args;
use caretta::{error::Error, utils::runnable::Runnable};
use libp2p::{noise, ping, swarm::{NetworkBehaviour, SwarmEvent}, tcp, yamux, Swarm};
use super::ConfigArgs;
#[derive(Args, Debug)]
pub struct ServerCommandArgs {
#[command(flatten)]
config: ConfigArgs,
}
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();
}
}

View file

@ -1,5 +1,11 @@
use crate::cli::Cli;
mod cli; mod cli;
mod ipc; mod ipc;
fn main() {
dioxus::launch(caretta_examples_core::ui::plain::App); #[tokio::main]
async fn main() {
let args = Cli::parse();
} }

View file

@ -1,17 +1,9 @@
[package] [package]
name = "caretta-examples-mobile" name = "caretta-example-mobile"
version = "0.1.0" version = "0.1.0"
authors = ["fluo10 <fluo10.dev@fireturtle.net>"] authors = ["fluo10 <fluo10.dev@fireturtle.net>"]
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
dioxus.workspace = true dioxus.workspace = true
caretta-examples-core.path = "../core" caretta-example-core.path = "../core"
[features]
default = ["mobile"]
web = ["dioxus/web"]
desktop = ["dioxus/desktop"]
mobile = ["dioxus/mobile"]

View file

@ -1,5 +1,5 @@
pub use caretta_core::*; pub use caretta_core::*;
#[cfg(desktop)] #[cfg(feature = "desktop")]
pub use caretta_desktop::*; pub use caretta_desktop::*;
#[cfg(mobile)] #[cfg(feature = "mobile")]
pub use caretta_mobile::*; pub use caretta_mobile::*;