diff --git a/Cargo.lock b/Cargo.lock index e57bc0a..540afd7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -141,6 +141,37 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "bevy_app" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4491cc4c718ae76b4c6883df58b94cc88b32dcd894ea8d5b603c7c7da72ca967" +dependencies = [ + "bevy_derive", + "bevy_ecs", + "bevy_platform", + "bevy_reflect", + "bevy_tasks", + "bevy_utils", + "cfg-if", + "ctrlc", + "downcast-rs", + "log", + "thiserror", + "variadics_please", +] + +[[package]] +name = "bevy_derive" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b837bf6c51806b10ebfa9edf1844ad80a3a0760d6c5fac4e90761df91a8901a" +dependencies = [ + "bevy_macro_utils", + "quote", + "syn", +] + [[package]] name = "bevy_ecs" version = "0.16.1" @@ -282,6 +313,14 @@ dependencies = [ "thread_local", ] +[[package]] +name = "bevy_vanth" +version = "0.1.0" +dependencies = [ + "bevy_app", + "bevy_ecs", +] + [[package]] name = "bitflags" version = "2.9.1" @@ -352,6 +391,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "clap" version = "4.5.42" @@ -445,6 +490,16 @@ dependencies = [ "typenum", ] +[[package]] +name = "ctrlc" +version = "3.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46f93780a459b7d656ef7f071fe699c4d3d2cb201c4b24d085b6ddc505276e73" +dependencies = [ + "nix", + "windows-sys", +] + [[package]] name = "derive_more" version = "1.0.0" @@ -750,6 +805,18 @@ version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +[[package]] +name = "nix" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "nonmax" version = "0.5.5" @@ -876,6 +943,15 @@ dependencies = [ "termtree", ] +[[package]] +name = "proc-macro-crate" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +dependencies = [ + "toml_edit", +] + [[package]] name = "proc-macro2" version = "1.0.95" @@ -901,10 +977,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] -name = "rand_chacha" -version = "0.3.1" +name = "rand" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", "rand_core", @@ -913,9 +999,12 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.4" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom", +] [[package]] name = "redox_syscall" @@ -1260,6 +1349,7 @@ dependencies = [ "bevy_ecs", "blake3", "digest", + "rand", "rusqlite", "serde", "serde_json", @@ -1286,6 +1376,7 @@ dependencies = [ name = "vanth_derive" version = "0.1.0" dependencies = [ + "proc-macro-crate", "proc-macro2", "quote", "syn", diff --git a/Cargo.toml b/Cargo.toml index 2506e61..140cd0f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ name = "vanth" [workspace.dependencies] clap = { version = "4.5.42", features = ["derive"] } bevy_ecs = "0.16.1" +bevy_app = "0.16.1" bincode = "2.0.1" serde = { version = "1.0.219", features = ["derive"] } serde_json = "1.0.140" @@ -22,8 +23,10 @@ syn = { version = "2.0", features = ["full"] } proc-macro2 = "1.0" rusqlite = { version = "0.32.1", features = ["bundled"] } tempfile = "3.12.0" -rand_core = "0.6.4" -rand_chacha = { version = "0.3.1", features = ["serde1"] } +rand = "0.9.2" +rand_chacha = { version = "0.9.0", features = ["serde"] } +rand_core = "0.9.0" tracing = "0.1.41" tracing-subscriber = "0.3.19" assert_cmd = "2.0.17" +proc-macro-crate = "3.3.0" diff --git a/README.md b/README.md index 24e878c..1a7afbc 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # Vanth -Vanth is a content-addressed database as a library designed for entity-component-system (ECS) applications. +Vanth is a content-addressed data storage and task memoization framework designed for entity-component-system (ECS) applications. + +Inspired by [Salsa](https://github.com/salsa-rs/salsa) and [Bevy](https://bevy.org/). It is currently experimental and should not be used for anything. diff --git a/crates/bevy_vanth/Cargo.toml b/crates/bevy_vanth/Cargo.toml new file mode 100644 index 0000000..90f4213 --- /dev/null +++ b/crates/bevy_vanth/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "bevy_vanth" +version.workspace = true +edition.workspace = true + +[dependencies] +bevy_ecs.workspace = true +bevy_app.workspace = true diff --git a/crates/bevy_vanth/src/lib.rs b/crates/bevy_vanth/src/lib.rs new file mode 100644 index 0000000..77e67aa --- /dev/null +++ b/crates/bevy_vanth/src/lib.rs @@ -0,0 +1,20 @@ +use bevy_app::Plugin; +use bevy_ecs::component::Component; + +#[derive(Component)] +pub struct VanthRoot {} + +pub struct VanthPlugin {} + +impl Plugin for VanthPlugin { + fn build(&self, app: &mut bevy_app::App) { + // TODO: Allow specifying custom schedules. + app.add_systems(bevy_app::FixedPreUpdate, run_vanth).finish(); + } +} + +impl VanthPlugin {} + +fn run_vanth() { + +} diff --git a/crates/vanth/Cargo.toml b/crates/vanth/Cargo.toml index 59003fb..34d49fc 100644 --- a/crates/vanth/Cargo.toml +++ b/crates/vanth/Cargo.toml @@ -16,6 +16,7 @@ blake3.workspace = true vanth_derive = { path = "../vanth_derive" } rusqlite.workspace = true tracing.workspace = true +rand = { workspace = true } [dev-dependencies] tempfile = { workspace = true } diff --git a/crates/vanth/examples/simple.rs b/crates/vanth/examples/simple.rs index 44a0d9c..7b7be60 100644 --- a/crates/vanth/examples/simple.rs +++ b/crates/vanth/examples/simple.rs @@ -1,12 +1,12 @@ -use serde::{Deserialize, Serialize}; -use vanth::{hash, Vanth}; +// use serde::{Deserialize, Serialize}; +// use vanth::{hash, Vanth}; -#[derive(Clone, Debug, Deserialize, Serialize, Vanth)] -struct Foo { - value: i32, -} +// #[derive(Clone, Debug, Deserialize, Serialize, Vanth)] +// struct Foo { +// value: i32, +// } fn main() { - let x = "hello"; - println!("Hash: {:?}", hash(&x).hex()); +// let x = "hello"; +// println!("Hash: {:?}", hash(&x).hex()); } diff --git a/crates/vanth/src/entity.rs b/crates/vanth/src/entity.rs index e42b362..b3374b5 100644 --- a/crates/vanth/src/entity.rs +++ b/crates/vanth/src/entity.rs @@ -1,11 +1,12 @@ +use crate::hash; +use crate::hashing_serializer::{self, HashingSerializer}; +use rand::rngs::OsRng; +use rand::TryRngCore; use serde::{Deserialize, Serialize}; use std::fmt::{self, Debug, Display}; use std::hash::{Hash, Hasher}; use std::marker::PhantomData; -use crate::hash; -use crate::hashing_serializer::{self, HashingSerializer}; - #[derive(Clone, Copy, Deserialize)] pub struct EntityId([u8; 32]); @@ -16,7 +17,7 @@ impl From for EntityId { } /// A generic identifier type that can be used for different entity types -#[derive(Clone, Copy, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] pub struct Id { /// The raw identifier value pub value: [u8; 32], @@ -37,14 +38,7 @@ impl Id { /// Generate a random ID pub fn random() -> Self { let mut value = [0u8; 32]; - // Simple random generation for demonstration - for i in 0..32 { - value[i] = (std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_nanos() as u8) - .wrapping_add(i as u8); - } + OsRng.try_fill_bytes(&mut value).unwrap(); Self::new(value) } @@ -67,42 +61,6 @@ impl Id { } Self::new(bytes) } - - /// Convert the ID to a pair of u128 values for handling larger values - /// Returns (high_bits, low_bits) - pub fn to_u128_pair(&self) -> (u128, u128) { - let mut high = 0u128; - let mut low = 0u128; - - // First 16 bytes go to low - for i in 0..16 { - low |= (self.value[i] as u128) << (i * 8); - } - - // Next 16 bytes go to high - for i in 0..16 { - high |= (self.value[i + 16] as u128) << (i * 8); - } - - (high, low) - } - - /// Create an ID from a pair of u128 values - pub fn from_u128_pair(high: u128, low: u128) -> Self { - let mut bytes = [0u8; 32]; - - // Low bits fill first 16 bytes - for i in 0..16 { - bytes[i] = ((low >> (i * 8)) & 0xFF) as u8; - } - - // High bits fill next 16 bytes - for i in 0..16 { - bytes[i + 16] = ((high >> (i * 8)) & 0xFF) as u8; - } - - Self::new(bytes) - } } impl PartialEq for Id { @@ -119,20 +77,6 @@ impl Hash for Id { } } -impl Debug for Id { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let (high, low) = self.to_u128_pair(); - write!(f, "Id<{}>({:016x}{:016x})", std::any::type_name::(), high, low) - } -} - -impl Display for Id { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let (high, low) = self.to_u128_pair(); - write!(f, "{:016x}{:016x}", high, low) - } -} - pub struct ContentHash { value: [u8; 32], } diff --git a/crates/vanth/src/lib.rs b/crates/vanth/src/lib.rs index ab37350..16e9bfe 100644 --- a/crates/vanth/src/lib.rs +++ b/crates/vanth/src/lib.rs @@ -7,10 +7,12 @@ use crate::entity::EntityId; pub mod entity; pub mod hashing_serializer; +pub mod net; pub mod nix; pub mod store; pub use hashing_serializer::hash; +pub use net::Node; pub use vanth_derive::Vanth; pub type Result = std::result::Result; @@ -19,41 +21,6 @@ pub enum Error { Other(String), } - -/// A view of all of the [`Node`]s in a cluster. -pub struct Network { - // TODO -} - -/// A Vanth server. -pub struct Node { - // TODO -} - -impl Node { - pub fn new() -> Self { - Self {} - } - - pub fn entity_count(&self) -> usize { - todo!() - } - - pub fn run() { - todo!() - } - - pub fn save(entity_id: impl Into) -> Result<()> { - // TODO - Ok(()) - } - - // pub fn load(entity_id: impl Into) -> Result> { - // // TODO - // Ok(None) - // } -} - #[derive(Clone, Debug, Deserialize, Serialize)] pub struct HashedValue { content_hash: ContentHash, @@ -75,9 +42,15 @@ pub struct Value { data: Vec, } +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Hash)] +pub struct EventTy(Ty); + +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Hash)] +pub struct TaskTy(Ty); + /// A wrapper for the fully-qualified name of a Rust type. This should be univerisally unique for a given type within a /// given project. -#[derive(Clone, Debug, Deserialize, Serialize, Eq, Hash)] +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Hash)] pub struct Ty { pub path: Vec, } @@ -88,12 +61,6 @@ impl ToString for Ty { } } -impl PartialEq for Ty { - fn eq(&self, other: &Self) -> bool { - self.path == other.path - } -} - impl> PartialEq for Ty { fn eq(&self, other: &T) -> bool { self.to_string() == *other.as_ref() @@ -182,5 +149,49 @@ pub struct Handle { _marker: PhantomData, } +pub trait Task { + type Output: Vanth + Serialize + DeserializeOwned; + + // TODO: Make async? + fn execute(self) -> Self::Output; +} + +/// A representation of the approximate computational cost of completing a task. +pub struct Cost { + value: f64, +} + /// A world which Vanth entities live in. Lifetimes `'v` of [`Vanth<'v>`] types are tied to the lifetime of the `Root`. -pub struct Root {} +pub struct Root { + current_epoch: u64, +} + +pub trait Module { + fn register(world_context: &mut ModuleRegistration); +} + +pub struct ModuleRegistration { + events: Vec, +} + +impl ModuleRegistration {} + +pub trait ModuleTools: Module { + type Tool: Tool; + + fn tools(&self) -> impl Iterator; +} + +pub trait Tool: Send + Sync { + type Input; + type Output; + type Error: std::error::Error; + + fn name<'a>() -> &'a str; + fn description<'a>() -> &'a str; + + fn call( + &self, + input: Self::Input, + ) -> impl Future> + Send; +} diff --git a/crates/vanth/src/net.rs b/crates/vanth/src/net.rs new file mode 100644 index 0000000..af162d1 --- /dev/null +++ b/crates/vanth/src/net.rs @@ -0,0 +1,63 @@ +use serde::{Deserialize, Serialize}; + +use crate::{Error, Result}; +use crate::{ + Vanth, + entity::{EntityId, Id}, +}; + +pub struct Packet { + pub source_node: Id, + pub signature: Vec, + pub message: Message, +} + +#[derive(Clone, Debug, Deserialize, Serialize, Vanth)] +pub struct Message { + pub events: Vec, +} + +#[derive(Clone, Debug, Deserialize, Serialize, Vanth)] +pub enum Event {} + +/// A view of all of the [`Node`]s in a cluster. +pub struct Network { + // TODO +} + +/// A Vanth server. +pub struct Node { + id: Id, +} + +impl Node { + pub fn new() -> Self { + Self { + id: Id::random(), + } + } + + pub fn with_id(id: Id) -> Self { + Self { + id, + } + } + + pub fn entity_count(&self) -> usize { + todo!() + } + + pub fn run() { + todo!() + } + + pub fn save(entity_id: impl Into) -> Result<()> { + // TODO + Ok(()) + } + + // pub fn load(entity_id: impl Into) -> Result> { + // // TODO + // Ok(None) + // } +} diff --git a/crates/vanth_derive/Cargo.toml b/crates/vanth_derive/Cargo.toml index 4c10d21..1daa430 100644 --- a/crates/vanth_derive/Cargo.toml +++ b/crates/vanth_derive/Cargo.toml @@ -9,4 +9,5 @@ proc-macro = true [dependencies] quote.workspace = true syn.workspace = true -proc-macro2.workspace = true \ No newline at end of file +proc-macro2.workspace = true +proc-macro-crate.workspace = true diff --git a/crates/vanth_derive/src/lib.rs b/crates/vanth_derive/src/lib.rs index 277406d..7b003e8 100644 --- a/crates/vanth_derive/src/lib.rs +++ b/crates/vanth_derive/src/lib.rs @@ -1,4 +1,6 @@ use proc_macro::TokenStream; +use proc_macro_crate::{crate_name, FoundCrate}; +use proc_macro2::Span; use quote::quote; use syn::parse_quote; use syn::{DeriveInput, GenericParam, Generics, parse_macro_input}; @@ -11,6 +13,15 @@ pub fn vanth_derive(input: TokenStream) -> TokenStream { let mut generics = input.generics.clone(); + let vanth_path = match crate_name("vanth") { + Ok(FoundCrate::Itself) => quote!(crate), + Ok(FoundCrate::Name(name)) => { + let ident = syn::Ident::new(&name, Span::call_site()); + quote!(#ident) + } + Err(_) => quote!(vanth), + }; + let type_params: Vec = generics .params .iter() @@ -25,7 +36,7 @@ pub fn vanth_derive(input: TokenStream) -> TokenStream { let mut where_clause = generics.where_clause.clone().unwrap_or_else(|| parse_quote!(where)); for tp in &type_params { - where_clause.predicates.push(parse_quote!(#tp : vanth::Vanth)); + where_clause.predicates.push(parse_quote!(#tp : #vanth_path::Vanth)); } let (impl_generics, ty_generics, _) = generics.split_for_impl(); @@ -34,19 +45,19 @@ pub fn vanth_derive(input: TokenStream) -> TokenStream { quote! { String::new() } } else { quote! { - format!("<{}>", [#(#type_params::ty().path.join("::")),*].join(",")) + format!("<{}>", [#( <#type_params as #vanth_path::Vanth>::ty().path.join("::") ),*].join(",")) } }; let expanded = quote! { - impl #impl_generics vanth::Vanth for #name #ty_generics #where_clause { - fn ty() -> vanth::Ty { + impl #impl_generics #vanth_path::Vanth for #name #ty_generics #where_clause { + fn ty() -> #vanth_path::Ty { let module_path = module_path!(); let mut path: Vec = module_path.split("::").map(|s| s.to_string()).collect(); let base_name = stringify!(#name); let generics_str = #generics_code; path.push(format!("{}{}", base_name, generics_str)); - vanth::Ty { path } + #vanth_path::Ty { path } } } }; diff --git a/flake.nix b/flake.nix index a80ad09..02f41dd 100644 --- a/flake.nix +++ b/flake.nix @@ -94,6 +94,18 @@ ''; } ); + + clippyCheck = craneLib.cargoClippy ( + commonArgs + // craneArgs + // { + inherit cargoArtifacts; + pname = "vanth-clippy"; + version = "0.1.0"; + # Fail on any lint warning and cover all targets/features. + cargoClippyExtraArgs = "--all-targets --all-features -- -D warnings"; + } + ); in { packages = rec { @@ -101,6 +113,10 @@ default = vanth; }; + checks = { + # clippy = clippyCheck; + }; + devShells.default = craneLib.devShell { inputsFrom = [ vanth ]; packages = packages;