Split lazy-supplements to core and desktop

This commit is contained in:
fluo10 2025-06-11 08:38:03 +09:00
parent d94a927461
commit f8422d98fa
48 changed files with 219 additions and 524 deletions

View file

@ -1,5 +1,5 @@
[workspace] [workspace]
members = [ "examples/*", "lazy-supplements", "lazy-supplements-*" ] members = [ "lazy-supplements-*" ]
[workspace.package] [workspace.package]
edition = "2024" edition = "2024"
@ -9,8 +9,11 @@ license = "MIT OR Apache-2.0"
repository = "https://forgejo.fireturlte.net" repository = "https://forgejo.fireturlte.net"
[workspace.dependencies] [workspace.dependencies]
lazy-supplements.path = "lazy-supplements" 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" ] }
serde = { version = "1.0.219", features = ["derive"] }
thiserror = "2.0.12"
tokio = { version = "1.45.0", features = ["macros", "rt", "rt-multi-thread"] }
[workspace.dependencies.sea-orm-migration] [workspace.dependencies.sea-orm-migration]
version = "1.1.0" version = "1.1.0"

View file

@ -1,19 +0,0 @@
[package]
name = "simple-list"
edition.workspace = true
version.workspace = true
description.workspace = true
license.workspace = true
repository.workspace = true
[dependencies]
chrono = "0.4.41"
clap = { version = "4.5.38", features = ["derive"] }
lazy-supplements.workspace = true
sea-orm = "1.1.11"
sea-orm-migration.workspace = true
serde = { version = "1.0.219", features = ["derive"] }
tokio = "1.45.1"
[dev-dependencies]
lazy-supplements = { workspace = true, features = [ "test" ] }

View file

@ -1,16 +0,0 @@
use clap::{Parser, Subcommand};
use lazy_supplements::{cli::ServerArgs, config::PartialServerConfig};
#[derive(Debug, Parser)]
pub struct Cli {
#[command(subcommand)]
command: CliCommand
}
#[derive(Debug, Subcommand)]
pub enum CliCommand {
Add,
Delete,
List,
Server(ServerArgs)
}

View file

@ -1,58 +0,0 @@
use chrono::Local;
use sea_orm::entity::{
*,
prelude::*
};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Serialize, Deserialize)]
#[sea_orm(table_name = "list_item")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: Uuid,
#[sea_orm(indexed)]
pub created_at: DateTimeUtc,
#[sea_orm(indexed)]
pub updated_at: DateTimeUtc,
#[sea_orm(indexed)]
pub is_trashed: bool,
#[sea_orm(indexed)]
pub content: String,
}
#[derive(Copy, Clone, Debug, DeriveRelation, EnumIter)]
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}
impl ActiveModel {
pub fn new() -> Self {
let timestamp: DateTimeUtc = Local::now().to_utc();
Self{
id: Set(Uuid::new_v4()),
created_at: Set(timestamp),
updated_at: Set(timestamp),
is_trashed: Set(false),
..Default::default()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::global::GLOBAL;
#[tokio::test]
async fn check_insert_node() {
let db = crate::tests::get_or_init_temporary_database().await;
ActiveModel{
content: Set("test note".to_owned()),
..ActiveModel::new()
}.insert(db).await.unwrap();
}
}

View file

@ -1,10 +0,0 @@
mod list_item;
pub use lazy_supplements::entity::*;
pub use list_item::{
ActiveModel as ListItemActiveModel,
Column as ListItemColumn,
Entity as ListItemEntity,
Model as ListItemModel,
};

View file

@ -1,25 +0,0 @@
use clap::Parser;
mod migration;
mod cli;
mod entity;
pub use lazy_supplements::error;
pub use lazy_supplements::global;
#[cfg(test)]
pub mod tests {
use sea_orm::DatabaseConnection;
use sea_orm_migration::MigratorTrait;
use super::*;
pub async fn get_or_init_temporary_database() -> &'static DatabaseConnection {
global::GLOBAL.get_or_try_init_temporary_database(migration::Migrator).await.unwrap()
}
}
#[tokio::main]
async fn main() {
let args = cli::Cli::parse();
println!("{:?}", args);
}

View file

@ -1,50 +0,0 @@
use sea_orm_migration::{prelude::*, schema::*};
use lazy_supplements_migration::TableMigration;
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
ListItem::up(manager).await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
ListItem::down(manager).await?;
Ok(())
}
}
#[derive(DeriveIden)]
enum ListItem {
Table,
Id,
CreatedAt,
UpdatedAt,
IsTrashed,
Content,
}
#[async_trait::async_trait]
impl TableMigration for ListItem {
async fn up<'a>(manager: &'a SchemaManager<'a>) -> Result<(), DbErr> {
manager.create_table(
Table::create()
.table(Self::Table)
.if_not_exists()
.col(pk_uuid(Self::Id))
.col(timestamp(Self::CreatedAt))
.col(timestamp(Self::UpdatedAt))
.col(boolean(Self::IsTrashed))
.col(string_len(Self::Content, 255))
.to_owned()
).await?;
Ok(())
}
async fn down<'a>(manager: &'a SchemaManager<'a>) -> Result<(), DbErr>{
manager.drop_table(Table::drop().table(Self::Table).to_owned()).await
}
}

