Rename project to caretta

This commit is contained in:
fluo10 2025-08-05 08:17:35 +09:00
parent 25ea7ed652
commit c39a28388d
100 changed files with 550 additions and 583 deletions

View file

@ -1,5 +1,5 @@
[workspace] [workspace]
members = [ "lazy-supplements-*", "examples/*" ] members = [ "core", "desktop", "macros", "mobile", "examples/*" ]
resolver = "3" resolver = "3"
[workspace.package] [workspace.package]
@ -14,7 +14,7 @@ chrono = "0.4.41"
ciborium = "0.2.2" ciborium = "0.2.2"
clap = { version = "4.5.38", features = ["derive"] } 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" caretta-core.path = "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" ] }
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"] }
sea-orm-migration = { version = "1.1.0", features = ["runtime-tokio-rustls", "sqlx-postgres"] } sea-orm-migration = { version = "1.1.0", features = ["runtime-tokio-rustls", "sqlx-postgres"] }

View file

@ -1,3 +1,8 @@
# Lazy Supplements Framework # Caretta Framework
A local-first application framework for lazy person A local-first application framework.
## Features
- Local first
- Decentralized data synchronization with libp2p
- Device management

View file

@ -1,5 +1,5 @@
[package] [package]
name = "lazy-supplements-core" name = "caretta-core"
edition.workspace = true edition.workspace = true
version.workspace = true version.workspace = true
description.workspace = true description.workspace = true
@ -10,7 +10,7 @@ repository.workspace = true
default = [] default = []
desktop = ["dep:clap", "macros"] desktop = ["dep:clap", "macros"]
mobile = ["macros"] mobile = ["macros"]
macros = ["dep:lazy-supplements-macros"] macros = ["dep:caretta-macros"]
test = ["dep:tempfile", "macros"] test = ["dep:tempfile", "macros"]
[dependencies] [dependencies]
@ -20,7 +20,7 @@ chrono-tz = "0.10.3"
ciborium.workspace = true ciborium.workspace = true
clap = {workspace = true, optional = true} clap = {workspace = true, optional = true}
futures = "0.3.31" futures = "0.3.31"
lazy-supplements-macros = { path = "../lazy-supplements-macros", optional = true } caretta-macros = { path = "../macros", optional = true }
libp2p.workspace = true libp2p.workspace = true
libp2p-core = { version = "0.43.0", features = ["serde"] } libp2p-core = { version = "0.43.0", features = ["serde"] }
libp2p-identity = { version = "0.2.11", features = ["ed25519", "peerid", "rand", "serde"] } libp2p-identity = { version = "0.2.11", features = ["ed25519", "peerid", "rand", "serde"] }

57
core/src/cache/entity/cached_address.rs vendored Normal file
View file

@ -0,0 +1,57 @@
use std::str::FromStr;
use chrono::{Days, Local};
use libp2p::{multiaddr, Multiaddr, PeerId};
use sea_orm::{entity::{
prelude::*, *
}, sea_query};
use serde::{Deserialize, Serialize};
use crate::{cache, data::value::{MultiaddrValue, PeerIdValue}};
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Deserialize, Serialize)]
#[sea_orm(table_name = "cached_address")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: u32,
#[sea_orm(indexed)]
pub created_at: DateTimeUtc,
#[sea_orm(indexed)]
pub last_used_at: DateTimeUtc,
#[sea_orm(indexed)]
pub cached_peer_id: u32,
#[sea_orm(indexed)]
pub address: MultiaddrValue,
}
#[derive(Copy, Clone, Debug, DeriveRelation, EnumIter)]
pub enum Relation {
#[sea_orm(
belongs_to = "super::CachedPeerEntity",
from = "Column::CachedPeerId",
to = "super::CachedPeerColumn::Id"
)]
CachedPeer,
}
impl Related<super::CachedPeerEntity> for Entity {
fn to() -> RelationDef {
Relation::CachedPeer.def()
}
}
impl ActiveModelBehavior for ActiveModel {}
impl ActiveModel {
pub fn new(cached_peer_id: u32, multiaddr: Multiaddr) -> Self {
let timestamp: DateTimeUtc = Local::now().to_utc();
Self{
cached_peer_id: Set(cached_peer_id),
address: Set(MultiaddrValue::from(multiaddr)),
created_at: Set(timestamp),
last_used_at: Set(timestamp),
..Default::default()
}
}
}

48
core/src/cache/entity/cached_peer.rs vendored Normal file
View file

@ -0,0 +1,48 @@
use std::str::FromStr;
use chrono::{Days, Local};
use libp2p::{multiaddr, Multiaddr, PeerId};
use sea_orm::{entity::{
prelude::*, *
}, sea_query};
use serde::{Deserialize, Serialize};
use crate::data::value::{MultiaddrValue, PeerIdValue};
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Deserialize, Serialize)]
#[sea_orm(table_name = "cached_peer")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: u32,
#[sea_orm(indexed)]
pub created_at: DateTimeUtc,
#[sea_orm(indexed)]
pub peer_id: PeerIdValue,
}
#[derive(Copy, Clone, Debug, DeriveRelation, EnumIter)]
pub enum Relation {
#[sea_orm(has_many = "super::CachedAddressEntity")]
CachedAddress,
}
impl Related<super::CachedAddressEntity> for Entity {
fn to() -> RelationDef {
Relation::CachedAddress.def()
}
}
impl ActiveModelBehavior for ActiveModel {}
impl ActiveModel {
pub fn new(peer_id: PeerId) -> Self {
let timestamp: DateTimeUtc = Local::now().to_utc();
Self{
peer_id: Set(PeerIdValue::from(peer_id)),
created_at: Set(timestamp),
..Default::default()
}
}
}

46
core/src/cache/entity/mod.rs vendored Normal file
View file

