Add caretta-sync-bevy and remove async from Runnable::run() for bevy

This commit is contained in:
fluo10 2025-08-24 16:31:15 +09:00
parent 347ef56d56
commit ff5256eadd
35 changed files with 182 additions and 73 deletions

View file

@ -8,13 +8,15 @@ repository.workspace = true
[features]
default = ["macros"]
bevy = ["dep:caretta-sync-bevy"]
mobile = ["dep:caretta-sync-mobile"]
cli = ["dep:caretta-sync-cli"]
desktop = ["cli"]
desktop = ["cli", "bevy"]
macros = ["dep:caretta-sync-macros"]
test = ["caretta-sync-core/test"]
[dependencies]
caretta-sync-bevy = { path = "bevy", optional = true }
caretta-sync-core.workspace = true
caretta-sync-cli = { path="cli", optional = true }
caretta-sync-mobile = { path = "mobile", optional = true }
@ -24,7 +26,7 @@ caretta-sync-macros = { path="macros", optional = true}
caretta-sync-core = {workspace = true, features = ["test"]}
[workspace]
members = [ ".", "core", "macros", "cli", "mobile", "examples/demo/*" ]
members = [ ".", "core", "macros", "cli", "mobile", "examples/demo/*" , "bevy"]
resolver = "3"
[workspace.package]
@ -35,11 +37,12 @@ license = "MIT OR Apache-2.0"
repository = "https://forgejo.fireturlte.net/lazy-supplements"
[workspace.dependencies]
bevy = "0.16.1"
chrono = "0.4.41"
ciborium = "0.2.2"
clap = { version = "4.5.38", features = ["derive"] }
dioxus = { version = "0.6.0", features = [] }
caretta-sync-core.path = "core"
futures = { version = "0.3.31", features = ["executor"] }
libp2p = { version = "0.55.0", features = ["macros", "mdns", "noise", "ping", "tcp", "tokio", "yamux" ] }
sea-orm = { version = "1.1.11", features = ["sqlx-sqlite", "runtime-tokio-native-tls", "macros", "with-chrono", "with-uuid"] }
sea-orm-migration = { version = "1.1.0", features = ["runtime-tokio-rustls", "sqlx-postgres"] }
@ -49,3 +52,12 @@ tokio = { version = "1.45.0", features = ["macros", "rt", "rt-multi-thread"] }
tonic = "0.14.0"
uuid = { version = "1.17.0", features = ["v7"] }
[profile.dev]
opt-level = 1
[profile.dev.package."*"]
opt-level = 3
[profile.release]
codegen-units = 1
lto = "thin"

15
bevy/Cargo.toml Normal file
View file

@ -0,0 +1,15 @@
[package]
name = "caretta-sync-bevy"
edition.workspace = true
version.workspace = true
description.workspace = true
license.workspace = true
repository.workspace = true
[dependencies]
bevy.workspace = true
caretta-sync-core.workspace = true
futures.workspace = true
sea-orm.workspace = true
tokio.workspace = true
tonic.workspace = true

4
bevy/src/global.rs Normal file
View file

@ -0,0 +1,4 @@
use bevy::{asset::uuid::Uuid, ecs::component::Component};
#[derive(Component)]
struct Id(Uuid);

2
bevy/src/lib.rs Normal file
View file

@ -0,0 +1,2 @@
pub mod global;
pub mod peer;

47
bevy/src/peer.rs Normal file
View file

@ -0,0 +1,47 @@
use bevy::{app::{App, Plugin, Startup, Update}, ecs::{component::Component, query::With, system::{Commands, Query}}, tasks::TaskPool};
use caretta_sync_core::{cache::entity::{CachedPeerEntity, CachedPeerModel}, global::{CONFIG, DATABASE_CONNECTIONS}};
use caretta_sync_core::{
proto::*,
};
use sea_orm::EntityTrait;
#[derive(Component)]
pub struct Peer;
#[derive(Component)]
pub struct PeerId(String);
#[derive(Component)]
pub struct PeerAddress(String);
#[tokio::main]
async fn add_cached_peers(mut commands: Commands) {
let config = CONFIG.get_unchecked();
let path = String::from("unix://") + config.rpc.socket_path.as_os_str().to_str().expect("Invalid string");
let mut client = caretta_sync_core::proto::cached_peer_service_client::CachedPeerServiceClient::connect(path).await.expect("Unix socket should be accessible");
let request = tonic::Request::new(CachedPeerListRequest {});
let response = client.list(request).await.expect("Faild to request/response");
let peers = response.into_inner().peers;
for model in peers.into_iter() {
commands.spawn((Peer, PeerId(model.peer_id.to_string())));
}
}
fn print_peer(query: Query<&PeerId, With<Peer>>) {
for peer_id in &query {
println!("Hello {}!", peer_id.0);
}
}
fn hello_world() {
println!("hello world!");
}
pub struct PeerPlugin;
impl Plugin for PeerPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Startup, add_cached_peers);
app.add_systems(Update, (hello_world, print_peer));
}
}