View file

@ -1,15 +0,0 @@
use sea_orm_migration::{prelude::*, schema::*};
use lazy_supplements_migration::m20220101_000001_create_lazy_supplements_tables;
mod m20220101_000002_create_simple_list_tables;
pub struct Migrator;
#[async_trait::async_trait]
impl MigratorTrait for Migrator {
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
vec![
Box::new(m20220101_000002_create_simple_list_tables::Migration),
Box::new(m20220101_000001_create_lazy_supplements_tables::Migration)
]
}
}

View file

@ -6,4 +6,26 @@ description.workspace = true
license.workspace = true license.workspace = true
repository.workspace = true repository.workspace = true
[features]
default = []
test = ["dep:tempfile"]
[dependencies] [dependencies]
base64 = "0.22.1"
chrono = "0.4.41"
chrono-tz = "0.10.3"
futures = "0.3.31"
libp2p.workspace = true
sea-orm = { version = "1.1.11", features = ["sqlx-sqlite", "runtime-tokio-native-tls", "macros", "with-chrono", "with-uuid"] }
sea-orm-migration.workspace = true
serde.workspace = true
tempfile = { version = "3.20.0", optional = true }
thiserror.workspace = true
tokio.workspace = true
toml = "0.8.22"
tracing = "0.1.41"
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
uuid = { version = "1.17.0", features = ["v7"] }
[dev-dependencies]
tempfile = "3.20.0"

View file

@ -0,0 +1,2 @@
pub mod entity;
pub mod migration;

View file