@ -0,0 +1,46 @@
mod cached_peer;
mod cached_address;
pub use cached_peer::{
ActiveModel as CachedPeerActiveModel,
Column as CachedPeerColumn,
Model as CachedPeerModel,
Entity as CachedPeerEntity,
};
pub use cached_address::{
ActiveModel as CachedAddressActiveModel,
Column as CachedAddressColumn,
Model as CachedAddressModel,
Entity as CachedAddressEntity,
};
#[cfg(test)]
mod tests {
use std::net::Ipv4Addr;
use crate::{cache::entity::cached_peer, global::get_or_init_test_cache_database};
use super::*;
use libp2p::{identity::{self, Keypair}, multiaddr, swarm::handler::multi, Multiaddr, PeerId};
use sea_orm::ActiveModelTrait;
#[tokio::test]
async fn insert() {
let db = get_or_init_test_cache_database().await;
let peer_id = Keypair::generate_ed25519().public().to_peer_id();
let multiaddr = Multiaddr::empty()
.with(Ipv4Addr::new(127,0,0,1).into())
.with(multiaddr::Protocol::Tcp(0));
let inserted_cached_peer: CachedPeerModel = CachedPeerActiveModel::new(peer_id.clone())
.insert(db).await.unwrap();
let inserted_cached_address: CachedAddressModel = CachedAddressActiveModel::new(inserted_cached_peer.id, multiaddr.clone())
.insert(db).await.unwrap();
assert_eq!(PeerId::from(inserted_cached_peer.peer_id), peer_id);
assert_eq!(Multiaddr::from(inserted_cached_address.address), multiaddr);
}
}

View file

