Rename project to caretta
This commit is contained in:
parent
25ea7ed652
commit
c39a28388d
100 changed files with 550 additions and 583 deletions
|
@ -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"] }
|
||||||
|
|
|
@ -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
|
|
@ -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
57
core/src/cache/entity/cached_address.rs
vendored
Normal 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
48
core/src/cache/entity/cached_peer.rs
vendored
Normal 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
46
core/src/cache/entity/mod.rs
vendored
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
139
core/src/cache/migration/m20220101_000001_create_cache_tables.rs
vendored
Normal file
139
core/src/cache/migration/m20220101_000001_create_cache_tables.rs
vendored
Normal 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
|
||||||
|
}
|
||||||
|
}
|
35
core/src/data/entity/mod.rs
Normal file
35
core/src/data/entity/mod.rs
Normal 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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()
|
|
@ -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;
|
|
@ -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
104
core/src/p2p/mod.rs
Normal 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?))
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
|
@ -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);
|
||||||
}
|
}
|
|
@ -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);
|
|
@ -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
4
desktop/build.rs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
tonic_prost_build::compile_protos("proto/caretta.proto")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -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;
|
||||||
}
|
}
|
|
@ -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};
|
|
@ -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};
|
|
@ -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::*;
|
|
@ -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};
|
||||||
|
|
|
@ -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() {
|
|
@ -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
13
desktop/src/rpc/mod.rs
Normal 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!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
desktop/src/rpc/server/cached_peer.rs
Normal file
20
desktop/src/rpc/server/cached_peer.rs
Normal 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?
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
1
desktop/src/rpc/server/mod.rs
Normal file
1
desktop/src/rpc/server/mod.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub mod cached_peer;
|
1
desktop/src/utils.rs
Normal file
1
desktop/src/utils.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub use caretta_core::utils::*;
|
|
@ -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
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
|
@ -1,8 +0,0 @@
|
||||||
mod peer;
|
|
||||||
|
|
||||||
pub use peer::{
|
|
||||||
ActiveModel as ActivePeerModel,
|
|
||||||
Column as PeerColumn,
|
|
||||||
Model as PeerModel,
|
|
||||||
Entity as PeerEntity,
|
|
||||||
};
|
|
73
lazy-supplements-core/src/cache/entity/peer.rs
vendored
73
lazy-supplements-core/src/cache/entity/peer.rs
vendored
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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,
|
|
||||||
};
|
|
|
@ -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));
|
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
tonic_prost_build::compile_protos("proto/lazy_supplements.proto")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
#[cfg(unix)]
|
|
||||||
pub mod unix;
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
pub mod windows;
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
pub use unix::*;
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
pub use windows::*;
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
mod response;
|
|
||||||
mod request;
|
|
||||||
pub use response::*;
|
|
||||||
pub use request::*;
|
|
|
@ -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,
|
|
||||||
}
|
|
|
@ -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>)
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
pub mod client;
|
|
||||||
pub mod server;
|
|
||||||
pub mod message;
|
|
|
@ -1,11 +0,0 @@
|
||||||
#[cfg(unix)]
|
|
||||||
pub mod unix;
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
pub mod windows;
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
pub use unix::*;
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
pub use windows::*;
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
pub use lazy_supplements_core::utils::*;
|
|
|
@ -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
|
|
|
@ -1,3 +0,0 @@
|
||||||
fn main() {
|
|
||||||
println!("Hello, world!");
|
|
||||||
}
|
|
|
@ -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
|
|
@ -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{
|
|
@ -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 {
|
|
@ -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;
|
||||||
|
|
|
@ -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")]
|
|
@ -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
|
Loading…
Add table
Reference in a new issue