@ -4,10 +4,7 @@ use std::path::Path;
use crate::error::Error; use crate::error::Error;
pub use node::{ NodeConfig, RawNodeConfig }; pub use node::{ NodeConfig, RawNodeConfig };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
pub use crate::global::{
DEFAULT_LISTEN_IPS,
DEFAULT_PORT,
};
use tokio::{fs::File, io::{AsyncReadExt, AsyncWriteExt}}; use tokio::{fs::File, io::{AsyncReadExt, AsyncWriteExt}};
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
pub struct PartialConfig { pub struct PartialConfig {

View file

@ -1,16 +1,15 @@
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};
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::{cli::ConfigArgs, error::Error, global::DEFAULT_DATABASE_FILE_PATH, p2p}; use crate::{error::Error, p2p};
use super::{PartialConfig, DEFAULT_LISTEN_IPS, DEFAULT_PORT}; use super::{PartialConfig};
fn keypair_to_base64(keypair: &Keypair) -> Result<String, Error> { fn keypair_to_base64(keypair: &Keypair) -> Result<String, Error> {
let vec = keypair.to_protobuf_encoding()?; let vec = keypair.to_protobuf_encoding()?;
@ -83,15 +82,11 @@ mod keypair_parser {
} }
} }
#[derive(Args, Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize)]
pub struct RawNodeConfig { pub struct RawNodeConfig {
#[arg(skip)]
pub secret: Option<String>, pub secret: Option<String>,
#[arg(long)]
pub database_path: Option<PathBuf>, pub database_path: Option<PathBuf>,
#[arg(long)]
pub listen_ips: Option<Vec<IpAddr>>, pub listen_ips: Option<Vec<IpAddr>>,
#[arg(long)]
pub port: Option<u16>, pub port: Option<u16>,
} }
impl RawNodeConfig { impl RawNodeConfig {

View file

@ -14,3 +14,8 @@ pub use record_deletion::{
Entity as RecordDeletionEntity, Entity as RecordDeletionEntity,
Model as RecordDeletionModel, Model as RecordDeletionModel,
}; };
use uuid::{ContextV7, Timestamp, Uuid};
pub fn generate_uuid() -> Uuid {
Uuid::new_v7(Timestamp::now(ContextV7::new()))
}

View file

@ -32,7 +32,7 @@ impl ActiveModel {
pub fn new() -> Self { pub fn new() -> Self {
let timestamp: DateTimeUtc = Local::now().to_utc(); let timestamp: DateTimeUtc = Local::now().to_utc();
Self{ Self{
id: Set(Uuid::new_v4()), id: Set(super::generate_uuid()),
created_at: Set(timestamp), created_at: Set(timestamp),
updated_at: Set(timestamp), updated_at: Set(timestamp),
..Default::default() ..Default::default()

View file

@ -26,7 +26,7 @@ impl ActiveModel {
pub fn new() -> Self { pub fn new() -> Self {
let timestamp: DateTimeUtc = Local::now().to_utc(); let timestamp: DateTimeUtc = Local::now().to_utc();
Self{ Self{
id: Set(Uuid::new_v4()), id: Set(super::generate_uuid()),
created_at: Set(timestamp), created_at: Set(timestamp),
..Default::default() ..Default::default()
} }
@ -37,7 +37,7 @@ impl ActiveModel {
mod tests { mod tests {
use super::*; use super::*;
use uuid::Uuid; use uuid::{Timestamp, Uuid};
use crate::global::get_or_init_temporary_main_database; use crate::global::get_or_init_temporary_main_database;
#[tokio::test] #[tokio::test]
@ -46,7 +46,7 @@ mod tests {
assert!(ActiveModel{ assert!(ActiveModel{
table_name: Set("test_table".to_string()), table_name: Set("test_table".to_string()),
record_id: Set(Uuid::new_v4()), record_id: Set(super::super::generate_uuid()),
..ActiveModel::new() ..ActiveModel::new()
}.insert(db).await.is_ok()); }.insert(db).await.is_ok());
} }

View file

@ -0,0 +1,2 @@
pub mod entity;
pub mod migration;

View file

@ -18,12 +18,6 @@ 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),
#[error("Parse args error: {0}")]
ParseCommand(#[from] clap::Error),
#[error("Readline error: {0}")]
Readline(#[from] rustyline::error::ReadlineError),
#[error("Shell word split error: {0}")]
ShellWord(#[from] shell_words::ParseError),
#[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}")]

View file

@ -16,43 +16,16 @@ pub static PRODUCT_NAME: LazyLock<String> = LazyLock::new(|| {
pub static DEFAULT_LISTEN_IPS: &[IpAddr] = &[IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))]; pub static DEFAULT_LISTEN_IPS: &[IpAddr] = &[IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0))];
pub static DEFAULT_PORT: u16 = 8080;
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(|| { pub static DEFAULT_CONFIG_FILE_NAME: LazyLock<PathBuf> = LazyLock::new(|| {
PathBuf::from(String::new() + env!("CARGO_PKG_NAME") + ".toml") 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(|| { pub static DEFAULT_DATABASE_FILE_NAME: LazyLock<PathBuf> = LazyLock::new(|| {
PathBuf::from(String::new() + env!("CARGO_PKG_NAME") + ".sqlite") 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(), node_config: OnceCell::const_new(),
@ -140,14 +113,7 @@ impl GlobalDatabase for Global {
} }
pub static DEFAULT_RAW_NODE_CONFIG: LazyLock<RawNodeConfig> = LazyLock::new(|| {
RawNodeConfig {
secret: None,
database_path: Some(DEFAULT_DATABASE_FILE_PATH.to_path_buf()),
listen_ips: Some(DEFAULT_LISTEN_IPS.to_vec()),
port: Some(DEFAULT_PORT),
}
});
#[cfg(test)] #[cfg(test)]
pub use tests::{get_or_init_temporary_main_database, get_or_init_temporary_cache_database}; pub use tests::{get_or_init_temporary_main_database, get_or_init_temporary_cache_database};
#[cfg(test)] #[cfg(test)]
@ -156,7 +122,7 @@ pub mod tests {
use sea_orm_migration::MigratorTrait; use sea_orm_migration::MigratorTrait;
use crate::{global::GLOBAL, migration::{cache::CacheMigrator, main::MainMigrator}}; use crate::{global::GLOBAL, cache::migration::CacheMigrator, data::migration::MainMigrator};
use super::*; use super::*;

View file

@ -0,0 +1,9 @@
pub mod cache;
pub mod config;
pub mod data;
pub mod error;
pub mod global;
pub mod migration;
pub mod p2p;
#[cfg(any(test, feature="test"))]
pub mod tests;

View file

@ -1,6 +1,3 @@
pub mod cache;
pub mod main;
use sea_orm_migration::{prelude::*, schema::*}; use sea_orm_migration::{prelude::*, schema::*};
#[async_trait::async_trait] #[async_trait::async_trait]

View file

@ -0,0 +1,19 @@
[package]
name = "lazy-supplements-desktop"
edition.workspace = true
version.workspace = true
description.workspace = true
license.workspace = true
repository.workspace = true
[dependencies]
clap = { version = "4.5.38", features = ["derive"] }
dirs = "6.0.0"
lazy-supplements-core.workspace = true
libp2p.workspace = true
serde.workspace = true
thiserror.workspace = true
tokio.workspace = true
[dev-dependencies]
lazy-supplements-core = {workspace = true, features = ["test"]}

View file

@ -0,0 +1,54 @@
use std::{net::IpAddr, path::PathBuf};
use clap::Args;
use lazy_supplements_core::config::RawNodeConfig;
use serde::{Deserialize, Serialize};
use crate::{config::NodeConfig, error::Error, global::{DEFAULT_CONFIG_FILE_PATH, DEFAULT_RAW_NODE_CONFIG}};
#[derive(Args, Clone, Debug)]
pub struct ConfigArgs {
#[arg(long)]
pub config: Option<PathBuf>,
#[command(flatten)]
pub config_values: ConfigValueArgs,
}
impl ConfigArgs {
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_raw_node_config(self) -> Result<RawNodeConfig, Error> {
Ok(RawNodeConfig::read_from(self.get_config_path_or_default()).await? + self.config_values.into())
}
pub async fn try_into_node_config(self) -> Result<NodeConfig, Error> {
Ok((DEFAULT_RAW_NODE_CONFIG.clone() + self.try_into_raw_node_config().await?).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
}
}
}

View file

@ -1,13 +1,9 @@
use std::path::PathBuf; use std::path::PathBuf;
mod config; mod config;
mod console;
mod init;
mod node; mod node;
mod server; mod server;
pub use config::ConfigArgs; pub use config::ConfigArgs;
pub use console::{ConsoleArgs, ConsoleCommands};
pub use init::InitArgs;
pub use node::{ NodeArgs, NodeCommand, PeerArgs , ConsoleNodeArgs}; pub use node::{ NodeArgs, NodeCommand, PeerArgs , ConsoleNodeArgs};
pub use server::ServerArgs; pub use server::ServerArgs;

View file

@ -1,13 +1,11 @@
use std::{net::IpAddr, ops::Mul, path::PathBuf, str::FromStr}; use std::{net::IpAddr, ops::Mul, path::PathBuf, str::FromStr};
use clap::{Args, Parser, Subcommand}; use clap::{Args, Parser, Subcommand};
use futures::StreamExt;
use libp2p::{ use libp2p::{
multiaddr::Protocol, noise, ping, swarm::SwarmEvent, tcp, yamux, Multiaddr, PeerId multiaddr::Protocol, noise, ping, swarm::SwarmEvent, tcp, yamux, Multiaddr, PeerId
}; };
use tracing_subscriber::EnvFilter;
use crate::{cli::ServerArgs, error::Error}; use crate::{cli::ServerArgs, error::{CoreError, DesktopError, Error}};
use super::ConfigArgs; use super::ConfigArgs;
@ -30,10 +28,6 @@ impl NodeArgs {
} }
} }
pub async fn parse_and_run_console_node_command(s:Vec<String>) -> Result<(), Error> {
ConsoleNodeArgs::try_parse_from(s)?.args.run().await
}
#[derive(Args, Debug)] #[derive(Args, Debug)]
pub struct PeerArgs { pub struct PeerArgs {
#[arg(value_parser = clap::value_parser!(PeerArg))] #[arg(value_parser = clap::value_parser!(PeerArg))]

View file

@ -1,9 +1,7 @@
use clap::Args; use clap::Args;
use futures::StreamExt;
use libp2p::{noise, ping, swarm::{NetworkBehaviour, SwarmEvent}, tcp, yamux, Swarm}; use libp2p::{noise, ping, swarm::{NetworkBehaviour, SwarmEvent}, tcp, yamux, Swarm};
use tracing_subscriber::EnvFilter;
use crate::{error::Error, global::GLOBAL}; use crate::{error::Error, global::GLOBAL, error::CoreError};
use super::ConfigArgs; use super::ConfigArgs;
@ -15,6 +13,6 @@ 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_node_config(self.config.try_into_node_config().await?).await;
GLOBAL.launch_swarm().await GLOBAL.launch_swarm().await.or_else(|e| {Err(Error::from(CoreError::from(e)))})
} }
} }

View file

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

View file

@ -0,0 +1,39 @@
use std::{path::PathBuf, sync::LazyLock};
use lazy_supplements_core::config::RawNodeConfig;
pub use lazy_supplements_core::global::*;
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_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_PATH: LazyLock<PathBuf> = LazyLock::new(|| {
DEFAULT_CONFIG_DIR_PATH.join(&*DEFAULT_CONFIG_FILE_NAME)
});
pub static DEFAULT_DATABASE_FILE_PATH: LazyLock<PathBuf> = LazyLock::new(|| {
DEFAULT_DATA_DIR_PATH.join(&*DEFAULT_DATABASE_FILE_NAME)
});
pub static DEFAULT_RAW_NODE_CONFIG: LazyLock<RawNodeConfig> = LazyLock::new(|| {
RawNodeConfig {
secret: None,
database_path: Some(DEFAULT_DATABASE_FILE_PATH.to_path_buf()),
listen_ips: Some(DEFAULT_LISTEN_IPS.to_vec()),
port: Some(0),
}
});

View file

@ -0,0 +1,8 @@
pub mod cli;
pub mod error;
pub mod global;
pub use lazy_supplements_core::{
cache,
config,
data,
};

View file

@ -1,5 +1,5 @@
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use lazy_supplements::{cli::{ConfigArgs, ConsoleArgs, ConsoleCommands, InitArgs, NodeArgs, NodeCommand, ServerArgs}, global::{Global, GLOBAL}, *}; use lazy_supplements_desktop::{cli::{ConfigArgs, NodeArgs, NodeCommand, ServerArgs}, global::{Global, GLOBAL}, *};
#[derive(Debug, Parser)] #[derive(Debug, Parser)]
struct Cli { struct Cli {
@ -11,9 +11,7 @@ struct Cli {
#[derive(Debug, Subcommand)] #[derive(Debug, Subcommand)]
enum Command { enum Command {
Console(ConsoleArgs),
Node(NodeArgs), Node(NodeArgs),
Init(InitArgs),
Server(ServerArgs), Server(ServerArgs),
} }
@ -24,8 +22,6 @@ async fn main() {
let _ = GLOBAL.get_or_init_node_config(cli.config.try_into_node_config().await.unwrap()).await; let _ = GLOBAL.get_or_init_node_config(cli.config.try_into_node_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::Init(x) => x.init_config().await,
Command::Server(x) => x.start_server().await.unwrap(), Command::Server(x) => x.start_server().await.unwrap(),
Command::Console(x) => x.start_console(ConsoleCommands::default()).await.unwrap(),
} }
} }

View file

@ -0,0 +1,9 @@
[package]
name = "lazy-supplements-mobile"
edition.workspace = true
version.workspace = true
description.workspace = true
license.workspace = true
repository.workspace = true
[dependencies]

View file

@ -0,0 +1,14 @@
pub fn add(left: u64, right: u64) -> u64 {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}

View file

@ -1,35 +0,0 @@
[package]
name = "lazy-supplements"
edition.workspace = true
version.workspace = true
description.workspace = true
license.workspace = true
repository.workspace = true
[features]
default = []
test = ["dep:tempfile"]
[dependencies]
base64 = "0.22.1"
chrono = "0.4.41"
chrono-tz = "0.10.3"
clap = { version = "4.5.38", features = ["derive"] }
dirs = "6.0.0"
futures = "0.3.31"
libp2p.workspace = true
rustyline = "16.0.0"
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"] }
shell-words = "1.1.0"
tempfile = { version = "3.20.0", optional = true }
thiserror = "2.0.12"
tokio = { version = "1.45.0", features = ["macros", "rt", "rt-multi-thread"] }
toml = "0.8.22"
tracing = "0.1.41"
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
uuid = { version = "1.17.0", features = ["v4"] }
[dev-dependencies]
tempfile = "3.20.0"

View file

@ -1,30 +0,0 @@
use std::path::PathBuf;
use clap::Args;
use libp2p::identity;
use crate::{config::{NodeConfig, RawNodeConfig}, error::Error, global::{DEFAULT_CONFIG_FILE_PATH, DEFAULT_RAW_NODE_CONFIG}};
#[derive(Args, Clone, Debug)]
pub struct ConfigArgs {
#[arg(long)]
pub config: Option<PathBuf>,
#[command(flatten)]
pub node_config: RawNodeConfig,
}
impl ConfigArgs {
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_raw_node_config(self) -> Result<RawNodeConfig, Error> {
Ok(RawNodeConfig::read_from(self.get_config_path_or_default()).await? + self.node_config)
}
pub async fn try_into_node_config(self) -> Result<NodeConfig, Error> {
Ok((DEFAULT_RAW_NODE_CONFIG.clone() + self.try_into_raw_node_config().await?).try_into()?)
}
}

View file

@ -1,108 +0,0 @@
mod writer;
use std::{collections::HashMap, ffi::OsString, hash::Hash, time::Duration};
use clap::{Args, Parser};
use futures::{future::BoxFuture, StreamExt};
use libp2p::{core::transport::dummy::DummyTransport, noise, ping, swarm::{NetworkBehaviour, SwarmEvent}, tcp, yamux, Swarm};
use rustyline::ExternalPrinter;
use tokio::{sync::Mutex, time::sleep};
use tracing_subscriber::EnvFilter;
use writer::ConsoleWriter;
use crate::{error::Error, global::GLOBAL};
use super::{node::parse_and_run_console_node_command, ConfigArgs};
pub trait Executable {
fn execute(self) -> Result<(), Error>;
}
pub trait ConsoleCommand {
fn execute_line(&self, line: String) -> Result<(), Error>;
}
pub struct ConsoleCommands {
content: HashMap<&'static str, Box<dyn Fn(Vec<String>) -> BoxFuture<'static, Result<(), Error>>>>,
}
impl ConsoleCommands {
pub fn new() -> Self {
Self{content: HashMap::new()}
}
pub fn insert<F, Fut>(&mut self,name: &'static str, f: F)
where
F: Fn(Vec<String>) -> Fut + 'static,
Fut: Future<Output = Result<(), Error>> + Send + 'static,
{
if let Some(_) = self.content.insert(name, Box::new(move |v| {
Box::pin(f(v))
})){
unreachable!();
};
}
pub async fn parse_and_run(&self, line: String) -> Result<(), Error>{
let args = shell_words::split(&line)?;
if let Some(command_name) = args.first().map(|s| {s.clone()}) {
if let Some(command) = self.content.get(command_name.as_str()) {
command(args).await
} else {
println!("Invalid command: {command_name}");
self.print_commands();
Ok(())
}
} else {
Ok(())
}
}
pub fn print_commands(&self) {
for key in self.content.keys(){
println!("{key}");
}
}
}
impl Default for ConsoleCommands {
fn default() -> Self {
let mut commands = Self::new();
commands.insert("node", parse_and_run_console_node_command);
commands
}
}
#[derive(Args, Debug)]
pub struct ConsoleArgs {
#[command(flatten)]
config: ConfigArgs,
}
impl ConsoleArgs {
pub async fn start_console(self, commands: ConsoleCommands) -> Result<(), Error>
{
let _ = crate::global::GLOBAL.get_or_init_node_config(self.config.try_into_node_config().await?).await;
tokio::spawn( async {
GLOBAL.launch_swarm().await
});
let mut rl = rustyline::DefaultEditor::new()?;
let mut printer = rl.create_external_printer()?;
tracing_subscriber::fmt()
.with_env_filter(EnvFilter::from_default_env())
.with_writer(std::sync::Mutex::new(ConsoleWriter::try_from(&mut rl)?)
).init();
tokio::spawn(async move {
loop{
tracing::event!(tracing::Level::ERROR, "test");
tokio::time::sleep(Duration::from_secs(1)).await;
}
});
loop {
match rl.readline(">> ") {
Ok(line) => {
if let Err(e) = commands.parse_and_run(line).await {
println!("{e}");
}
},
Err(x) => Err(x)?,
};
}
}
}

View file

@ -1,31 +0,0 @@
use std::io::Read;
use rustyline::{DefaultEditor, ExternalPrinter};
use crate::error::Error;
pub struct ConsoleWriter {
printer: Box<dyn ExternalPrinter + 'static + Send>
}
impl TryFrom<&mut DefaultEditor> for ConsoleWriter {
type Error = Error;
fn try_from(e: &mut DefaultEditor) -> Result<Self, Error> {
Ok(ConsoleWriter {
printer: Box::new(e.create_external_printer()?)
})
}
}
impl std::io::Write for ConsoleWriter {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
let msg = String::from_utf8_lossy(buf).into_owned();
let size = msg.as_bytes().len();
self.printer.as_mut().print(msg);
Ok(size)
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}

View file

@ -1,34 +0,0 @@
use std::path::PathBuf;
use clap::Args;
use libp2p::identity;
use crate::{config::RawNodeConfig, global::DEFAULT_CONFIG_FILE_PATH};
#[derive(Args, Debug)]
pub struct InitArgs {
#[arg(long)]
config: Option<PathBuf>,
#[arg(short, long)]
force: bool,
#[command(flatten)]
node_config: RawNodeConfig,
}
impl InitArgs {
pub async fn init_config(self) {
let config_path = if let Some(x) = self.config {
x
} else {
DEFAULT_CONFIG_FILE_PATH.to_path_buf()
};
if config_path.exists() && !self.force {
println!("Config file already exists!");
return;
} else {
let config = self.node_config.with_new_secret();
config.write_to(config_path).await.unwrap()
}
}
}

View file

@ -1,9 +0,0 @@
pub mod cli;
pub mod config;
pub mod entity;
pub mod error;
pub mod global;
pub mod migration;
pub mod p2p;
#[cfg(any(test, feature="test"))]
pub mod tests;