Fix errors

This commit is contained in:
fluo10 2025-07-05 10:30:55 +09:00
parent 1adebe17e4
commit 9c960a9d59
16 changed files with 138 additions and 238 deletions

View file

@ -10,11 +10,11 @@ use serde::{Deserialize, Serialize};
use crate::data::value::{MultiaddrValue, PeerIdValue};
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Deserialize, Serialize)]
#[sea_orm(table_name = "peer")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: u32,
pub id: Uuid,
#[sea_orm(indexed)]
pub created_at: DateTimeUtc,
#[sea_orm(indexed)]

View file

@ -42,7 +42,7 @@ impl TableMigration for Peer {
Table::create()
.table(Self::Table)
.if_not_exists()
.col(pk_auto(Self::Id))
.col(pk_uuid(Self::Id))
.col(string_len(Self::PeerId, 255))
.col(timestamp(Self::CreatedAt))
.col(timestamp(Self::UpdatedAt))

View file

@ -2,4 +2,10 @@
pub enum ConfigError {
#[error("missing config: {0}")]
MissingConfig(String),
#[error("Io error: {0}")]
Io(#[from] std::io::Error),
#[error("Toml Deserialization Error")]
TomlDerialization(#[from] toml::de::Error),
#[error("Toml Serialization Error")]
TomlSerialization(#[from] toml::ser::Error),
}

View file

@ -3,7 +3,7 @@ mod storage;
mod p2p;
use std::path::Path;
use crate::{error::Error, utils::{emptiable::Emptiable, mergeable::Mergeable}};
use crate::{utils::{emptiable::Emptiable, mergeable::Mergeable}};
pub use error::ConfigError;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
@ -19,7 +19,7 @@ pub trait PartialConfig: Emptiable + From<Self::Config> + Mergeable {
}
pub trait PartialCoreConfig: DeserializeOwned + Serialize {
pub trait BaseConfig: DeserializeOwned + Serialize {
fn new() -> Self;
fn from_toml(s: &str) -> Result<Self, toml::de::Error> {
toml::from_str(s)
@ -27,7 +27,7 @@ pub trait PartialCoreConfig: DeserializeOwned + Serialize {
fn into_toml(&self) -> Result<String, toml::ser::Error> {
toml::to_string(self)
}
async fn read_or_create<T>(path: T) -> Result<Self, Error>
async fn read_or_create<T>(path: T) -> Result<Self, ConfigError>
where
T: AsRef<Path>
{
@ -36,7 +36,7 @@ pub trait PartialCoreConfig: DeserializeOwned + Serialize {
}
Self::read_from(&path).await
}
async fn read_from<T>(path:T) -> Result<Self, Error>
async fn read_from<T>(path:T) -> Result<Self, ConfigError>
where
T: AsRef<Path>
{
@ -46,7 +46,7 @@ pub trait PartialCoreConfig: DeserializeOwned + Serialize {
let config: Self = toml::from_str(&content)?;
Ok(config)
}
async fn write_to<T>(&self, path:T) -> Result<(), Error>
async fn write_to<T>(&self, path:T) -> Result<(), ConfigError>
where
T: AsRef<Path>
{

View file

@ -3,7 +3,8 @@ use std::{net::{IpAddr, Ipv4Addr}, ops, path::{Path, PathBuf}};
use base64::{prelude::BASE64_STANDARD, Engine};
#[cfg(feature="desktop")]
use clap::Args;
use libp2p::{identity::{self, DecodingError, Keypair}, noise, ping, tcp, yamux, Swarm};
use futures::StreamExt;
use libp2p::{identity::{self, DecodingError, Keypair}, noise, ping, swarm::SwarmEvent, tcp, yamux, Swarm};
use serde::{Deserialize, Serialize};
use tokio::{fs::File, io::{AsyncReadExt, AsyncWriteExt}};
use tracing_subscriber::EnvFilter;
@ -11,7 +12,7 @@ use tracing_subscriber::EnvFilter;
use crate::{
config::PartialConfig,
error::Error, p2p, utils::emptiable::Emptiable
error::Error, p2p, utils::{emptiable::Emptiable, mergeable::Mergeable}
};
static DEFAULT_P2P_LISTEN_IPS: &[IpAddr] = &[IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))];
@ -30,7 +31,7 @@ fn base64_to_keypair(base64: &str) -> Result<Keypair, Error> {
Ok(Keypair::from_protobuf_encoding(&vec)?)
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[derive(Clone, Debug, Deserialize, Serialize,)]
pub struct P2pConfig {
#[serde(with = "keypair_parser")]
pub secret: Keypair,
@ -39,7 +40,7 @@ pub struct P2pConfig {
}
impl P2pConfig {
pub async fn try_into_swarm (self) -> Result<Swarm<p2p::Behaviour>, Error> {
async fn try_into_swarm (self) -> Result<Swarm<p2p::Behaviour>, Error> {
let mut swarm = libp2p::SwarmBuilder::with_existing_identity(self.secret)
.with_tokio()
.with_tcp(
@ -52,6 +53,22 @@ impl P2pConfig {
swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse()?)?;
Ok(swarm)
}
pub async fn launch_swarm(self) -> Result<(), Error>{
let mut swarm = self.try_into_swarm().await?;
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;
},
_ => {}
}
});
}
}
}
impl TryFrom<PartialP2pConfig> for P2pConfig {
@ -86,7 +103,6 @@ mod keypair_parser {
}
#[cfg_attr(feature="desktop",derive(Args))]
#[cfg_attr(feature="macros", derive(Emptiable))]
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
pub struct PartialP2pConfig {
#[cfg_attr(feature="desktop",arg(long))]
@ -123,7 +139,33 @@ impl Default for PartialP2pConfig {
}
}
impl Emptiable for PartialP2pConfig {
fn empty() -> Self {
Self{
secret: None,
listen_ips: None,
port: None
}
}
fn is_empty(&self) -> bool {
self.secret.is_none() && self.listen_ips.is_none() && self.port.is_none()
}
}
impl Mergeable for PartialP2pConfig {
fn merge(&mut self, mut other: Self) {
if let Some(x) = other.secret.take() {
let _ = self.secret.insert(x);
};
if let Some(x) = other.listen_ips.take() {
let _ = self.listen_ips.insert(x);
};
if let Some(x) = other.port.take() {
let _ = self.port.insert(x);
};
}
}
#[cfg(test)]

View file

@ -5,7 +5,7 @@ use clap::Args;
#[cfg(any(test, feature="test"))]
use tempfile::tempdir;
use crate::{config::{ConfigError, PartialConfig}, utils::emptiable::Emptiable};
use crate::{config::{ConfigError, PartialConfig}, utils::{emptiable::Emptiable, mergeable::Mergeable}};
use libp2p::mdns::Config;
use serde::{Deserialize, Serialize};
@ -50,7 +50,6 @@ impl TryFrom<PartialStorageConfig> for StorageConfig {
}
}
#[cfg_attr(feature="desktop", derive(Args))]
#[cfg_attr(feature="macros", derive(Emptiable))]
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct PartialStorageConfig {
#[cfg_attr(feature="desktop", arg(long))]
@ -66,4 +65,27 @@ impl From<StorageConfig> for PartialStorageConfig {
cache_directory: Some(config.cache_directory),
}
}
}
impl Emptiable for PartialStorageConfig {
fn empty() -> Self {
Self {
data_directory: None,
cache_directory: None
}
}
fn is_empty(&self) -> bool {
self.data_directory.is_none() && self.cache_directory.is_none()
}
}
impl Mergeable for PartialStorageConfig {
fn merge(&mut self, mut other: Self) {
if let Some(x) = other.data_directory.take() {
let _ = self.data_directory.insert(x);
};
if let Some(x) = other.cache_directory.take() {
let _ = self.cache_directory.insert(x);
};
}
}

View file

@ -10,9 +10,9 @@ use sea_orm_migration::MigratorTrait;
use tokio::sync::{OnceCell, RwLock, RwLockReadGuard, RwLockWriteGuard};
mod peers;
pub use peers::PEERS;
pub use peers::*;
mod config;
pub use config::STORAGE_CONFIG;
pub use config::*;
mod database_connection;
pub use database_connection::*;
use uuid::{ContextV7, Timestamp, Uuid};

View file

@ -1,3 +1,5 @@
#[cfg(feature="macros")]
pub use lazy_supplements_macros::Mergeable;
pub trait Mergeable: Sized {
fn merge(&mut self, other: Self);
}

View file

@ -1,7 +1,7 @@
use std::{net::IpAddr, path::PathBuf};
use clap::Args;
use lazy_supplements_core::config::ConfigError;
use lazy_supplements_core::config::{BaseConfig, ConfigError};
use crate::config::{PartialP2pConfig, PartialStorageConfig};
#[cfg(unix)]
use crate::config::PartialUnixConfig;
@ -9,46 +9,29 @@ use crate::config::PartialUnixConfig;
use serde::{Deserialize, Serialize};
use crate::{
config::PartialDesktopConfig,
config::DesktopBaseConfig,
error::Error,
global::{DEFAULT_CONFIG_FILE_PATH, DEFAULT_PARTIAL_CORE_CONFIG,}
global::DEFAULT_CONFIG_FILE_PATH
};
#[derive(Args, Clone, Debug)]
pub struct ConfigArgs {
#[arg(short = "c", long = "config")]
#[arg(short = 'c', long = "config")]
pub file_path: Option<PathBuf>,
#[arg(skip)]
pub file_content: Option<Result<PartialDesktopConfig, ConfigError>>,
pub file_content: Option<DesktopBaseConfig>,
#[command(flatten)]
pub args: PartialDesktopConfig,
pub args: DesktopBaseConfig,
}
impl ConfigArgs {
pub fn get_file_path_or_default(&self) -> PathBuf {
self.file_path.unwrap_or(DEFAULT_CONFIG_FILE_PATH)
self.file_path.clone().unwrap_or((*DEFAULT_CONFIG_FILE_PATH).clone())
}
pub async fn get_or_read_file_content(&mut self) -> Result<PartialDesktopConfig, ConfigError> {
pub async fn get_or_read_file_content(&mut self) -> &mut DesktopBaseConfig {
self.file_content.get_or_insert(
PartialDesktopConfig::read_from(self.get_config_path_or_default()).await
).clone()
}
pub fn get_config_path_or_default(&self) -> PathBuf {
if let Some(x) = self.config.as_ref() {
x.clone()
} else {
DEFAULT_CONFIG_FILE_PATH.to_path_buf()
}
}
pub async fn try_into_partial_core_config(self) -> Result<PartialCoreConfig, Error> {
let mut config = PartialCoreConfig::read_from(self.get_config_path_or_default()).await?;
config.merge(self.core_config.into());
Ok(config)
}
pub async fn try_into_core_config(self) -> Result<CoreConfig, Error> {
let mut config = DEFAULT_PARTIAL_CORE_CONFIG.clone();
config.merge(self.try_into_partial_core_config().await?);
config.try_into()
DesktopBaseConfig::read_from(self.get_file_path_or_default()).await.unwrap()
)
}
}

View file

@ -12,14 +12,8 @@ pub use ping::DevicePingCommandArgs;
pub use remove::DeviceRemoveCommandArgs;
pub use scan::DeviceScanCommandArgs;
use std::{net::IpAddr, ops::Mul, path::PathBuf, str::FromStr};
use clap::{Args, Parser, Subcommand};
use crate::{cli::ServerArgs, error::Error};
use super::ConfigArgs;
#[derive(Debug, Args, Runnable)]
pub struct DeviceCommandArgs {

View file

@ -1,18 +1,18 @@
use clap::Args;
use lazy_supplements_core::utils::runnable::Runnable;
use libp2p::{noise, ping, swarm::{NetworkBehaviour, SwarmEvent}, tcp, yamux, Swarm};
use crate::{error::Error, global::GLOBAL};
use crate::{error::Error, global::P2P_CONFIG};
use super::ConfigArgs;
#[derive(Args, Debug)]
pub struct ServerArgs {
pub struct ServerCommandArgs {
#[command(flatten)]
config: ConfigArgs,
}
impl ServerArgs {
pub async fn start_server(self) -> Result<(), Error>{
let _ = crate::global::GLOBAL.get_or_init_core_config(self.config.try_into_core_config().await?).await;
GLOBAL.launch_swarm().await
impl Runnable for ServerCommandArgs {
async fn run(self) {
P2P_CONFIG.get_and_unwrap().clone().launch_swarm();
}
}

View file

@ -4,8 +4,10 @@ pub mod unix;
#[cfg(windows)]
pub mod windows;
use clap::Args;
pub use lazy_supplements_core::config::*;
use lazy_supplements_core::utils::{emptiable::Emptiable, mergeable::Mergeable};
use serde::{Deserialize, Serialize};
#[cfg(unix)]
pub use unix::*;
@ -13,15 +15,18 @@ pub use unix::*;
#[cfg(windows)]
pub use windows::*;
#[derive(Debug, Deserialize, Serialize)]
pub struct PartialDesktopConfig {
#[derive(Args, Clone, Debug, Deserialize, Emptiable, Mergeable, Serialize)]
pub struct DesktopBaseConfig {
#[command(flatten)]
p2p: PartialP2pConfig,
#[command(flatten)]
storage: PartialStorageConfig,
#[cfg(unix)]
#[command(flatten)]
unix: PartialUnixConfig,
}
impl PartialConfigRoot for PartialDesktopConfig {
impl BaseConfig for DesktopBaseConfig {
fn new() -> Self {
Self {
p2p : PartialP2pConfig::empty().with_new_secret(),
@ -29,4 +34,20 @@ impl PartialConfigRoot for PartialDesktopConfig {
unix: PartialUnixConfig::empty(),
}
}
}
impl Into<PartialP2pConfig> for &DesktopBaseConfig {
fn into(self) -> PartialP2pConfig {
self.p2p.clone()
}
}
impl Into<PartialStorageConfig> for &DesktopBaseConfig {
fn into(self) -> PartialStorageConfig {
self.storage.clone()
}
}
impl Into<PartialUnixConfig> for &DesktopBaseConfig {
fn into(self) -> PartialUnixConfig {
self.unix.clone()
}
}

View file

@ -1,6 +1,6 @@
use std::path::PathBuf;
use clap::Args;
use lazy_supplements_core::config::PartialConfig;
use lazy_supplements_core::{config::PartialConfig, utils::{emptiable::Emptiable, mergeable::Mergeable}};
use libp2p::mdns::Config;
use serde::{Deserialize, Serialize};
@ -20,11 +20,17 @@ impl TryFrom<PartialUnixConfig> for UnixConfig {
}
}
#[derive(Args, Clone, Debug, Deserialize, Serialize)]
#[derive(Args, Clone, Debug, Deserialize, Emptiable, Mergeable, Serialize)]
pub struct PartialUnixConfig {
pub socket_path: Option<PathBuf>,
}
impl Default for PartialUnixConfig {
fn default() -> Self {
todo!()
}
}
impl From<UnixConfig> for PartialUnixConfig {
fn from(source: UnixConfig) -> Self {
Self {
@ -33,19 +39,4 @@ impl From<UnixConfig> for PartialUnixConfig {
}
}
impl PartialConfig for PartialUnixConfig {
fn empty() -> Self {
Self { socket_path: None }
}
fn default() -> Self {
todo!()
}
fn is_empty(&self) -> bool {
self.socket_path.is_none()
}
fn merge(&mut self, other: Self) {
if let Some(x) = other.socket_path {
self.socket_path = Some(x);
};
}
}

View file

@ -1,6 +1,5 @@
use std::{path::PathBuf, sync::LazyLock};
use lazy_supplements_core::config::PartialCoreConfig;
pub use lazy_supplements_core::global::*;
pub static DEFAULT_DATA_DIR_PATH: LazyLock<PathBuf> = LazyLock::new(|| {
@ -28,123 +27,3 @@ pub static DEFAULT_CONFIG_FILE_PATH: LazyLock<PathBuf> = LazyLock::new(|| {
pub static DEFAULT_DATABASE_FILE_PATH: LazyLock<PathBuf> = LazyLock::new(|| {
DEFAULT_DATA_DIR_PATH.join(&*DEFAULT_DATABASE_FILE_NAME)
});
pub static DEFAULT_PARTIAL_CORE_CONFIG: LazyLock<PartialCoreConfig> = LazyLock::new(|| {
PartialCoreConfig {
secret: None,
listen_ips: Some(DEFAULT_LISTEN_IPS.to_vec()),
port: Some(0),
}
});
pub struct Global {
pub p2p_config: OnceCell<P2pConfig>,
pub main_database: OnceCell<DatabaseConnection>,
pub cache_database: OnceCell<DatabaseConnection>,
pub peers: OnceCell<RwLock<HashMap<PeerId, Multiaddr>>>,
}
impl Global {
pub fn get_p2p_config(&self) -> Option<&P2pConfig> {
self.p2p_config.get()
}
pub async fn get_or_init_p2p_config(&self, config: P2pConfig) -> &P2pConfig {
self.p2p_config.get_or_init(|| async {config}).await
}
pub async fn get_or_init_peers(&self) -> &RwLock<HashMap<PeerId, Multiaddr>> {
self.peers.get_or_init(|| async {
RwLock::new(HashMap::new())
}).await
}
pub async fn read_peers(&self) -> tokio::sync::RwLockReadGuard<'_, HashMap<PeerId, Multiaddr>>{
self.get_or_init_peers().await.read().await
}
pub async fn write_peers(&self) -> tokio::sync::RwLockWriteGuard<'_, HashMap<PeerId, Multiaddr>>{
self.get_or_init_peers().await.write().await
}
pub async fn launch_swarm(&self) -> Result<(), Error> {
let mut swarm = self.get_p2p_config().unwrap().clone().try_into_swarm().await?;
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;
},
_ => {}
}
});
}
}
}
impl GlobalDatabase for Global {
fn get_main_database(&self) -> Option<&DatabaseConnection> {
self.main_database.get()
}
async fn get_or_try_init_main_database<T, U>(&self, path: T, _: U) -> Result<&DatabaseConnection, Error>
where
T: AsRef<Path>,
U: MigratorTrait,
{
let url = "sqlite://".to_string() + path.as_ref().to_str().unwrap() + "?mode=rwc";
Ok(self.main_database.get_or_try_init(|| async {
let db = Database::connect(&url).await?;
U::up(&db, None).await?;
Ok::<DatabaseConnection, DbErr>(db)
}).await?)
}
fn get_cache_database(&self) -> Option<&DatabaseConnection> {
self.cache_database.get()
}
async fn get_or_try_init_cache_database<T, U>(&self, path: T, _: U) -> Result<&DatabaseConnection, Error>
where
T: AsRef<Path>,
U: MigratorTrait,
{
let url = "sqlite://".to_string() + path.as_ref().to_str().unwrap() + "?mode=rwc";
Ok(self.cache_database.get_or_try_init(|| async {
let db = Database::connect(&url).await?;
U::up(&db, None).await?;
Ok::<DatabaseConnection, DbErr>(db)
}).await?)
}
}
#[cfg(test)]
pub use tests::{get_or_init_temporary_main_database, get_or_init_temporary_cache_database};
#[cfg(test)]
pub mod tests {
use std::sync::LazyLock;
use sea_orm_migration::MigratorTrait;
use crate::{global::GLOBAL, cache::migration::CacheMigrator, data::migration::MainMigrator};
use super::*;
pub async fn get_or_init_temporary_main_database() -> &'static DatabaseConnection {
GLOBAL.get_or_try_init_temporary_main_database(MainMigrator).await.unwrap()
}
pub async fn get_or_init_temporary_cache_database() -> &'static DatabaseConnection {
GLOBAL.get_or_try_init_temporary_cache_database(CacheMigrator).await.unwrap()
}
#[tokio::test]
async fn connect_main_database () {
let db = get_or_init_temporary_main_database().await;
assert!(db.ping().await.is_ok());
}
#[tokio::test]
async fn connect_cache_database () {
let db = get_or_init_temporary_cache_database().await;
assert!(db.ping().await.is_ok());
}
}

View file

@ -2,16 +2,3 @@ mod response;
mod request;
pub use response::*;
pub use request::*;
pub trait IpcRequest {
type IpcResponse: IpcResponse<IpcRequest = Self>;
async fn try_into_p2p_response(&self) -> Result<IpcResponse, Error> {
}
}
pub trait IpcResponse {
type IpcRequest: IpcRequest<IpcResponse = Self>;
async fn try_from_ipc_request(&self) -> Result<IpcRequest, Error>;
}

View file

@ -1,27 +0,0 @@
use clap::{Parser, Subcommand};
use lazy_supplements_desktop::{cli::{ConfigArgs, NodeArgs, NodeCommand, ServerArgs}, global::{Global, GLOBAL}, *};
#[derive(Debug, Parser)]
struct Cli {
#[command(subcommand)]
command: Command,
#[command(flatten)]
pub config: ConfigArgs,
}
#[derive(Debug, Subcommand)]
enum Command {
Node(NodeArgs),
Server(ServerArgs),
}
#[tokio::main]
async fn main() {
let cli = Cli::parse();
let _ = GLOBAL.get_or_init_core_config(cli.config.try_into_core_config().await.unwrap()).await;
match cli.command {
Command::Node(x) => x.run().await.unwrap(),
Command::Server(x) => x.start_server().await.unwrap(),
}
}