View file

@ -26,4 +26,3 @@ uuid.workspace = true
[dev-dependencies]
caretta-sync-core = {workspace = true, features = ["test"]}

View file

@ -9,6 +9,7 @@ pub struct ConfigCheckCommandArgs{
}
impl Runnable for ConfigCheckCommandArgs {
#[tokio::main]
async fn run(self, app_name: &'static str) {
let _ = self.config.into_config(app_name).await;
println!("Ok");

View file

@ -11,6 +11,7 @@ pub struct ConfigListCommandArgs{
}
impl Runnable for ConfigListCommandArgs {
#[tokio::main]
async fn run(self, app_name: &'static str) {
let config: PartialConfig = if self.all {
self.config.into_config(app_name).await.into()

View file

@ -15,8 +15,8 @@ pub struct ConfigCommandArgs {
}
impl Runnable for ConfigCommandArgs {
async fn run(self, app_name: &'static str) {
self.command.run(app_name).await
fn run(self, app_name: &'static str) {
self.command.run(app_name)
}
}
@ -27,10 +27,10 @@ pub enum ConfigSubcommand {
}
impl Runnable for ConfigSubcommand {
async fn run(self, app_name: &'static str) {
fn run(self, app_name: &'static str) {
match self {
Self::Check(x) => x.run(app_name).await,
Self::List(x) => x.run(app_name).await,
Self::Check(x) => x.run(app_name),
Self::List(x) => x.run(app_name),
}
}
}

View file

@ -16,7 +16,7 @@ pub struct DeviceAddCommandArgs {
}
impl Runnable for DeviceAddCommandArgs {
async fn run(self, app_name: &'static str) {
fn run(self, app_name: &'static str) {
todo!()
}
}

View file

@ -9,7 +9,7 @@ pub struct DeviceListCommandArgs{
}
impl Runnable for DeviceListCommandArgs {
async fn run(self, app_name: &'static str) {
fn run(self, app_name: &'static str) {
todo!()
}
}

View file

@ -22,8 +22,8 @@ pub struct DeviceCommandArgs {
}
impl Runnable for DeviceCommandArgs {
async fn run(self, app_name: &'static str) {
self.command.run(app_name).await
fn run(self, app_name: &'static str) {
self.command.run(app_name)
}
}
@ -37,13 +37,13 @@ pub enum DeviceSubcommand {
}
impl Runnable for DeviceSubcommand {
async fn run(self, app_name: &'static str) {
fn run(self, app_name: &'static str) {
match self {
Self::Add(x) => x.run(app_name).await,
Self::List(x) => x.run(app_name).await,
Self::Ping(x) => x.run(app_name).await,
Self::Remove(x) => x.run(app_name).await,
Self::Scan(x) => x.run(app_name).await,
Self::Add(x) => x.run(app_name),
Self::List(x) => x.run(app_name),
Self::Ping(x) => x.run(app_name),
Self::Remove(x) => x.run(app_name),
Self::Scan(x) => x.run(app_name),
}
}
}

View file

@ -11,7 +11,7 @@ pub struct DevicePingCommandArgs{
}
impl Runnable for DevicePingCommandArgs {
async fn run(self, app_name: &'static str) {
fn run(self, app_name: &'static str) {
todo!()
}
}

View file

@ -11,7 +11,7 @@ pub struct DeviceRemoveCommandArgs{
}
impl Runnable for DeviceRemoveCommandArgs {
async fn run(self, app_name: &'static str) {
fn run(self, app_name: &'static str) {
todo!()
}
}

View file

@ -9,7 +9,7 @@ pub struct DeviceScanCommandArgs{
}
impl Runnable for DeviceScanCommandArgs {
async fn run(self, app_name: &'static str) {
fn run(self, app_name: &'static str) {
todo!()
}
}

View file

@ -1,14 +0,0 @@
use caretta_sync_core::utils::runnable::Runnable;
use clap::Args;
#[derive(Args, Debug)]
pub struct LogsCommandArgs {
#[arg(short='n', long)]
lines: Option<u32>,
}
impl Runnable for LogsCommandArgs {
async fn run(self, app_name: &'static str) {
todo!()
}
}

View file

@ -3,13 +3,11 @@ use std::path::PathBuf;
mod args;
mod config;
mod device;
mod logs;
mod peer;
mod serve;
pub use args::*;
pub use config::*;
pub use device::*;
pub use logs::*;
pub use peer::*;
pub use serve::*;

View file

@ -11,7 +11,7 @@ pub struct PeerInfoCommandArgs{
}
impl Runnable for PeerInfoCommandArgs {
async fn run(self, app_name: &'static str) {
fn run(self, app_name: &'static str) {
todo!()
}
}

View file

@ -12,6 +12,7 @@ pub struct PeerListCommandArgs{
}
impl Runnable for PeerListCommandArgs {
#[tokio::main]
async fn run(self, app_name: &'static str) {
let config = self.config.into_config(app_name).await;
let path = String::from("unix://") + config.rpc.socket_path.as_os_str().to_str().expect("Invalid string");

View file

@ -17,8 +17,8 @@ pub struct PeerCommandArgs {
}
impl Runnable for PeerCommandArgs {
async fn run(self, app_name: &'static str) {
self.command.run(app_name).await
fn run(self, app_name: &'static str) {
self.command.run(app_name)
}
}
@ -30,11 +30,11 @@ pub enum PeerSubcommand {
}
impl Runnable for PeerSubcommand {
async fn run(self, app_name: &'static str) {
fn run(self, app_name: &'static str) {
match self {
Self::Info(x) => x.run(app_name).await,
Self::List(x) => x.run(app_name).await,
Self::Ping(x) => x.run(app_name).await,
Self::Info(x) => x.run(app_name),
Self::List(x) => x.run(app_name),
Self::Ping(x) => x.run(app_name),
}
}
}

View file

@ -11,6 +11,7 @@ pub struct PeerPingCommandArgs{
}
impl Runnable for PeerPingCommandArgs {
#[tokio::main]
async fn run(self, app_name: &'static str) {
todo!()
}

View file

@ -20,6 +20,7 @@ impl<T> Runnable for ServeCommandArgs<T>
where
T: ServerTrait
{
#[tokio::main]
async fn run(self, app_name: &'static str) {
let config = CONFIG.get_or_init::<Config>(self.config.into_config(app_name).await).await;
let _ = DATABASE_CONNECTIONS.get_or_init_unchecked(&config, DataMigrator).await;

View file

@ -18,7 +18,7 @@ chrono-tz = "0.10.3"
ciborium.workspace = true
clap = {workspace = true, optional = true}
dirs = "6.0.0"
futures = "0.3.31"
futures.workspace = true
libp2p.workspace = true
libp2p-core = { version = "0.43.0", features = ["serde"] }
libp2p-identity = { version = "0.2.11", features = ["ed25519", "peerid", "rand", "serde"] }

View file

@ -32,4 +32,7 @@ impl GlobalConfig {
pub fn get(&'static self) -> Option<&'static Config> {
self.inner.get()
}
pub fn get_unchecked(&'static self) -> &'static Config {
self.get().expect("Config must be initialized before use!")
}
}

View file

@ -1,6 +1,3 @@
#[cfg(feature="macros")]
pub use caretta_sync_macros::Runnable;
pub trait Runnable {
async fn run(self, app_name: &'static str);
fn run(self, app_name: &'static str);
}

View file

@ -7,7 +7,8 @@ license.workspace = true
repository.workspace = true
[dependencies]
caretta-sync.path = "../../.."
bevy.workspace = true
caretta-sync = { path = "../../..", features = ["bevy"] }
libp2p.workspace = true
tokio.workspace = true
tokio-stream = { version = "0.1.17", features = ["net"] }

View file

@ -0,0 +1,13 @@
use caretta_sync::{bevy::peer::PeerPlugin, utils::Runnable};
use bevy::prelude::*;
pub struct Gui {}
impl Runnable for Gui {
fn run(self, app_name: &'static str) {
App::new()
//.add_plugins(DefaultPlugins)
.add_plugins(PeerPlugin)
.run();
}
}

View file

@ -1,3 +1,4 @@
pub mod global;
pub mod gui;
pub mod rpc;
pub mod server;

View file

@ -10,8 +10,7 @@ repository.workspace = true
[dependencies]
clap.workspace = true
caretta-sync = { path = "../../..", features = ["desktop", "test"] }
caretta-sync = { path = "../../..", features = ["cli", "bevy", "test"] }
caretta-sync-demo-core.path = "../core"
libp2p.workspace = true
tokio.workspace = true

View file

@ -1,20 +1,39 @@
use caretta_sync_demo_core::server::Server;
use caretta_sync_demo_core::{gui::Gui, server::Server};
use clap::{Parser, Subcommand};
use caretta_sync::{cli::*, utils::runnable::Runnable};
use caretta_sync::{cli::*, config::Config, data::migration::DataMigrator, global::{CONFIG, DATABASE_CONNECTIONS}, utils::Runnable};
#[derive(Debug, Parser, Runnable)]
#[derive(Debug, Parser)]
pub struct Cli {
#[command(subcommand)]
#[runnable]
command: CliCommand
command: Option<CliCommand>,
#[command(flatten)]
config: ConfigArgs,
}
impl Runnable for Cli {
fn run(self, app_name: &'static str) {
if let Some(x) = self.command {
x.run(app_name)
} else {
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap()
.block_on(async {
let config: caretta_sync::config::Config = self.config.into_config(app_name).await;
let _ = CONFIG.get_or_init::<Config>(config).await;
});
//let _ = DATABASE_CONNECTIONS.get_or_init_unchecked(&config, DataMigrator).await;
Gui{}.run(app_name)
}
}
}
#[derive(Debug, Subcommand, Runnable)]
pub enum CliCommand {
Config(ConfigCommandArgs),
Device(DeviceCommandArgs),
Logs(LogsCommandArgs),
Peer(PeerCommandArgs),
Serve(ServeCommandArgs<Server>),
}

View file

@ -1,4 +1,4 @@
use caretta_sync::utils::runnable::Runnable;
use caretta_sync::utils::Runnable;
use caretta_sync_demo_core::global::APP_NAME;
use clap::Parser;
@ -7,8 +7,7 @@ use crate::cli::Cli;
mod cli;
mod ipc;
#[tokio::main]
async fn main() {
fn main() {
let args = Cli::parse();
args.run(APP_NAME).await;
args.run(APP_NAME);
}

View file

@ -206,8 +206,8 @@ pub fn runnable(input: TokenStream) -> TokenStream {
quote!{
impl Runnable for #type_ident {
async fn run(self, app_name: &'static str) {
<#field_type as Runnable>::run(self.#field_ident, app_name).await
fn run(self, app_name: &'static str) {
<#field_type as Runnable>::run(self.#field_ident, app_name)
}
}
}.into()
@ -216,12 +216,12 @@ pub fn runnable(input: TokenStream) -> TokenStream {
let quote_vec = extract_idents_and_types_from_enum_struct(&variants);
let quote_iter = quote_vec.iter().map(|(variant_ident, variant_type)|{
quote!{
Self::#variant_ident(x) => <#variant_type as Runnable>::run(x, app_name).await,
Self::#variant_ident(x) => <#variant_type as Runnable>::run(x, app_name),
}
});
quote!{
impl Runnable for #type_ident {
async fn run(self, app_name: &'static str) {
fn run(self, app_name: &'static str) {
match self {
#(#quote_iter)*
}

View file

@ -1,4 +1,7 @@
pub use caretta_sync_core::*;
#[cfg(feature = "bevy")]
pub use caretta_sync_bevy as bevy;
#[cfg(feature = "cli")]
pub use caretta_sync_cli::*;
#[cfg(feature = "mobile")]
@ -6,8 +9,14 @@ pub use caretta_sync_mobile::*;
#[cfg(feature = "macros")]
pub mod utils {
pub mod runnable {
pub use caretta_sync_core::utils::runnable::Runnable;
pub use caretta_sync_macros::Runnable;
}
pub use caretta_sync_core::utils::{
runnable::Runnable,
emptiable::Emptiable,
mergeable::Mergeable,
};
pub use caretta_sync_macros::{
Runnable,
Emptiable,
Mergeable,
};
}