Add desktop config
This commit is contained in:
parent
1854e84949
commit
66be78dabf
19 changed files with 291 additions and 122 deletions
|
@ -11,6 +11,7 @@ repository = "https://forgejo.fireturlte.net/lazy-supplements"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
ciborium = "0.2.2"
|
ciborium = "0.2.2"
|
||||||
|
clap = { version = "4.5.38", features = ["derive"] }
|
||||||
dioxus = { version = "0.6.0", features = [] }
|
dioxus = { version = "0.6.0", features = [] }
|
||||||
lazy-supplements-core.path = "lazy-supplements-core"
|
lazy-supplements-core.path = "lazy-supplements-core"
|
||||||
libp2p = { version = "0.55.0", features = ["macros", "mdns", "noise", "ping", "tcp", "tokio", "yamux" ] }
|
libp2p = { version = "0.55.0", features = ["macros", "mdns", "noise", "ping", "tcp", "tokio", "yamux" ] }
|
||||||
|
|
|
@ -8,12 +8,15 @@ repository.workspace = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
desktop = ["dep:clap"]
|
||||||
test = ["dep:tempfile"]
|
test = ["dep:tempfile"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
base64 = "0.22.1"
|
base64 = "0.22.1"
|
||||||
chrono = "0.4.41"
|
chrono = "0.4.41"
|
||||||
chrono-tz = "0.10.3"
|
chrono-tz = "0.10.3"
|
||||||
|
ciborium.workspace = true
|
||||||
|
clap = {workspace = true, optional = true}
|
||||||
futures = "0.3.31"
|
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"] }
|
||||||
|
|
|
@ -1,20 +1,25 @@
|
||||||
use std::{net::IpAddr, ops, path::{Path, PathBuf}};
|
use std::{net::IpAddr, ops, path::{Path, PathBuf}};
|
||||||
|
|
||||||
use base64::{prelude::BASE64_STANDARD, Engine};
|
use base64::{prelude::BASE64_STANDARD, Engine};
|
||||||
|
#[cfg(feature="desktop")]
|
||||||
|
use clap::Args;
|
||||||
use libp2p::{identity::{self, DecodingError, Keypair}, noise, ping, tcp, yamux, Swarm};
|
use libp2p::{identity::{self, DecodingError, Keypair}, noise, ping, tcp, yamux, Swarm};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tokio::{fs::File, io::{AsyncReadExt, AsyncWriteExt}};
|
use tokio::{fs::File, io::{AsyncReadExt, AsyncWriteExt}};
|
||||||
use tracing_subscriber::EnvFilter;
|
use tracing_subscriber::EnvFilter;
|
||||||
|
|
||||||
|
|
||||||
use crate::{error::Error, p2p};
|
use crate::{
|
||||||
|
config::PartialConfig,
|
||||||
|
error::Error, p2p
|
||||||
|
};
|
||||||
|
|
||||||
use super::{PartialConfig};
|
fn keypair_to_base64(keypair: &Keypair) -> String {
|
||||||
|
let vec = match keypair.to_protobuf_encoding() {
|
||||||
fn keypair_to_base64(keypair: &Keypair) -> Result<String, Error> {
|
Ok(x) => x,
|
||||||
let vec = keypair.to_protobuf_encoding()?;
|
Err(_) => unreachable!(),
|
||||||
let base64 = BASE64_STANDARD.encode(vec);
|
};
|
||||||
Ok(base64)
|
BASE64_STANDARD.encode(vec)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn base64_to_keypair(base64: &str) -> Result<Keypair, Error> {
|
fn base64_to_keypair(base64: &str) -> Result<Keypair, Error> {
|
||||||
|
@ -23,15 +28,14 @@ fn base64_to_keypair(base64: &str) -> Result<Keypair, Error> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
pub struct NodeConfig {
|
pub struct CoreConfig {
|
||||||
#[serde(with = "keypair_parser")]
|
#[serde(with = "keypair_parser")]
|
||||||
pub secret: Keypair,
|
pub secret: Keypair,
|
||||||
pub database_path: PathBuf,
|
|
||||||
pub listen_ips: Vec<IpAddr>,
|
pub listen_ips: Vec<IpAddr>,
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NodeConfig {
|
impl CoreConfig {
|
||||||
pub async fn try_into_swarm (self) -> Result<Swarm<p2p::Behaviour>, Error> {
|
pub async fn try_into_swarm (self) -> Result<Swarm<p2p::Behaviour>, Error> {
|
||||||
let mut swarm = libp2p::SwarmBuilder::with_existing_identity(self.secret)
|
let mut swarm = libp2p::SwarmBuilder::with_existing_identity(self.secret)
|
||||||
.with_tokio()
|
.with_tokio()
|
||||||
|
@ -47,12 +51,11 @@ impl NodeConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<RawNodeConfig> for NodeConfig {
|
impl TryFrom<PartialCoreConfig> for CoreConfig {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
fn try_from(raw: RawNodeConfig) -> Result<NodeConfig, Self::Error> {
|
fn try_from(raw: PartialCoreConfig) -> Result<CoreConfig, Self::Error> {
|
||||||
Ok(NodeConfig {
|
Ok(CoreConfig {
|
||||||
secret: base64_to_keypair(&raw.secret.ok_or(Error::MissingConfig("secret"))?)?,
|
secret: base64_to_keypair(&raw.secret.ok_or(Error::MissingConfig("secret"))?)?,
|
||||||
database_path: raw.database_path.ok_or(Error::MissingConfig("database_path"))?,
|
|
||||||
listen_ips: raw.listen_ips.ok_or(Error::MissingConfig("listen_ips"))?,
|
listen_ips: raw.listen_ips.ok_or(Error::MissingConfig("listen_ips"))?,
|
||||||
port: raw.port.ok_or(Error::MissingConfig("port"))?
|
port: raw.port.ok_or(Error::MissingConfig("port"))?
|
||||||
})
|
})
|
||||||
|
@ -66,10 +69,7 @@ mod keypair_parser {
|
||||||
pub fn serialize<S>(keypair: &Keypair, serializer: S) -> Result<S::Ok, S::Error>
|
pub fn serialize<S>(keypair: &Keypair, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where S: Serializer
|
where S: Serializer
|
||||||
{
|
{
|
||||||
match super::keypair_to_base64(keypair) {
|
serializer.serialize_str(&super::keypair_to_base64(keypair))
|
||||||
Ok(x) => serializer.serialize_str(&x),
|
|
||||||
Err(_) => Err(serde::ser::Error::custom("Decoding keypair error"))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Keypair, D::Error>
|
pub fn deserialize<'de, D>(deserializer: D) -> Result<Keypair, D::Error>
|
||||||
where D: Deserializer<'de>
|
where D: Deserializer<'de>
|
||||||
|
@ -82,35 +82,28 @@ mod keypair_parser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature="desktop",derive(Args))]
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
pub struct RawNodeConfig {
|
pub struct PartialCoreConfig {
|
||||||
|
#[cfg_attr(feature="desktop",arg(long))]
|
||||||
pub secret: Option<String>,
|
pub secret: Option<String>,
|
||||||
pub database_path: Option<PathBuf>,
|
#[cfg_attr(feature="desktop",arg(long))]
|
||||||
pub listen_ips: Option<Vec<IpAddr>>,
|
pub listen_ips: Option<Vec<IpAddr>>,
|
||||||
|
#[cfg_attr(feature="desktop",arg(long))]
|
||||||
pub port: Option<u16>,
|
pub port: Option<u16>,
|
||||||
}
|
}
|
||||||
impl RawNodeConfig {
|
impl PartialCoreConfig {
|
||||||
|
|
||||||
pub fn with_new_secret(mut self) -> Self {
|
pub fn with_new_secret(mut self) -> Self {
|
||||||
self.secret = Some(keypair_to_base64(&Keypair::generate_ed25519()).unwrap());
|
self.secret = Some(keypair_to_base64(&Keypair::generate_ed25519()));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new() -> Self {
|
|
||||||
RawNodeConfig {
|
|
||||||
secret: None,
|
|
||||||
database_path: None,
|
|
||||||
listen_ips: None,
|
|
||||||
port: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn read_or_create<T>(path: T) -> Result<Self, Error>
|
pub async fn read_or_create<T>(path: T) -> Result<Self, Error>
|
||||||
where
|
where
|
||||||
T: AsRef<Path>
|
T: AsRef<Path>
|
||||||
{
|
{
|
||||||
if !path.as_ref().exists() {
|
if !path.as_ref().exists() {
|
||||||
Self::new().write_to(&path).await?;
|
Self::empty().write_to(&path).await?;
|
||||||
}
|
}
|
||||||
Self::read_from(&path).await
|
Self::read_from(&path).await
|
||||||
}
|
}
|
||||||
|
@ -121,7 +114,7 @@ impl RawNodeConfig {
|
||||||
let mut file = File::open(path.as_ref()).await?;
|
let mut file = File::open(path.as_ref()).await?;
|
||||||
let mut content = String::new();
|
let mut content = String::new();
|
||||||
file.read_to_string(&mut content).await?;
|
file.read_to_string(&mut content).await?;
|
||||||
let config: RawNodeConfig = toml::from_str(&content)?;
|
let config: Self = toml::from_str(&content)?;
|
||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
pub async fn write_to<T>(&self, path:T) -> Result<(), Error>
|
pub async fn write_to<T>(&self, path:T) -> Result<(), Error>
|
||||||
|
@ -138,14 +131,29 @@ impl RawNodeConfig {
|
||||||
file.write_all(toml::to_string(self)?.as_bytes()).await?;
|
file.write_all(toml::to_string(self)?.as_bytes()).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn merge(&mut self, another: RawNodeConfig) {
|
impl From<CoreConfig> for PartialCoreConfig {
|
||||||
|
fn from(config: CoreConfig) -> Self {
|
||||||
|
Self {
|
||||||
|
secret: Some(keypair_to_base64(&config.secret)),
|
||||||
|
listen_ips: Some(config.listen_ips),
|
||||||
|
port: Some(config.port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl PartialConfig<CoreConfig> for PartialCoreConfig {
|
||||||
|
fn empty() -> Self {
|
||||||
|
Self {
|
||||||
|
secret: None,
|
||||||
|
listen_ips: None,
|
||||||
|
port: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn merge(&mut self, another: Self) {
|
||||||
if let Some(x) = another.secret {
|
if let Some(x) = another.secret {
|
||||||
self.secret = Some(x);
|
self.secret = Some(x);
|
||||||
};
|
};
|
||||||
if let Some(x) = another.database_path {
|
|
||||||
self.database_path = Some(x);
|
|
||||||
};
|
|
||||||
if let Some(x) = another.listen_ips {
|
if let Some(x) = another.listen_ips {
|
||||||
self.listen_ips = Some(x);
|
self.listen_ips = Some(x);
|
||||||
};
|
};
|
||||||
|
@ -153,18 +161,12 @@ impl RawNodeConfig {
|
||||||
self.port = Some(x);
|
self.port = Some(x);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
fn default() -> Self {
|
||||||
impl ops::Add<RawNodeConfig> for RawNodeConfig {
|
todo!()
|
||||||
type Output = RawNodeConfig;
|
|
||||||
fn add(mut self, another: RawNodeConfig) -> RawNodeConfig {
|
|
||||||
self.merge(another);
|
|
||||||
self
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use libp2p::identity;
|
use libp2p::identity;
|
||||||
|
@ -174,7 +176,7 @@ mod tests {
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn parse_keypair() {
|
async fn parse_keypair() {
|
||||||
let keypair = identity::Keypair::generate_ed25519();
|
let keypair = identity::Keypair::generate_ed25519();
|
||||||
let keypair2 = base64_to_keypair(&keypair_to_base64(&keypair).unwrap()).unwrap();
|
let keypair2 = base64_to_keypair(&keypair_to_base64(&keypair)).unwrap();
|
||||||
|
|
||||||
assert_eq!(keypair.public(), keypair2.public());
|
assert_eq!(keypair.public(), keypair2.public());
|
||||||
}
|
}
|
5
lazy-supplements-core/src/config/error.rs
Normal file
5
lazy-supplements-core/src/config/error.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
pub enum ConfigError {
|
||||||
|
#[error("missing config: {0}")]
|
||||||
|
MissingConfig(String),
|
||||||
|
}
|
|
@ -1,14 +1,55 @@
|
||||||
mod node;
|
pub mod error;
|
||||||
|
mod core;
|
||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
pub use node::{ NodeConfig, RawNodeConfig };
|
pub use core::{ CoreConfig, PartialCoreConfig };
|
||||||
use serde::{Deserialize, Serialize};
|
pub use error::ConfigError;
|
||||||
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
|
|
||||||
use tokio::{fs::File, io::{AsyncReadExt, AsyncWriteExt}};
|
use tokio::{fs::File, io::{AsyncReadExt, AsyncWriteExt}};
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
|
||||||
pub struct PartialConfig {
|
pub trait PartialConfig<T>: From<T>
|
||||||
node: Option<NodeConfig>,
|
where T: TryFrom<Self> {
|
||||||
|
fn default() -> Self;
|
||||||
|
fn empty() -> Self;
|
||||||
|
fn merge(&mut self, other: Self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait ConfigFile: DeserializeOwned + Serialize {
|
||||||
|
fn new() -> Self;
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
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: Self = toml::from_str(&content)?;
|
||||||
|
Ok(config)
|
||||||
|
}
|
||||||
|
async fn write_to<T>(&self, path:T) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
T: AsRef<Path>
|
||||||
|
{
|
||||||
|
if !path.as_ref().exists() {
|
||||||
|
if let Some(x) = path.as_ref().parent() {
|
||||||
|
std::fs::create_dir_all(x)?;
|
||||||
|
};
|
||||||
|
let _ = File::create(&path).await?;
|
||||||
|
}
|
||||||
|
let mut file = File::create(&path).await?;
|
||||||
|
file.write_all(toml::to_string(self)?.as_bytes()).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,10 @@
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("Base64 decode error: {0}")]
|
#[error("Base64 decode error: {0}")]
|
||||||
Base64Decode(#[from] base64::DecodeError),
|
Base64Decode(#[from] base64::DecodeError),
|
||||||
|
#[error(transparent)]
|
||||||
|
CiborDeserialize(#[from] ciborium::de::Error<std::io::Error>),
|
||||||
|
#[error(transparent)]
|
||||||
|
CiborSerialize(#[from] ciborium::ser::Error<std::io::Error>),
|
||||||
#[error("DB Error: {0}")]
|
#[error("DB Error: {0}")]
|
||||||
Db(#[from]sea_orm::DbErr),
|
Db(#[from]sea_orm::DbErr),
|
||||||
#[error("Dial Error: {0}")]
|
#[error("Dial Error: {0}")]
|
||||||
|
@ -18,10 +22,13 @@ pub enum Error {
|
||||||
Multiaddr(#[from] libp2p::multiaddr::Error),
|
Multiaddr(#[from] libp2p::multiaddr::Error),
|
||||||
#[error("Noise error: {0}")]
|
#[error("Noise error: {0}")]
|
||||||
Noise(#[from] libp2p::noise::Error),
|
Noise(#[from] libp2p::noise::Error),
|
||||||
|
#[cfg(feature="desktop")]
|
||||||
|
#[error("Parse args error: {0}")]
|
||||||
|
ParseCommand(#[from] clap::Error),
|
||||||
#[error("toml deserialization error: {0}")]
|
#[error("toml deserialization error: {0}")]
|
||||||
TomlDe(#[from] toml::de::Error),
|
TomlDe(#[from] toml::de::Error),
|
||||||
#[error("toml serialization error: {0}")]
|
#[error("toml serialization error: {0}")]
|
||||||
TomlSer(#[from] toml::ser::Error),
|
TomlSer(#[from] toml::ser::Error),
|
||||||
#[error("Transport error: {0}")]
|
#[error("Transport error: {0}")]
|
||||||
Transport(#[from]libp2p::TransportError<std::io::Error>)
|
Transport(#[from]libp2p::TransportError<std::io::Error>)
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{collections::HashMap, net::{IpAddr, Ipv4Addr}, path::{Path, PathBuf}, sync::LazyLock};
|
use std::{collections::HashMap, net::{IpAddr, Ipv4Addr}, path::{Path, PathBuf}, sync::LazyLock};
|
||||||
|
|
||||||
use crate::{config::{NodeConfig, RawNodeConfig}, error::Error};
|
use crate::{config::{CoreConfig, PartialCoreConfig}, error::Error};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use libp2p::{swarm::SwarmEvent, Multiaddr, PeerId};
|
use libp2p::{swarm::SwarmEvent, Multiaddr, PeerId};
|
||||||
use sea_orm::{prelude::*, Database};
|
use sea_orm::{prelude::*, Database};
|
||||||
|
@ -33,25 +33,25 @@ pub static DEFAULT_DATABASE_FILE_NAME: LazyLock<PathBuf> = LazyLock::new(|| {
|
||||||
|
|
||||||
|
|
||||||
pub static GLOBAL: Global = Global{
|
pub static GLOBAL: Global = Global{
|
||||||
node_config: OnceCell::const_new(),
|
core_config: OnceCell::const_new(),
|
||||||
main_database: OnceCell::const_new(),
|
main_database: OnceCell::const_new(),
|
||||||
cache_database: OnceCell::const_new(),
|
cache_database: OnceCell::const_new(),
|
||||||
peers: OnceCell::const_new(),
|
peers: OnceCell::const_new(),
|
||||||
|
|
||||||
};
|
};
|
||||||
pub struct Global {
|
pub struct Global {
|
||||||
pub node_config: OnceCell<NodeConfig>,
|
pub core_config: OnceCell<CoreConfig>,
|
||||||
pub main_database: OnceCell<DatabaseConnection>,
|
pub main_database: OnceCell<DatabaseConnection>,
|
||||||
pub cache_database: OnceCell<DatabaseConnection>,
|
pub cache_database: OnceCell<DatabaseConnection>,
|
||||||
pub peers: OnceCell<RwLock<HashMap<PeerId, Multiaddr>>>,
|
pub peers: OnceCell<RwLock<HashMap<PeerId, Multiaddr>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Global {
|
impl Global {
|
||||||
pub fn get_node_config(&self) -> Option<&NodeConfig> {
|
pub fn get_core_config(&self) -> Option<&CoreConfig> {
|
||||||
self.node_config.get()
|
self.core_config.get()
|
||||||
}
|
}
|
||||||
pub async fn get_or_init_node_config(&self, config: NodeConfig) -> &NodeConfig {
|
pub async fn get_or_init_core_config(&self, config: CoreConfig) -> &CoreConfig {
|
||||||
self.node_config.get_or_init(|| async {config}).await
|
self.core_config.get_or_init(|| async {config}).await
|
||||||
}
|
}
|
||||||
pub async fn get_or_init_peers(&self) -> &RwLock<HashMap<PeerId, Multiaddr>> {
|
pub async fn get_or_init_peers(&self) -> &RwLock<HashMap<PeerId, Multiaddr>> {
|
||||||
self.peers.get_or_init(|| async {
|
self.peers.get_or_init(|| async {
|
||||||
|
@ -65,7 +65,7 @@ impl Global {
|
||||||
self.get_or_init_peers().await.write().await
|
self.get_or_init_peers().await.write().await
|
||||||
}
|
}
|
||||||
pub async fn launch_swarm(&self) -> Result<(), Error> {
|
pub async fn launch_swarm(&self) -> Result<(), Error> {
|
||||||
let mut swarm = self.get_node_config().unwrap().clone().try_into_swarm().await?;
|
let mut swarm = self.get_core_config().unwrap().clone().try_into_swarm().await?;
|
||||||
loop{
|
loop{
|
||||||
let swarm_event = swarm.select_next_some().await;
|
let swarm_event = swarm.select_next_some().await;
|
||||||
tokio::spawn(async move{
|
tokio::spawn(async move{
|
||||||
|
|
|
@ -12,9 +12,9 @@ test = ["lazy-supplements-core/test"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ciborium.workspace = true
|
ciborium.workspace = true
|
||||||
clap = { version = "4.5.38", features = ["derive"] }
|
clap.workspace = true
|
||||||
dirs = "6.0.0"
|
dirs = "6.0.0"
|
||||||
lazy-supplements-core.workspace = true
|
lazy-supplements-core = { workspace = true, features = ["desktop"] }
|
||||||
libp2p.workspace = true
|
libp2p.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
|
|
|
@ -1,19 +1,22 @@
|
||||||
use std::{net::IpAddr, path::PathBuf};
|
use std::{net::IpAddr, path::PathBuf};
|
||||||
|
|
||||||
use clap::Args;
|
use clap::Args;
|
||||||
use lazy_supplements_core::config::RawNodeConfig;
|
use lazy_supplements_core::config::{PartialConfig, PartialCoreConfig};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{config::NodeConfig, error::Error, global::{DEFAULT_CONFIG_FILE_PATH, DEFAULT_RAW_NODE_CONFIG}};
|
use crate::{config::{desktop::PartialDesktopConfig, CoreConfig}, error::Error, global::{DEFAULT_CONFIG_FILE_PATH, DEFAULT_PARTIAL_CORE_CONFIG,}};
|
||||||
|
|
||||||
#[derive(Args, Clone, Debug)]
|
#[derive(Args, Clone, Debug)]
|
||||||
pub struct ConfigArgs {
|
pub struct ConfigArgs {
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub config: Option<PathBuf>,
|
pub config: Option<PathBuf>,
|
||||||
#[command(flatten)]
|
#[command(flatten)]
|
||||||
pub config_values: ConfigValueArgs,
|
pub core_config: PartialCoreConfig,
|
||||||
|
#[command(flatten)]
|
||||||
|
pub desktop_config: PartialDesktopConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl ConfigArgs {
|
impl ConfigArgs {
|
||||||
pub fn get_config_path_or_default(&self) -> PathBuf {
|
pub fn get_config_path_or_default(&self) -> PathBuf {
|
||||||
if let Some(x) = self.config.as_ref() {
|
if let Some(x) = self.config.as_ref() {
|
||||||
|
@ -22,33 +25,14 @@ impl ConfigArgs {
|
||||||
DEFAULT_CONFIG_FILE_PATH.to_path_buf()
|
DEFAULT_CONFIG_FILE_PATH.to_path_buf()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub async fn try_into_raw_node_config(self) -> Result<RawNodeConfig, Error> {
|
pub async fn try_into_partial_core_config(self) -> Result<PartialCoreConfig, Error> {
|
||||||
Ok(RawNodeConfig::read_from(self.get_config_path_or_default()).await? + self.config_values.into())
|
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_node_config(self) -> Result<NodeConfig, Error> {
|
pub async fn try_into_core_config(self) -> Result<CoreConfig, Error> {
|
||||||
Ok((DEFAULT_RAW_NODE_CONFIG.clone() + self.try_into_raw_node_config().await?).try_into()?)
|
let mut config = DEFAULT_PARTIAL_CORE_CONFIG.clone();
|
||||||
|
config.merge(self.try_into_partial_core_config().await?);
|
||||||
|
config.try_into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Args, Clone, Debug, Deserialize, Serialize)]
|
|
||||||
pub struct ConfigValueArgs {
|
|
||||||
#[arg(skip)]
|
|
||||||
pub secret: Option<String>,
|
|
||||||
#[arg(long)]
|
|
||||||
pub database_path: Option<PathBuf>,
|
|
||||||
#[arg(long)]
|
|
||||||
pub listen_ips: Option<Vec<IpAddr>>,
|
|
||||||
#[arg(long)]
|
|
||||||
pub port: Option<u16>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<RawNodeConfig> for ConfigValueArgs {
|
|
||||||
fn into(self) -> RawNodeConfig {
|
|
||||||
RawNodeConfig {
|
|
||||||
secret : self.secret,
|
|
||||||
database_path: self.database_path,
|
|
||||||
listen_ips: self.listen_ips,
|
|
||||||
port: self.port
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,7 +5,7 @@ use libp2p::{
|
||||||
multiaddr::Protocol, noise, ping, swarm::SwarmEvent, tcp, yamux, Multiaddr, PeerId
|
multiaddr::Protocol, noise, ping, swarm::SwarmEvent, tcp, yamux, Multiaddr, PeerId
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{cli::ServerArgs, error::{CoreError, DesktopError, Error}};
|
use crate::{cli::ServerArgs, error::Error};
|
||||||
|
|
||||||
use super::ConfigArgs;
|
use super::ConfigArgs;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use clap::Args;
|
use clap::Args;
|
||||||
use libp2p::{noise, ping, swarm::{NetworkBehaviour, SwarmEvent}, tcp, yamux, Swarm};
|
use libp2p::{noise, ping, swarm::{NetworkBehaviour, SwarmEvent}, tcp, yamux, Swarm};
|
||||||
|
|
||||||
use crate::{error::Error, global::GLOBAL, error::CoreError};
|
use crate::{error::Error, global::GLOBAL};
|
||||||
|
|
||||||
use super::ConfigArgs;
|
use super::ConfigArgs;
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ pub struct ServerArgs {
|
||||||
}
|
}
|
||||||
impl ServerArgs {
|
impl ServerArgs {
|
||||||
pub async fn start_server(self) -> Result<(), Error>{
|
pub async fn start_server(self) -> Result<(), Error>{
|
||||||
let _ = crate::global::GLOBAL.get_or_init_node_config(self.config.try_into_node_config().await?).await;
|
let _ = crate::global::GLOBAL.get_or_init_core_config(self.config.try_into_core_config().await?).await;
|
||||||
GLOBAL.launch_swarm().await.or_else(|e| {Err(Error::from(CoreError::from(e)))})
|
GLOBAL.launch_swarm().await
|
||||||
}
|
}
|
||||||
}
|
}
|
77
lazy-supplements-desktop/src/config/desktop.rs
Normal file
77
lazy-supplements-desktop/src/config/desktop.rs
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use clap::Args;
|
||||||
|
use lazy_supplements_core::config::{ConfigError, PartialConfig};
|
||||||
|
use libp2p::mdns::Config;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
pub struct DesktopConfig {
|
||||||
|
pub data_directory: PathBuf,
|
||||||
|
pub data_database: PathBuf,
|
||||||
|
pub cache_directory: PathBuf,
|
||||||
|
pub cache_database: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<PartialDesktopConfig> for DesktopConfig {
|
||||||
|
type Error = ConfigError;
|
||||||
|
|
||||||
|
fn try_from(value: PartialDesktopConfig) -> Result<Self, Self::Error> {
|
||||||
|
Ok(Self {
|
||||||
|
data_directory: value.data_directory.ok_or(ConfigError::MissingConfig("data_directory".to_string()))?,
|
||||||
|
data_database: value.data_database.ok_or(ConfigError::MissingConfig("data_database".to_string()))?,
|
||||||
|
cache_directory: value.cache_directory.ok_or(ConfigError::MissingConfig("cache_directory".to_string()))?,
|
||||||
|
cache_database: value.cache_database.ok_or(ConfigError::MissingConfig("cache_database".to_string()))?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args, Clone, Debug, Deserialize, Serialize)]
|
||||||
|
pub struct PartialDesktopConfig {
|
||||||
|
#[arg(long)]
|
||||||
|
pub data_directory: Option<PathBuf>,
|
||||||
|
#[arg(long)]
|
||||||
|
pub data_database: Option<PathBuf>,
|
||||||
|
#[arg(long)]
|
||||||
|
pub cache_directory: Option<PathBuf>,
|
||||||
|
#[arg(long)]
|
||||||
|
pub cache_database: Option<PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DesktopConfig> for PartialDesktopConfig {
|
||||||
|
fn from(config: DesktopConfig) -> PartialDesktopConfig {
|
||||||
|
Self {
|
||||||
|
data_database: Some(config.data_database),
|
||||||
|
data_directory: Some(config.data_directory),
|
||||||
|
cache_database: Some(config.cache_database),
|
||||||
|
cache_directory: Some(config.cache_directory),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialConfig<DesktopConfig> for PartialDesktopConfig {
|
||||||
|
fn empty() -> Self {
|
||||||
|
Self{
|
||||||
|
data_database: None,
|
||||||
|
cache_database: None,
|
||||||
|
data_directory: None,
|
||||||
|
cache_directory: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn default() -> Self {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn merge(&mut self, other: Self) {
|
||||||
|
if let Some(x) = other.data_directory {
|
||||||
|
self.data_directory = Some(x);
|
||||||
|
}
|
||||||
|
if let Some(x) = other.data_database {
|
||||||
|
self.data_database = Some(x);
|
||||||
|
}
|
||||||
|
if let Some(x) = other.cache_directory {
|
||||||
|
self.cache_directory = Some(x);
|
||||||
|
}
|
||||||
|
if let Some(x) = other.cache_database {
|
||||||
|
self.cache_database = Some(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
lazy-supplements-desktop/src/config/mod.rs
Normal file
15
lazy-supplements-desktop/src/config/mod.rs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#[cfg(unix)]
|
||||||
|
pub mod unix;
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
pub mod windows;
|
||||||
|
pub mod desktop;
|
||||||
|
pub use lazy_supplements_core::config::*;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
pub use unix::*;
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
pub use windows::*;
|
48
lazy-supplements-desktop/src/config/unix.rs
Normal file
48
lazy-supplements-desktop/src/config/unix.rs
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use clap::Args;
|
||||||
|
use lazy_supplements_core::config::PartialConfig;
|
||||||
|
use libp2p::mdns::Config;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::config::error::ConfigError;
|
||||||
|
|
||||||
|
|
||||||
|
pub struct UnixConfig {
|
||||||
|
pub socket_path: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<PartialUnixConfig> for UnixConfig {
|
||||||
|
type Error = ConfigError;
|
||||||
|
fn try_from(config: PartialUnixConfig) -> Result<Self, Self::Error> {
|
||||||
|
Ok(Self{
|
||||||
|
socket_path: config.socket_path.ok_or(ConfigError::MissingConfig("socket_path".to_string()))?
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Args, Clone, Debug, Deserialize, Serialize)]
|
||||||
|
pub struct PartialUnixConfig {
|
||||||
|
pub socket_path: Option<PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<UnixConfig> for PartialUnixConfig {
|
||||||
|
fn from(source: UnixConfig) -> Self {
|
||||||
|
Self {
|
||||||
|
socket_path: Some(source.socket_path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialConfig<UnixConfig> for PartialUnixConfig {
|
||||||
|
fn empty() -> Self {
|
||||||
|
Self { socket_path: None }
|
||||||
|
}
|
||||||
|
fn default() -> Self {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn merge(&mut self, other: Self) {
|
||||||
|
if let Some(x) = other.socket_path {
|
||||||
|
self.socket_path = Some(x);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
3
lazy-supplements-desktop/src/config/windows.rs
Normal file
3
lazy-supplements-desktop/src/config/windows.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
pub struct WindowsConfig {
|
||||||
|
pub pipe_name: String
|
||||||
|
}
|
|
@ -1,16 +0,0 @@
|
||||||
pub use lazy_supplements_core::error::Error as CoreError;
|
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
|
||||||
pub enum DesktopError {
|
|
||||||
#[error("Parse args error: {0}")]
|
|
||||||
ParseCommand(#[from] clap::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
|
||||||
pub enum Error {
|
|
||||||
#[error("{0}")]
|
|
||||||
Core(#[from] CoreError),
|
|
||||||
#[error("{0}")]
|
|
||||||
Desktop(#[from] DesktopError),
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{path::PathBuf, sync::LazyLock};
|
use std::{path::PathBuf, sync::LazyLock};
|
||||||
|
|
||||||
use lazy_supplements_core::config::RawNodeConfig;
|
use lazy_supplements_core::config::PartialCoreConfig;
|
||||||
pub use lazy_supplements_core::global::*;
|
pub use lazy_supplements_core::global::*;
|
||||||
|
|
||||||
pub static DEFAULT_DATA_DIR_PATH: LazyLock<PathBuf> = LazyLock::new(|| {
|
pub static DEFAULT_DATA_DIR_PATH: LazyLock<PathBuf> = LazyLock::new(|| {
|
||||||
|
@ -29,10 +29,9 @@ pub static DEFAULT_DATABASE_FILE_PATH: LazyLock<PathBuf> = LazyLock::new(|| {
|
||||||
DEFAULT_DATA_DIR_PATH.join(&*DEFAULT_DATABASE_FILE_NAME)
|
DEFAULT_DATA_DIR_PATH.join(&*DEFAULT_DATABASE_FILE_NAME)
|
||||||
});
|
});
|
||||||
|
|
||||||
pub static DEFAULT_RAW_NODE_CONFIG: LazyLock<RawNodeConfig> = LazyLock::new(|| {
|
pub static DEFAULT_PARTIAL_CORE_CONFIG: LazyLock<PartialCoreConfig> = LazyLock::new(|| {
|
||||||
RawNodeConfig {
|
PartialCoreConfig {
|
||||||
secret: None,
|
secret: None,
|
||||||
database_path: Some(DEFAULT_DATABASE_FILE_PATH.to_path_buf()),
|
|
||||||
listen_ips: Some(DEFAULT_LISTEN_IPS.to_vec()),
|
listen_ips: Some(DEFAULT_LISTEN_IPS.to_vec()),
|
||||||
port: Some(0),
|
port: Some(0),
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
pub mod cli;
|
pub mod cli;
|
||||||
pub mod error;
|
pub mod config;
|
||||||
pub mod global;
|
pub mod global;
|
||||||
pub mod ipc;
|
pub mod ipc;
|
||||||
pub use lazy_supplements_core::{
|
pub use lazy_supplements_core::{
|
||||||
cache,
|
cache,
|
||||||
config,
|
|
||||||
data,
|
data,
|
||||||
|
error,
|
||||||
};
|
};
|
||||||
|
|
|
@ -19,7 +19,7 @@ enum Command {
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
let _ = GLOBAL.get_or_init_node_config(cli.config.try_into_node_config().await.unwrap()).await;
|
let _ = GLOBAL.get_or_init_core_config(cli.config.try_into_core_config().await.unwrap()).await;
|
||||||
match cli.command {
|
match cli.command {
|
||||||
Command::Node(x) => x.run().await.unwrap(),
|
Command::Node(x) => x.run().await.unwrap(),
|
||||||
Command::Server(x) => x.start_server().await.unwrap(),
|
Command::Server(x) => x.start_server().await.unwrap(),
|
||||||
|
|
Loading…
Add table
Reference in a new issue