@ -0,0 +1,139 @@
use sea_orm_migration::{prelude::*, schema::*};
use crate::migration::TableMigration;
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
CachedPeer::up(manager).await?;
CachedAddress::up(manager).await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
CachedAddress::down(manager).await?;
CachedPeer::down(manager).await?;
Ok(())
}
}
#[derive(DeriveIden, DeriveMigrationName)]
enum CachedPeer {
Table,
Id,
PeerId,
CreatedAt,
}
static IDX_CACHED_ADDRESS: &str = "idx_CACHED_ADDRESS";
static IDX_CACHED_PEER_PEER_ID: &str = "idx_cached_peer_peer_id";
static IDX_CACHED_PEER_CREATED_AT: &str = "idx_cached_peer_created_at";
#[async_trait::async_trait]
impl TableMigration for CachedPeer {
async fn up<'a>(manager: &'a SchemaManager<'a>) -> Result<(), DbErr> {
manager.create_table(
Table::create()
.table(Self::Table)
.if_not_exists()
.col(pk_auto(Self::Id))
.col(string_len(Self::PeerId, 255))
.col(timestamp(Self::CreatedAt))
.to_owned()
).await?;
manager.create_index(
Index::create()
.name(IDX_CACHED_PEER_PEER_ID)
.table(Self::Table)
.col(Self::PeerId)
.to_owned()
).await?;
manager.create_index(
Index::create()
.name(IDX_CACHED_PEER_CREATED_AT)
.table(Self::Table)
.col(Self::CreatedAt)
.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
}
}
#[derive(DeriveIden, DeriveMigrationName)]
enum CachedAddress {
Table,
Id,
CachedPeerId,
CreatedAt,
LastUsedAt,
Address,
}
static IDX_CACHED_ADDRESS_ADDRESS: &str = "idx_cached_address_address";
static IDX_CACHED_ADDRESS_CACHED_PEER_ID: &str = "idx_cached_address_cached_peer_id";
static IDX_CACHED_ADDRESS_CREATED_AT: &str = "idx_cached_address_created_at";
static IDX_CACHED_ADDRESS_LAST_USED_AT: &str = "idx_cached_address_last_used_at";
static FK_CACHED_ADDRESS_CACHED_PEER: &str = "fk_cached_address_cached_peer";
#[async_trait::async_trait]
impl TableMigration for CachedAddress {
async fn up<'a>(manager: &'a SchemaManager<'a>) -> Result<(), DbErr> {
manager.create_table(
Table::create()
.table(Self::Table)
.if_not_exists()
.col(pk_auto(Self::Id))
.col(integer(Self::CachedPeerId))
.foreign_key(ForeignKey::create()
.name(FK_CACHED_ADDRESS_CACHED_PEER)
.from(Self::Table,Self::CachedPeerId)
.to(CachedPeer::Table, CachedPeer::Id)
.on_delete(ForeignKeyAction::Cascade)
.on_update(ForeignKeyAction::Cascade)
)
.col(timestamp(Self::CreatedAt))
.col(timestamp(Self::LastUsedAt))
.col(text_uniq(Self::Address))
.to_owned()
).await?;
manager.create_index(
Index::create()
.name(IDX_CACHED_ADDRESS_CACHED_PEER_ID)
.table(Self::Table)
.col(Self::CachedPeerId)
.to_owned()
).await?;
manager.create_index(
Index::create()
.name(IDX_CACHED_ADDRESS_ADDRESS)
.table(Self::Table)
.col(Self::Address)
.to_owned()
).await?;
manager.create_index(
Index::create()
.name(IDX_CACHED_ADDRESS_CREATED_AT)
.table(Self::Table)
.col(Self::CreatedAt)
.to_owned()
).await?;
manager.create_index(
Index::create()
.name(IDX_CACHED_ADDRESS_LAST_USED_AT)
.table(Self::Table)
.col(Self::LastUsedAt)
.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

@ -0,0 +1,35 @@
mod trusted_node;
mod record_deletion;
pub use trusted_node::{
ActiveModel as TrustedNodeActiveModel,
Column as TrustedNodeColumn,
Entity as TrustedNodeEntity,
Model as TrustedNodeModel,
};
pub use record_deletion::{
ActiveModel as RecordDeletionActiveModel,
Column as RecordDeletionColumn,
Entity as RecordDeletionEntity,
Model as RecordDeletionModel,
};
#[cfg(test)]
mod tests {
use crate::{data::value::PeerIdValue, global::{generate_uuid, get_or_init_test_data_database}};
use super::*;
use libp2p::{identity, PeerId};
use sea_orm::ActiveModelTrait;
#[tokio::test]
async fn check_insert() {
let db = get_or_init_test_data_database().await;
let node = TrustedNodeActiveModel::new(PeerId::random(), "test note".to_owned()).insert(db).await.unwrap();
let _ = RecordDeletionActiveModel::new(node.id, "test_table".to_string(), generate_uuid()).insert(db).await.unwrap();
}
}

View file

@ -1,8 +1,7 @@
use chrono::Local; use chrono::Local;
use sea_orm::entity::{ use sea_orm::{entity::{
*, prelude::*, *
prelude::* }, sea_query::table};
};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::data::syncable::*; use crate::data::syncable::*;
@ -29,33 +28,15 @@ pub enum Relation{}
impl ActiveModelBehavior for ActiveModel {} impl ActiveModelBehavior for ActiveModel {}
impl ActiveModel { impl ActiveModel {
pub fn new() -> Self { pub fn new(node_id: Uuid, table_name: String, record_id: Uuid) -> Self {
let timestamp: DateTimeUtc = Local::now().to_utc(); let timestamp: DateTimeUtc = Local::now().to_utc();
Self{ Self{
id: Set(crate::global::generate_uuid()), id: Set(crate::global::generate_uuid()),
created_at: Set(timestamp), created_at: Set(timestamp),
..Default::default() created_by: Set(node_id),
table_name: Set(table_name),
record_id: Set(record_id),
} }
} }
} }
#[cfg(test)]
mod tests {
use crate::global::get_or_init_test_data_database;
use super::*;
use uuid::{Timestamp, Uuid};
#[tokio::test]
async fn check_insert_record_deletion() {
let db = get_or_init_test_data_database().await;
assert!(ActiveModel{
table_name: Set("test_table".to_string()),
record_id: Set(crate::global::generate_uuid()),
..ActiveModel::new()
}.insert(db).await.is_ok());
}
}

View file

@ -1,4 +1,5 @@
use chrono::Local; use chrono::Local;
use libp2p::PeerId;
use sea_orm::entity::{ use sea_orm::entity::{
*, *,
prelude::* prelude::*
@ -32,34 +33,17 @@ pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {} impl ActiveModelBehavior for ActiveModel {}
impl ActiveModel { impl ActiveModel {
pub fn new() -> Self { pub fn new(peer_id: PeerId, note: String) -> Self {
let timestamp: DateTimeUtc = Local::now().to_utc(); let timestamp: DateTimeUtc = Local::now().to_utc();
Self{ Self{
id: Set(crate::global::generate_uuid()), id: Set(crate::global::generate_uuid()),
peer_id: Set(PeerIdValue::from(peer_id)),
created_at: Set(timestamp), created_at: Set(timestamp),
updated_at: Set(timestamp), updated_at: Set(timestamp),
..Default::default() synced_at: Set(None),
is_prefered: Set(false),
note: Set(note),
} }
} }
} }
#[cfg(test)]
mod tests {
use crate::global::get_or_init_test_data_database;
use super::*;
use libp2p::{identity, PeerId};
#[tokio::test]
async fn check_insert_node() {
let db = get_or_init_test_data_database().await;
ActiveModel{
peer_id: Set(PeerIdValue::from(PeerId::random())),
note: Set("test note".to_owned()),
..ActiveModel::new()
}.insert(db).await.unwrap();
}
}

View file

@ -62,10 +62,13 @@ enum RecordDeletion {
Table, Table,
Id, Id,
CreatedAt, CreatedAt,
CreatedBy,
TableName, TableName,
RecordId, RecordId,
} }
static FK_RECORD_DELETION_TRUSTED_NODE: &str = "fk_record_deletion_trusted_node";
#[async_trait::async_trait] #[async_trait::async_trait]
impl TableMigration for RecordDeletion { impl TableMigration for RecordDeletion {
async fn up<'a>(manager: &'a SchemaManager<'a>) -> Result<(), DbErr> { async fn up<'a>(manager: &'a SchemaManager<'a>) -> Result<(), DbErr> {
@ -75,6 +78,14 @@ impl TableMigration for RecordDeletion {
.if_not_exists() .if_not_exists()
.col(pk_uuid(Self::Id)) .col(pk_uuid(Self::Id))
.col(timestamp_with_time_zone(Self::CreatedAt)) .col(timestamp_with_time_zone(Self::CreatedAt))
.col(uuid(Self::CreatedBy))
.foreign_key(ForeignKey::create()
.name(FK_RECORD_DELETION_TRUSTED_NODE)
.from(Self::Table,Self::CreatedBy)
.to(TrustedNode::Table, TrustedNode::Id)
.on_delete(ForeignKeyAction::Cascade)
.on_update(ForeignKeyAction::Cascade)
)
.col(string(Self::TableName)) .col(string(Self::TableName))
.col(uuid(Self::RecordId)) .col(uuid(Self::RecordId))
.to_owned() .to_owned()

View file

@ -1,6 +1,6 @@
use sea_orm::{prelude::*, query::*, sea_query::SimpleExpr, *}; use sea_orm::{prelude::*, query::*, sea_query::SimpleExpr, *};
#[cfg(feature="macros")] #[cfg(feature="macros")]
pub use lazy_supplements_macros::SyncableModel; pub use caretta_macros::SyncableModel;
pub trait SyncableModel: ModelTrait<Entity = Self::SyncableEntity> { pub trait SyncableModel: ModelTrait<Entity = Self::SyncableEntity> {
type SyncableEntity: SyncableEntity<SyncableModel = Self>; type SyncableEntity: SyncableEntity<SyncableModel = Self>;
fn get_timestamp(&self) -> DateTimeUtc; fn get_timestamp(&self) -> DateTimeUtc;

View file

@ -9,8 +9,6 @@ use sea_orm::{prelude::*, Database};
use sea_orm_migration::MigratorTrait; use sea_orm_migration::MigratorTrait;
use tokio::sync::{OnceCell, RwLock, RwLockReadGuard, RwLockWriteGuard}; use tokio::sync::{OnceCell, RwLock, RwLockReadGuard, RwLockWriteGuard};
mod peers;
pub use peers::*;
mod config; mod config;
pub use config::*; pub use config::*;
mod database_connection; mod database_connection;

104
core/src/p2p/mod.rs Normal file
View file

@ -0,0 +1,104 @@
pub mod error;
use chrono::Local;
use libp2p::{ identity::Keypair, mdns, ping, swarm, Multiaddr, PeerId};
use sea_orm::{prelude::DateTimeUtc, ActiveModelTrait, ActiveValue::Set, ColumnTrait, EntityTrait, ModelTrait, QueryFilter};
use tracing::{event, Level};
use crate::{cache::entity::{CachedPeerActiveModel, CachedAddressActiveModel, CachedAddressColumn, CachedAddressEntity, CachedAddressModel, CachedPeerColumn, CachedPeerEntity, CachedPeerModel}, data::value::{MultiaddrValue, PeerIdValue}, error::Error, global::CACHE_DATABASE_CONNECTION};
#[derive(swarm::NetworkBehaviour)]
#[behaviour(to_swarm = "Event")]
pub struct Behaviour {
pub mdns: mdns::tokio::Behaviour,
pub ping: ping::Behaviour,
}
impl TryFrom<&Keypair> for Behaviour {
type Error = Error;
fn try_from(keypair: &Keypair) -> Result<Self, Error> {
Ok(Self {
mdns: mdns::tokio::Behaviour::new(
mdns::Config::default(),
keypair.public().into(),
)?,
ping: libp2p::ping::Behaviour::new(ping::Config::new()),
})
}
}
#[derive(Debug)]
pub enum Event {
Mdns(mdns::Event),
Ping(ping::Event),
}
impl Event {
pub async fn run(&self)
{
match self {
Self::Mdns(x) => {
match x {
mdns::Event::Discovered(e) => {
for peer in e.iter() {
event!(Level::TRACE, "Peer discovered via mdns: {}, {}", &peer.0, &peer.1);
match try_get_or_insert_cached_peer(&peer.0, &peer.1).await {
Ok(_) => {},
Err(e) => {
event!(Level::WARN, "{:?}", e);
}
};
}
},
_ => {},
}
},
_ => {}
}
}
}
impl From<mdns::Event> for Event {
fn from(event: mdns::Event) -> Self {
Self::Mdns(event)
}
}
impl From<ping::Event> for Event {
fn from(event: ping::Event) -> Self {
Self::Ping(event)
}
}
async fn try_get_or_insert_cached_peer(peer_id: &PeerId, peer_addr: &Multiaddr) -> Result<(CachedPeerModel, CachedAddressModel), Error> {
match (
CachedPeerEntity::find().filter(CachedPeerColumn::PeerId.eq(PeerIdValue::from(peer_id.clone()))).one(CACHE_DATABASE_CONNECTION.get()).await?,
CachedAddressEntity::find().filter(CachedAddressColumn::Address.eq(MultiaddrValue::from(peer_addr.clone()))).one(CACHE_DATABASE_CONNECTION.get()).await?,
) {
(Some(x), Some(y) ) => {
if x.id == y.cached_peer_id {
event!(Level::TRACE, "Known peer: {}, {}", peer_id, peer_addr);
let mut addr: CachedAddressActiveModel = y.into();
addr.last_used_at = Set(Local::now().to_utc());
let updated = addr.update(CACHE_DATABASE_CONNECTION.get()).await?;
Ok((x, updated))
} else {
y.delete(CACHE_DATABASE_CONNECTION.get()).await?;
Ok((x.clone(), CachedAddressActiveModel::new(x.id, peer_addr.clone()).insert(CACHE_DATABASE_CONNECTION.get()).await?))
}
}
(Some(x), None) => {
event!(Level::INFO, "New address {} for {}", peer_addr, peer_id);
Ok((x.clone(),CachedAddressActiveModel::new(x.id, peer_addr.clone()).insert(CACHE_DATABASE_CONNECTION.get()).await?))
},
(None, x) => {
event!(Level::INFO, "Add new peer: {}", peer_id);
let inserted = CachedPeerActiveModel::new(peer_id.clone()).insert(CACHE_DATABASE_CONNECTION.get()).await?;
if let Some(y) = x {
event!(Level::INFO, "Remove {} from {}", peer_addr, peer_id);
y.delete(CACHE_DATABASE_CONNECTION.get()).await?;
};
event!(Level::INFO, "Add address {} to {}", peer_addr, peer_id);
Ok((inserted.clone(), CachedAddressActiveModel::new(inserted.id, peer_addr.clone()).insert(CACHE_DATABASE_CONNECTION.get()).await?))
},
}
}

View file

@ -1,6 +1,6 @@
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
#[cfg(feature="macros")] #[cfg(feature="macros")]
pub use lazy_supplements_macros::Emptiable; pub use caretta_macros::Emptiable;
pub trait Emptiable{ pub trait Emptiable{
fn empty() -> Self; fn empty() -> Self;

View file

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

View file

@ -1,5 +1,5 @@
#[cfg(feature="macros")] #[cfg(feature="macros")]
pub use lazy_supplements_macros::Runnable; pub use caretta_macros::Runnable;
pub trait Runnable { pub trait Runnable {
async fn run(self); async fn run(self);

View file

@ -1,5 +1,5 @@
[package] [package]
name = "lazy-supplements-desktop" name = "caretta-desktop"
edition.workspace = true edition.workspace = true
version.workspace = true version.workspace = true
description.workspace = true description.workspace = true
@ -8,23 +8,25 @@ repository.workspace = true
[features] [features]
default = [] default = []
test = ["lazy-supplements-core/test"] test = ["caretta-core/test"]
[dependencies] [dependencies]
ciborium.workspace = true ciborium.workspace = true
clap.workspace = true clap.workspace = true
dirs = "6.0.0" dirs = "6.0.0"
lazy-supplements-core = { workspace = true, features = ["desktop"] } caretta-core = { workspace = true, features = ["desktop"] }
libp2p.workspace = true libp2p.workspace = true
prost = "0.14.1" prost = "0.14.1"
sea-orm.workspace = true
serde.workspace = true serde.workspace = true
thiserror.workspace = true thiserror.workspace = true
tokio.workspace = true tokio.workspace = true
tonic = "0.14.0" tonic = "0.14.0"
tonic-prost = "0.14.0"
uuid.workspace = true uuid.workspace = true
[dev-dependencies] [dev-dependencies]
lazy-supplements-core = {workspace = true, features = ["test"]} caretta-core = {workspace = true, features = ["test"]}
[build-dependencies] [build-dependencies]
tonic-prost-build = "0.14.0" tonic-prost-build = "0.14.0"

4
desktop/build.rs Normal file
View file

@ -0,0 +1,4 @@
fn main() -> Result<(), Box<dyn std::error::Error>> {
tonic_prost_build::compile_protos("proto/caretta.proto")?;
Ok(())
}

View file

@ -1,5 +1,5 @@
syntax = "proto3"; syntax = "proto3";
package lazy_supplements; package caretta;
enum PeerListOrderBy { enum PeerListOrderBy {
CREATED_AT = 0; CREATED_AT = 0;
@ -18,11 +18,13 @@ message CachedPeerListRequest {
} }
message CachedPeer { message CachedPeerMessage {
string peer_id = 1; string peer_id = 1;
repeated string multi_addresss = 2;
repeated string multi_addresss = 2;
} }
message CachedPeerListResponse { message CachedPeerListResponse {
repeated CachedPeer peers = 1; repeated CachedPeerMessage peers = 1;
} }

View file

@ -1,7 +1,7 @@
use std::{net::IpAddr, path::PathBuf}; use std::{net::IpAddr, path::PathBuf};
use clap::Args; use clap::Args;
use lazy_supplements_core::config::{BaseConfig, ConfigError}; use caretta_core::config::{BaseConfig, ConfigError};
use crate::config::{PartialP2pConfig, PartialStorageConfig}; use crate::config::{PartialP2pConfig, PartialStorageConfig};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};

View file

@ -1,5 +1,5 @@
use clap::Args; use clap::Args;
use lazy_supplements_core::utils::runnable::Runnable; use caretta_core::utils::runnable::Runnable;
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::P2P_CONFIG}; use crate::{error::Error, global::P2P_CONFIG};

View file

@ -1,9 +1,9 @@
pub mod rpc; pub mod rpc;
use clap::Args; use clap::Args;
pub use lazy_supplements_core::config::*; pub use caretta_core::config::*;
use lazy_supplements_core::utils::{emptiable::Emptiable, mergeable::Mergeable}; use caretta_core::utils::{emptiable::Emptiable, mergeable::Mergeable};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[cfg(unix)] #[cfg(unix)]
pub use rpc::*; pub use rpc::*;

View file

@ -1,6 +1,6 @@
use std::{net::{IpAddr, Ipv4Addr, SocketAddr, TcpListener}, path::PathBuf}; use std::{net::{IpAddr, Ipv4Addr, SocketAddr, TcpListener}, path::PathBuf};
use clap::Args; use clap::Args;
use lazy_supplements_core::{config::PartialConfig, utils::{emptiable::Emptiable, mergeable::Mergeable}}; use caretta_core::{config::PartialConfig, utils::{emptiable::Emptiable, mergeable::Mergeable}};
use libp2p::mdns::Config; use libp2p::mdns::Config;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};

View file

@ -1,6 +1,6 @@
use std::{path::PathBuf, sync::LazyLock}; use std::{path::PathBuf, sync::LazyLock};
pub use lazy_supplements_core::global::*; pub use caretta_core::global::*;
pub static DEFAULT_DATA_DIR_PATH: LazyLock<PathBuf> = LazyLock::new(|| { pub static DEFAULT_DATA_DIR_PATH: LazyLock<PathBuf> = LazyLock::new(|| {
let dir = if let Some(x) = dirs::data_local_dir() { let dir = if let Some(x) = dirs::data_local_dir() {

View file

@ -1,9 +1,9 @@
pub mod cli; pub mod cli;
pub mod config; pub mod config;
pub mod global; pub mod global;
pub mod ipc; pub mod rpc;
pub mod utils; pub mod utils;
pub use lazy_supplements_core::{ pub use caretta_core::{
cache, cache,
data, data,
error, error,

13
desktop/src/rpc/mod.rs Normal file
View file

@ -0,0 +1,13 @@
pub mod server;
pub mod proto {
use caretta_core::cache::entity::CachedPeerModel;
tonic::include_proto!("caretta");
impl From<CachedPeerModel> for CachedPeerMessage {
fn from(s: CachedPeerModel) -> Self {
todo!()
}
}
}

View file

@ -0,0 +1,20 @@
use caretta_core::{cache::entity::CachedPeerEntity, global::DATA_DATABASE_CONNECTION};
use tonic::{Request, Response, Status};
use crate::rpc::proto::{cached_peer_service_server::{CachedPeerService, CachedPeerServiceServer}, CachedPeerListRequest, CachedPeerListResponse};
use sea_orm::prelude::*;
#[derive(Debug, Default)]
pub struct CachedPeerServer {}
#[tonic::async_trait]
impl CachedPeerService for CachedPeerServer {
async fn list(&self, request: Request<CachedPeerListRequest>) -> Result<Response<CachedPeerListResponse>, Status> {
println!("Got a request: {:?}", request);
let reply = CachedPeerListResponse {
peers: CachedPeerEntity::find().all(DATA_DATABASE_CONNECTION.get()).await?
};
}
}

View file

@ -0,0 +1 @@
pub mod cached_peer;

1
desktop/src/utils.rs Normal file
View file

@ -0,0 +1 @@
pub use caretta_core::utils::*;

View file

@ -1,5 +1,5 @@
[package] [package]
name = "lazy-supplements-examples-core" name = "caretta-examples-core"
edition.workspace = true edition.workspace = true
version.workspace = true version.workspace = true
description.workspace = true description.workspace = true

View file

@ -1,5 +1,5 @@
[package] [package]
name = "lazy-supplements-examples-desktop" name = "caretta-examples-desktop"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
@ -8,8 +8,8 @@ edition = "2021"
[dependencies] [dependencies]
clap.workspace = true clap.workspace = true
dioxus.workspace = true dioxus.workspace = true
lazy-supplements-desktop.path = "../../lazy-supplements-desktop" caretta-desktop.path = "../../desktop"
lazy-supplements-examples-core.path = "../core" caretta-examples-core.path = "../core"
[features] [features]
default = ["desktop"] default = ["desktop"]

View file

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

View file

@ -1,5 +1,5 @@
mod cli; mod cli;
mod ipc; mod ipc;
fn main() { fn main() {
dioxus::launch(lazy_supplements_examples_core::ui::plain::App); dioxus::launch(caretta_examples_core::ui::plain::App);
} }

View file

@ -1,5 +1,5 @@
[package] [package]
name = "lazy-supplements-examples-mobile" name = "caretta-examples-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"
@ -8,7 +8,7 @@ edition = "2021"
[dependencies] [dependencies]
dioxus.workspace = true dioxus.workspace = true
lazy-supplements-examples-core.path = "../core" caretta-examples-core.path = "../core"
[features] [features]
default = ["mobile"] default = ["mobile"]

View file

@ -1,3 +1,3 @@
fn main() { fn main() {
dioxus::launch(lazy_supplements_examples_core::ui::plain::App); dioxus::launch(caretta_examples_core::ui::plain::App);
} }

View file

@ -1,8 +0,0 @@
mod peer;
pub use peer::{
ActiveModel as ActivePeerModel,
Column as PeerColumn,
Model as PeerModel,
Entity as PeerEntity,
};

View file

@ -1,73 +0,0 @@
use std::str::FromStr;
use chrono::{Days, Local};
use libp2p::{multiaddr, Multiaddr, PeerId};
use sea_orm::{entity::{
prelude::*, *
}, sea_query};
use serde::{Deserialize, Serialize};
use crate::data::value::{MultiaddrValue, PeerIdValue};
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Deserialize, Serialize)]
#[sea_orm(table_name = "peer")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: Uuid,
#[sea_orm(indexed)]
pub created_at: DateTimeUtc,
#[sea_orm(indexed)]
pub updated_at: DateTimeUtc,
#[sea_orm(indexed)]
pub expires_at: DateTimeUtc,
#[sea_orm(indexed)]
pub peer_id: PeerIdValue,
#[sea_orm(indexed)]
pub address: MultiaddrValue,
}
#[derive(Copy, Clone, Debug, DeriveRelation, EnumIter)]
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}
impl ActiveModel {
pub fn new(peer_id: PeerId, multiaddr: Multiaddr) -> Self {
let timestamp: DateTimeUtc = Local::now().to_utc();
Self{
peer_id: Set(PeerIdValue::from(peer_id)),
address: Set(MultiaddrValue::from(multiaddr)),
created_at: Set(timestamp),
updated_at: Set(timestamp),
expires_at: Set(timestamp.checked_add_days(Days::new(30)).unwrap()),
..Default::default()
}
}
}
#[cfg(test)]
mod tests {
use std::net::Ipv4Addr;
use crate::{cache::entity::peer, global::get_or_init_test_cache_database};
use super::*;
use libp2p::{identity::{self, Keypair}, swarm::handler::multi};
#[tokio::test]
async fn insert() {
let db = get_or_init_test_cache_database().await;
let peer_id = Keypair::generate_ed25519().public().to_peer_id();
let multiaddr = Multiaddr::empty()
.with(Ipv4Addr::new(127,0,0,1).into())
.with(multiaddr::Protocol::Tcp(0));
let inserted: Model = ActiveModel::new(peer_id.clone(), multiaddr.clone())
.insert(db).await.unwrap();
assert_eq!(PeerId::from(inserted.peer_id), peer_id);
assert_eq!(Multiaddr::from(inserted.address), multiaddr);
}
}

View file

@ -1,93 +0,0 @@
use sea_orm_migration::{prelude::*, schema::*};
use crate::migration::TableMigration;
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
Peer::up(manager).await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
Peer::down(manager).await?;
Ok(())
}
}
#[derive(DeriveIden, DeriveMigrationName)]
enum Peer {
Table,
Id,
PeerId,
CreatedAt,
UpdatedAt,
ExpiresAt,
Address,
}
static IDX_PEER_ADDRESS: &str = "idx_peer_address";
static IDX_PEER_PEER_ID: &str = "idx_peer_peer_id";
static IDX_PEER_CREATED_AT: &str = "idx_peer_created_at";
static IDX_PEER_UPDATED_AT: &str = "idx_peer_updated_at";
static IDX_PEER_EXPIRES_AT: &str = "idx_peer_expires_at";
#[async_trait::async_trait]
impl TableMigration for Peer {
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(string_len(Self::PeerId, 255))
.col(timestamp(Self::CreatedAt))
.col(timestamp(Self::UpdatedAt))
.col(timestamp(Self::ExpiresAt))
.col(text_uniq(Self::Address))
.to_owned()
).await?;
manager.create_index(
Index::create()
.name(IDX_PEER_PEER_ID)
.table(Self::Table)
.col(Self::PeerId)
.to_owned()
).await?;
manager.create_index(
Index::create()
.name(IDX_PEER_ADDRESS)
.table(Self::Table)
.col(Self::Address)
.to_owned()
).await?;
manager.create_index(
Index::create()
.name(IDX_PEER_CREATED_AT)
.table(Self::Table)
.col(Self::CreatedAt)
.to_owned()
).await?;
manager.create_index(
Index::create()
.name(IDX_PEER_UPDATED_AT)
.table(Self::Table)
.col(Self::UpdatedAt)
.to_owned()
).await?;
manager.create_index(
Index::create()
.name(IDX_PEER_EXPIRES_AT)
.table(Self::Table)
.col(Self::ExpiresAt)
.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,16 +0,0 @@
mod trusted_node;
mod record_deletion;
pub use trusted_node::{
ActiveModel as TrustedNodeActiveModel,
Column as TrustedNodeColumn,
Entity as TrustedNodeEntity,
Model as TrustedNodeModel,
};
pub use record_deletion::{
ActiveModel as RecordDeletionActiveModel,
Column as RecordDeletionColumn,
Entity as RecordDeletionEntity,
Model as RecordDeletionModel,
};

View file

@ -1,11 +0,0 @@
use std::collections::HashSet;
use libp2p::bytes::buf::UninitSlice;
use tokio::sync::{OnceCell, RwLock, RwLockReadGuard};
use crate::cache::entity::PeerModel;
use super::GlobalRwLock;
pub static PEERS: GlobalRwLock<HashSet<PeerModel>> = GlobalRwLock::const_new(stringify!(PEERS));

View file

@ -1,66 +0,0 @@
pub mod error;
use libp2p::{ identity::Keypair, mdns, ping, swarm};
use sea_orm::{ActiveModelTrait, ActiveValue::Set, ColumnTrait, EntityTrait, QueryFilter};
use crate::{cache::entity::{ActivePeerModel, PeerColumn, PeerEntity}, error::Error, global::{CACHE_DATABASE_CONNECTION, PEERS}};
#[derive(swarm::NetworkBehaviour)]
#[behaviour(to_swarm = "Event")]
pub struct Behaviour {
pub mdns: mdns::tokio::Behaviour,
pub ping: ping::Behaviour,
}
impl TryFrom<&Keypair> for Behaviour {
type Error = Error;
fn try_from(keypair: &Keypair) -> Result<Self, Error> {
Ok(Self {
mdns: mdns::tokio::Behaviour::new(
mdns::Config::default(),
keypair.public().into(),
)?,
ping: libp2p::ping::Behaviour::new(ping::Config::new()),
})
}
}
#[derive(Debug)]
pub enum Event {
Mdns(mdns::Event),
Ping(ping::Event),
}
impl Event {
pub async fn run(&self)
{
match self {
Self::Mdns(x) => {
match x {
mdns::Event::Discovered(e) => {
for peer in e.iter() {
match PeerEntity::find().filter(PeerColumn::PeerId.contains(&peer.0.to_string())).one(CACHE_DATABASE_CONNECTION.get()).await {
Ok(_) => {}
Err(_) => {
ActivePeerModel::new(peer.0.clone(), peer.1.clone())
.insert(CACHE_DATABASE_CONNECTION.get()).await;
}
}
}
},
_ => {},
}
},
_ => {}
}
}
}
impl From<mdns::Event> for Event {
fn from(event: mdns::Event) -> Self {
Self::Mdns(event)
}
}
impl From<ping::Event> for Event {
fn from(event: ping::Event) -> Self {
Self::Ping(event)
}
}

View file

@ -1,4 +0,0 @@
fn main() -> Result<(), Box<dyn std::error::Error>> {
tonic_prost_build::compile_protos("proto/lazy_supplements.proto")?;
Ok(())
}

View file

@ -1,11 +0,0 @@
#[cfg(unix)]
pub mod unix;
#[cfg(windows)]
pub mod windows;
#[cfg(unix)]
pub use unix::*;
#[cfg(windows)]
pub use windows::*;

View file

@ -1,50 +0,0 @@
use std::path::Path;
use tokio::{io::Interest, net::UnixStream};
use crate::{
error::Error,
ipc::message::{Request, Response, ResponseContent},
};
pub async fn request<T, U>(path: T, request: U) -> Result<ResponseContent, Error>
where
T: AsRef<Path>,
U: Into<Request>
{
let stream = UnixStream::connect(path).await?;
let ready = stream.ready(Interest::WRITABLE).await?;
let request: Request = request.into();
let mut response_buf = Vec::new();
if let Err(e) = ciborium::into_writer(&request, &mut response_buf) {
todo!();
};
match stream.try_write(&response_buf) {
Ok(x) => {
println!("write {} bytes", x)
}
Err(e) => {
return Err(e.into())
}
}
loop {
let ready_write = stream.ready(Interest::READABLE).await?;
let mut read_buf : Vec<u8> = Vec::new();
match stream.try_read_buf(&mut read_buf) {
Ok(x) => {
println!("read {} bytes", x)
}
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {
continue;
}
Err(e) => {
return Err(e.into())
}
}
let mut buf : Vec<u8> = Vec::new();
let response: Response = ciborium::from_reader_with_buffer(read_buf.as_slice(), &mut buf)?;
if response.id == request.id {
return Ok(response.content)
}
}
}

View file

@ -1,4 +0,0 @@
mod response;
mod request;
pub use response::*;
pub use request::*;

View file

@ -1,24 +0,0 @@
use lazy_supplements_core::global::generate_uuid;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Debug, Deserialize, Serialize)]
pub struct Request {
pub id: Uuid,
pub content: RequestContent,
}
impl From<RequestContent> for Request {
fn from(c: RequestContent) -> Self {
Self{
id: generate_uuid(),
content: c
}
}
}
#[derive(Debug, Deserialize, Serialize)]
pub enum RequestContent {
Ping,
ListPeers,
}

View file

@ -1,27 +0,0 @@
use lazy_supplements_core::{
global::generate_uuid,
cache::entity::PeerModel,
};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Debug, Deserialize, Serialize)]
pub struct Response {
pub id: Uuid,
pub content: ResponseContent,
}
impl From<ResponseContent> for Response {
fn from(c: ResponseContent) -> Self {
Self{
id: generate_uuid(),
content: c
}
}
}
#[derive(Debug, Deserialize, Serialize)]
pub enum ResponseContent {
Pong,
ListPeers(Vec<PeerModel>)
}

View file

@ -1,3 +0,0 @@
pub mod client;
pub mod server;
pub mod message;

View file

@ -1,11 +0,0 @@
#[cfg(unix)]
pub mod unix;
#[cfg(windows)]
pub mod windows;
#[cfg(unix)]
pub use unix::*;
#[cfg(windows)]
pub use windows::*;

View file

@ -1,69 +0,0 @@
use std::{collections::VecDeque, path::Path, sync::Arc};
use lazy_supplements_core::error::Error;
use tokio::{io::Interest, net::UnixStream, sync::Mutex};
use crate::ipc::message::{RequestContent, Response, ResponseContent};
pub async fn listen<T>(path: T) -> Result<(), Error>
where T: AsRef<Path> {
let stream = UnixStream::connect(path).await?;
let write_que: Arc<Mutex<VecDeque<Vec<u8>>>> = Arc::new(Mutex::new(VecDeque::new()));
let mut write_next: Option<Vec<u8>> = None;
loop {
let ready = stream.ready(Interest::READABLE).await?;
if ready.is_readable() {
let mut data = Vec::new();
match stream.try_read(&mut data) {
Ok(x) => {
println!("read {} bytes", x)
}
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {
continue;
}
Err(e) => {
return Err(e.into())
}
}
let write_que2 = write_que.clone();
tokio::spawn( async move {
let mut buf = Vec::new();
let request: crate::ipc::message::Request = ciborium::from_reader_with_buffer(data.as_slice(), &mut buf).unwrap();
let response_id = request.id;
let response_content: ResponseContent = match request.content {
RequestContent::Ping => {
ResponseContent::Pong
}
RequestContent::ListPeers => todo!(),
};
let mut response_buf = Vec::new();
if let Err(e) = ciborium::into_writer(&Response{
id: response_id,
content: response_content,
}, &mut response_buf) {
todo!();
};
let mut que = write_que2.lock().await;
que.push_back(response_buf);
});
} else if ready.is_writable() {
if let Some(x) = write_next.take() {
match stream.try_write(&x) {
Ok(x) => {
println!("write {} bytes", x)
}
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {
continue;
}
Err(e) => {
return Err(e.into())
}
}
}
}
let mut locked_que = write_que.lock().await;
write_next = locked_que.pop_front();
}
}

View file

@ -1 +0,0 @@
pub use lazy_supplements_core::utils::*;

View file

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

View file

@ -1,3 +0,0 @@
fn main() {
println!("Hello, world!");
}

View file

@ -1,5 +1,5 @@
[package] [package]
name = "lazy-supplements-macros" name = "caretta-macros"
edition.workspace = true edition.workspace = true
version.workspace = true version.workspace = true
description.workspace = true description.workspace = true
@ -17,7 +17,7 @@ syn = { version = "2.0.104", features = ["full"] }
[dev-dependencies] [dev-dependencies]
chrono.workspace = true chrono.workspace = true
lazy-supplements-core.workspace = true caretta-core.workspace = true
sea-orm.workspace = true sea-orm.workspace = true
tokio.workspace = true tokio.workspace = true
uuid.workspace = true uuid.workspace = true

View file

@ -1,7 +1,7 @@
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use lazy_supplements_core::utils::emptiable::Emptiable; use caretta_core::utils::emptiable::Emptiable;
use lazy_supplements_macros::Emptiable; use caretta_macros::Emptiable;
#[derive(Debug, PartialEq, Emptiable)] #[derive(Debug, PartialEq, Emptiable)]
struct EmptiableStruct{ struct EmptiableStruct{

View file

@ -1,7 +1,7 @@
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use lazy_supplements_core::utils::mergeable::Mergeable; use caretta_core::utils::mergeable::Mergeable;
use lazy_supplements_macros::Mergeable; use caretta_macros::Mergeable;
#[derive(Clone, Debug, PartialEq, Mergeable)] #[derive(Clone, Debug, PartialEq, Mergeable)]
struct MergeableStruct { struct MergeableStruct {

View file

@ -1,5 +1,5 @@
use lazy_supplements_core::utils::runnable::Runnable; use caretta_core::utils::runnable::Runnable;
use lazy_supplements_macros::Runnable; use caretta_macros::Runnable;
struct RunnableStruct1; struct RunnableStruct1;

View file

@ -6,8 +6,8 @@ use sea_orm::{
prelude::* prelude::*
} }
}; };
use lazy_supplements_core::data::syncable::*; use caretta_core::data::syncable::*;
use lazy_supplements_macros::SyncableModel; use caretta_macros::SyncableModel;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, SyncableModel)] #[derive(Clone, Debug, PartialEq, DeriveEntityModel, SyncableModel)]
#[sea_orm(table_name = "syncable")] #[sea_orm(table_name = "syncable")]

View file

@ -1,5 +1,5 @@
[package] [package]
name = "lazy-supplements-mobile" name = "caretta-mobile"
edition.workspace = true edition.workspace = true
version.workspace = true version.workspace = true
description.workspace = true description.workspace = true
@ -8,7 +8,7 @@ repository.workspace = true
[features] [features]
default = [] default = []
test = ["lazy-supplements-core/test"] test = ["caretta-core/test"]
[dependencies] [dependencies]
lazy-supplements-core.workspace = true caretta-core.workspace = true