♻️🍱💩👷 crates/vanth, project: Refactor entity system and setup; migrate to Nix flakes

- Add `.cargo/config.toml` with target directory and rustflags configuration.
- Remove devenv files and set up Nix flake configuration with `flake.nix` and `flake.lock`.
- Add dependencies on `blake3`, `digest`, and hashing-related crates for content hashing functionality.
- Implement `EntityId` as hashed representation and `ContentHash` component using Blake3.
- Add `hashing_serializer` module for serializing and hashing components.
- Remove unused `parser` module and restructure lib.rs with new `Reference` type and storage interfaces.
- Add test files for `store` and `reference` functionality with TODOs for implementation.
- Introduce `VanthPlugin` skeleton and entity save/load interfaces with placeholder implementations.
- Add `Reference` type with retrieval state machine and async handling stubs.
This commit is contained in:
Markus Scully 2025-08-04 21:03:15 +03:00
parent be75844fdd
commit 45a68865b6
Signed by: mascully
GPG key ID: 93CA5814B698101C
19 changed files with 918 additions and 261 deletions

3
.cargo/config.toml Normal file
View file

@ -0,0 +1,3 @@
[build]
target-dir = "target/cargo"
rustflags = ["-A", "unused", "-W", "unused_must_use"]

3
.envrc
View file

@ -1,3 +0,0 @@
source_url "https://raw.githubusercontent.com/cachix/devenv/95f329d49a8a5289d31e0982652f7058a189bfca/direnvrc" "sha256-d+8cBpDfDBj41inrADaJt+bDWhOktwslgoP5YiGJ1v0="
use devenv

1
.rules
View file

@ -1 +0,0 @@
/home/asraelite/base/ai_rules/zed.md

101
Cargo.lock generated
View file

@ -2,6 +2,12 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "arrayref"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
[[package]]
name = "arrayvec"
version = "0.7.6"
@ -238,6 +244,29 @@ dependencies = [
"serde",
]
[[package]]
name = "blake3"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0"
dependencies = [
"arrayref",
"arrayvec",
"cc",
"cfg-if",
"constant_time_eq",
"digest",
]
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "bumpalo"
version = "3.19.0"
@ -250,6 +279,15 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cc"
version = "1.2.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7"
dependencies = [
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.1"
@ -272,6 +310,12 @@ dependencies = [
"portable-atomic",
]
[[package]]
name = "constant_time_eq"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
[[package]]
name = "critical-section"
version = "1.2.0"
@ -293,6 +337,16 @@ version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "ctrlc"
version = "3.4.7"
@ -324,6 +378,17 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
"subtle",
]
[[package]]
name = "disqualified"
version = "1.0.0"
@ -395,6 +460,16 @@ dependencies = [
"pin-project-lite",
]
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "getrandom"
version = "0.3.3"
@ -657,6 +732,12 @@ dependencies = [
"serde",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "slab"
version = "0.4.10"
@ -693,6 +774,12 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "2.0.104"
@ -756,6 +843,12 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c"
[[package]]
name = "typenum"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
[[package]]
name = "unicode-ident"
version = "1.0.18"
@ -786,6 +879,8 @@ version = "0.1.0"
dependencies = [
"bevy_app",
"bevy_ecs",
"blake3",
"digest",
"serde",
"serde_json",
]
@ -801,6 +896,12 @@ dependencies = [
"syn",
]
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "wasi"
version = "0.14.2+wasi-0.2.4"

View file

@ -9,5 +9,8 @@ edition = "2024"
[workspace.dependencies]
bevy_app = "0.16.1"
bevy_ecs = "0.16.1"
bitcode = "0.6.6"
serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.140"
digest = "0.10.7"
blake3 = { version = "1.8.2", features = ["traits-preview"] }

View file

@ -9,5 +9,7 @@ path = "src/lib.rs"
[dependencies]
bevy_app.workspace = true
bevy_ecs.workspace = true
digest.workspace = true
serde.workspace = true
serde_json.workspace = true
blake3.workspace = true

View file

@ -3,6 +3,18 @@ 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]);
impl From<String> for EntityId {
fn from(value: String) -> Self {
Self(hash(&value).hash)
}
}
/// A generic identifier type that can be used for different entity types
#[derive(Clone, Copy, Serialize, Deserialize)]
pub struct Id<T: ?Sized> {

View file

@ -0,0 +1,390 @@
// Stolen and adapted from https://github.com/fjarri/hashing-serializer/blob/master/src/lib.rs
use std::fmt;
use digest::Update;
use serde::{
ser::{
self, SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant, SerializeTuple,
SerializeTupleStruct, SerializeTupleVariant,
},
Serialize, Serializer,
};
use crate::ContentHash;
pub fn hash(value: &impl Serialize) -> ContentHash {
let mut digest = blake3::Hasher::new();
let mut serializer = HashingSerializer { digest: &mut digest };
// TODO: Don't unwrap.
serializer.serialize_value(value).unwrap();
ContentHash { hash: *serializer.digest.finalize().as_bytes() }
}
/// A serializer that hashes the data instead of serializing it.
pub struct HashingSerializer<'a, T: Update> {
/// A reference to the digest that will accumulate the data.
pub digest: &'a mut T,
}
/// Possible errors during serialization.
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Error {
/// The type's [`serde::Serialize`] impl tried to serialize a sequence of undefined length.
UndefinedSequenceLength,
/// Sequence length does not fit into `u128`.
///
/// Really, this shouldn't ever happen.
SequenceLengthTooLarge,
/// [`Serializer::collect_str`] got called, but heap memory allocation is not available.
///
/// Can only be returned if `alloc` feature not enabled.
CannotCollectStr,
/// Custom `serde` error, but memory allocation is not available.
/// Set a breakpoint where this is thrown for more information.
///
/// Can only be returned if `alloc` feature not enabled).
CustomErrorNoAlloc,
/// Custom `serde` error.
CustomError(String),
}
impl ser::Error for Error {
fn custom<T: fmt::Display>(msg: T) -> Self {
Self::CustomError(format!("{}", msg))
}
}
impl ser::StdError for Error {}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
/// Converts the `usize` sequence length to a fixed length type,
/// since we want the result to be portable.
fn try_into_sequence_length(len: usize) -> Result<u128, Error> {
u128::try_from(len)
.ok()
.ok_or(Error::SequenceLengthTooLarge)
}
// Implement `serialize_$ty` for int types
macro_rules! impl_trivial_serialize {
($method_name:ident , $t:ty) => {
fn $method_name(self, v: $t) -> Result<Self::Ok, Self::Error> {
self.digest.update(&v.to_be_bytes());
Ok(())
}
};
}
macro_rules! impl_serialize_trait {
($method_name:ident , $t:ty) => {
fn $method_name(self, v: $t) -> Result<Self::Ok, Self::Error> {
self.digest.update(&v.to_be_bytes());
Ok(())
}
};
}
impl<'a, T: Update> Serializer for HashingSerializer<'a, T> {
type Ok = ();
type Error = Error;
type SerializeSeq = Self;
type SerializeTuple = Self;
type SerializeTupleStruct = Self;
type SerializeTupleVariant = Self;
type SerializeMap = Self;
type SerializeStruct = Self;
type SerializeStructVariant = Self;
fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
self.digest.update(&(v as u8).to_be_bytes());
Ok(())
}
impl_trivial_serialize!(serialize_i8, i8);
impl_trivial_serialize!(serialize_i16, i16);
impl_trivial_serialize!(serialize_i32, i32);
impl_trivial_serialize!(serialize_i64, i64);
impl_trivial_serialize!(serialize_u8, u8);
impl_trivial_serialize!(serialize_u16, u16);
impl_trivial_serialize!(serialize_u32, u32);
impl_trivial_serialize!(serialize_u64, u64);
impl_trivial_serialize!(serialize_f32, f32);
impl_trivial_serialize!(serialize_f64, f64);
fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> {
// `char` is always at most 4 bytes, regardless of the platform,
// so this conversion is safe.
self.digest.update(&u64::from(v).to_be_bytes());
Ok(())
}
fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
self.digest.update(v.as_ref());
Ok(())
}
fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> {
self.digest.update(v);
Ok(())
}
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
self.digest.update(&[0]);
Ok(())
}
fn serialize_some<V: ?Sized + Serialize>(self, value: &V) -> Result<Self::Ok, Self::Error> {
self.digest.update(&[1]);
value.serialize(self)
}
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
Ok(())
}
fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
Ok(())
}
fn serialize_unit_variant(
self,
_name: &'static str,
variant_index: u32,
_variant: &'static str,
) -> Result<Self::Ok, Self::Error> {
self.digest.update(&variant_index.to_be_bytes());
Ok(())
}
fn serialize_newtype_struct<V: ?Sized + Serialize>(
self,
_name: &'static str,
value: &V,
) -> Result<Self::Ok, Self::Error> {
value.serialize(self)
}
fn serialize_newtype_variant<V: ?Sized + Serialize>(
self,
_name: &'static str,
variant_index: u32,
_variant: &'static str,
value: &V,
) -> Result<Self::Ok, Self::Error> {
self.digest.update(&variant_index.to_be_bytes());
value.serialize(self)
}
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
let len = len.ok_or(Error::UndefinedSequenceLength)?;
self.digest
.update(&try_into_sequence_length(len)?.to_be_bytes());
Ok(self)
}
fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error> {
self.digest
.update(&try_into_sequence_length(len)?.to_be_bytes());
Ok(self)
}
fn serialize_tuple_struct(
self,
_name: &'static str,
len: usize,
) -> Result<Self::SerializeTupleStruct, Self::Error> {
self.digest
.update(&try_into_sequence_length(len)?.to_be_bytes());
Ok(self)
}
fn serialize_tuple_variant(
self,
_name: &'static str,
variant_index: u32,
_variant: &'static str,
len: usize,
) -> Result<Self::SerializeTupleVariant, Self::Error> {
self.digest.update(&variant_index.to_be_bytes());
self.digest
.update(&try_into_sequence_length(len)?.to_be_bytes());
Ok(self)
}
fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
let len = len.ok_or(Error::UndefinedSequenceLength)?;
self.digest
.update(&try_into_sequence_length(len)?.to_be_bytes());
Ok(self)
}
fn serialize_struct(
self,
_name: &'static str,
len: usize,
) -> Result<Self::SerializeStruct, Self::Error> {
self.digest
.update(&try_into_sequence_length(len)?.to_be_bytes());
Ok(self)
}
fn serialize_struct_variant(
self,
_name: &'static str,
variant_index: u32,
_variant: &'static str,
len: usize,
) -> Result<Self::SerializeStructVariant, Self::Error> {
self.digest.update(&variant_index.to_be_bytes());
self.digest
.update(&try_into_sequence_length(len)?.to_be_bytes());
Ok(self)
}
fn collect_str<V: fmt::Display + ?Sized>(self, _: &V) -> Result<Self::Ok, Self::Error> {
Err(Error::CannotCollectStr)
}
fn is_human_readable(&self) -> bool {
false
}
}
impl<'a, T: Update> SerializeSeq for HashingSerializer<'a, T> {
type Ok = ();
type Error = Error;
fn serialize_element<V: ?Sized + Serialize>(&mut self, value: &V) -> Result<Self::Ok, Error> {
value.serialize(HashingSerializer {
digest: self.digest,
})?;
Ok(())
}
fn end(self) -> Result<Self::Ok, Error> {
Ok(())
}
}
impl<'a, T: Update> SerializeTuple for HashingSerializer<'a, T> {
type Ok = ();
type Error = Error;
fn serialize_element<V: ?Sized + Serialize>(&mut self, value: &V) -> Result<Self::Ok, Error> {
value.serialize(HashingSerializer {
digest: self.digest,
})?;
Ok(())
}
fn end(self) -> Result<Self::Ok, Error> {
Ok(())
}
}
impl<'a, T: Update> SerializeTupleStruct for HashingSerializer<'a, T> {
type Ok = ();
type Error = Error;
fn serialize_field<V: ?Sized + Serialize>(&mut self, value: &V) -> Result<Self::Ok, Error> {
value.serialize(HashingSerializer {
digest: self.digest,
})?;
Ok(())
}
fn end(self) -> Result<Self::Ok, Error> {
Ok(())
}
}
impl<'a, T: Update> SerializeTupleVariant for HashingSerializer<'a, T> {
type Ok = ();
type Error = Error;
fn serialize_field<V: ?Sized + Serialize>(&mut self, value: &V) -> Result<Self::Ok, Error> {
value.serialize(HashingSerializer {
digest: self.digest,
})?;
Ok(())
}
fn end(self) -> Result<Self::Ok, Error> {
Ok(())
}
}
impl<'a, T: Update> SerializeMap for HashingSerializer<'a, T> {
type Ok = ();
type Error = Error;
fn serialize_key<K: ?Sized + Serialize>(&mut self, key: &K) -> Result<Self::Ok, Error> {
key.serialize(HashingSerializer {
digest: self.digest,
})?;
Ok(())
}
fn serialize_value<V: ?Sized + Serialize>(&mut self, value: &V) -> Result<Self::Ok, Error> {
value.serialize(HashingSerializer {
digest: self.digest,
})?;
Ok(())
}
fn end(self) -> Result<Self::Ok, Error> {
Ok(())
}
}
impl<'a, T: Update> SerializeStruct for HashingSerializer<'a, T> {
type Ok = ();
type Error = Error;
fn serialize_field<V: ?Sized + Serialize>(
&mut self,
_key: &'static str,
value: &V,
) -> Result<Self::Ok, Error> {
value.serialize(HashingSerializer {
digest: self.digest,
})?;
Ok(())
}
fn end(self) -> Result<Self::Ok, Error> {
Ok(())
}
}
impl<'a, T: Update> SerializeStructVariant for HashingSerializer<'a, T> {
type Ok = ();
type Error = Error;
fn serialize_field<V: ?Sized + Serialize>(
&mut self,
_key: &'static str,
value: &V,
) -> Result<Self::Ok, Error> {
value.serialize(HashingSerializer {
digest: self.digest,
})?;
Ok(())
}
fn end(self) -> Result<Self::Ok, Error> {
Ok(())
}
}

View file

@ -1,12 +1,23 @@
use std::marker::PhantomData;
/// Library crate for the `vanth` ECS-based database node.
use bevy_app::App;
use bevy_app::{App, Plugin};
use bevy_ecs::{prelude::*, query::QueryData};
use serde::{Deserialize, Serialize};
use crate::entity::EntityId;
pub mod store;
pub mod entity;
pub mod hashing_serializer;
pub use hashing_serializer::hash;
pub type Result<T> = std::result::Result<T, Error>;
pub enum Error {
Other(String),
}
/// A server node wrapping a Bevy App without running it.
pub struct Node {
@ -31,6 +42,16 @@ impl Node {
pub fn run() {
}
pub fn save(entity_id: impl Into<EntityId>) -> Result<()> {
// TODO
Ok(())
}
pub fn load(entity_id: impl Into<EntityId>) -> Result<Option<EntityContents>> {
// TODO
Ok(None)
}
}
#[derive(Debug, Deserialize, Component, Serialize)]
@ -50,13 +71,81 @@ pub trait VanthTuple {
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct EntityContents {
components: Vec<ComponentContents>
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ComponentContents {
id: String,
content_hash: ContentHash,
data: Vec<u8>,
}
pub trait Component: Serialize {
fn id() -> String;
}
// use a macro to implement VanthTuiple here.
#[derive(Debug, Deserialize, Component, Serialize)]
#[derive(Copy, Clone, Debug, Deserialize, Component, Serialize)]
pub struct ContentHash {
pub hash: [u8; 32],
}
#[derive(Clone, Debug, Deserialize, Component, Serialize)]
pub struct Reference<T: Clone + Serialize> {
value: ReferenceValue,
_marker: PhantomData<T>
}
#[derive( Clone, Debug, Deserialize, Component, Serialize)]
pub enum ReferenceValue {
Absent,
Retrieving(ReferenceRetrievalTask),
Present(Vec<u8>)
}
impl <T: Clone + Serialize> Reference<T> {
pub async fn take() -> T {
todo!()
}
pub async fn get() -> Handle<T> {
todo!()
}
}
#[derive(Component, Clone, Debug, Deserialize, Serialize)]
struct ReferenceRetrievalTask {
}
impl Future for ReferenceRetrievalTask {
type Output = Vec<u8>;
fn poll(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll<Self::Output> {
todo!()
}
}
pub struct Handle<T> {
_marker: PhantomData<T>
}
// TODO:
// A trait is derivable for ECS components
// The components must have a content hash, not the entity. For efficiency and ergonomics. This means that a hash of each relevant component must be stored in the Vanth component of the entity, in a `HashMap` or something. The ID of the component used by Vanth should be a method on the derived trait.
pub struct VanthPlugin;
impl Plugin for VanthPlugin {
fn build(&self, app: &mut App) {
}
}
// fn run_reference_tasks(tasks: Query<(&ReferenceGetTask<>)>) {
// }

View file

@ -1,6 +1,3 @@
#![allow(unused)]
mod parser;
mod util;
mod vanth;

View file

@ -11,3 +11,20 @@ pub struct Store {
impl Store {
}
#[derive(Debug, Deserialize, Component, Serialize)]
pub struct Cache {
size_limit_bytes: u64,
backend: Backend,
}
#[derive(Debug, Deserialize, Serialize)]
pub enum Backend {
Memory,
Sqlite(Sqlite)
}
#[derive(Debug, Deserialize, Serialize)]
pub struct Sqlite {
path: PathBuf,
}

View file

@ -0,0 +1,28 @@
use serde::{Deserialize, Serialize};
use vanth::{Component, Node, Reference};
// #[test]
// fn test_store() {
// #[derive(Clone, Deserialize, Serialize)]
// struct Foo {
// bar: Reference<Bar>,
// }
// #[derive(Clone, Deserialize, Serialize)]
// struct Bar {
// foo: Reference<Foo>,
// }
// impl Component for Foo {
// fn id() -> String {
// "foo".into()
// }
// }
// let node = Node::in_memory();
// let entity_id = "entity_1";
// let entity_components = (Foo { a: 5, b: 6.0 },);
// node.save("entity_1", entity_components);
// }

View file

@ -0,0 +1,24 @@
use serde::{Deserialize, Serialize};
use vanth::{Component, Node};
// #[test]
// fn test_store() {
// #[derive(Deserialize, Serialize)]
// struct Foo {
// a: u32,
// b: f32,
// }
// impl Component for Foo {
// fn id() -> String {
// "foo".into()
// }
// }
// let node = Node::in_memory();
// let entity_id = "entity_1";
// let entity_components = (Foo { a: 5, b: 6.0 },);
// node.save("entity_1", entity_components);
// }

View file

@ -1,178 +0,0 @@
{
"nodes": {
"devenv": {
"locked": {
"dir": "src/modules",
"lastModified": 1731619804,
"owner": "cachix",
"repo": "devenv",
"rev": "87edaaf1dddf17fe16eabab3c8edaf7cca2c3bc2",
"treeHash": "c0c2fd8edf08b32c9da79d4bb917690dc5b126d0",
"type": "github"
},
"original": {
"dir": "src/modules",
"owner": "cachix",
"repo": "devenv",
"type": "github"
}
},
"fenix": {
"inputs": {
"nixpkgs": [
"nixpkgs"
],
"rust-analyzer-src": "rust-analyzer-src"
},
"locked": {
"lastModified": 1731565929,
"owner": "nix-community",
"repo": "fenix",
"rev": "4c6c7d5088f12f57afd4ba6449f9eb168ca05620",
"treeHash": "06a361535bdfec44eb64eb02dc5628ea71b2f488",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "fenix",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"treeHash": "2addb7b71a20a25ea74feeaf5c2f6a6b30898ecb",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"pre-commit-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"treeHash": "ca14199cabdfe1a06a7b1654c76ed49100a689f9",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1716977621,
"owner": "cachix",
"repo": "devenv-nixpkgs",
"rev": "4267e705586473d3e5c8d50299e71503f16a6fb6",
"treeHash": "6d9f1f7ca0faf1bc2eeb397c78a49623260d3412",
"type": "github"
},
"original": {
"owner": "cachix",
"ref": "rolling",
"repo": "devenv-nixpkgs",
"type": "github"
}
},
"nixpkgs-stable": {
"locked": {
"lastModified": 1731386116,
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "689fed12a013f56d4c4d3f612489634267d86529",
"treeHash": "8786ed18f037bad8b80b7a75c0e7b21a6769ee87",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-24.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-unstable": {
"locked": {
"lastModified": 1731531548,
"owner": "nixos",
"repo": "nixpkgs",
"rev": "24f0d4acd634792badd6470134c387a3b039dace",
"treeHash": "a35f71a0fa18e0a51dfeead7b9c971ed73043752",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"pre-commit-hooks": {
"inputs": {
"flake-compat": "flake-compat",
"gitignore": "gitignore",
"nixpkgs": [
"nixpkgs"
],
"nixpkgs-stable": "nixpkgs-stable"
},
"locked": {
"lastModified": 1731363552,
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"rev": "cd1af27aa85026ac759d5d3fccf650abe7e1bbf0",
"treeHash": "d246c8c2f312332367d56063d2b049419880acc7",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "pre-commit-hooks.nix",
"type": "github"
}
},
"root": {
"inputs": {
"devenv": "devenv",
"fenix": "fenix",
"nixpkgs": "nixpkgs",
"nixpkgs-unstable": "nixpkgs-unstable",
"pre-commit-hooks": "pre-commit-hooks"
}
},
"rust-analyzer-src": {
"flake": false,
"locked": {
"lastModified": 1731342671,
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "fc98e0657abf3ce07eed513e38274c89bbb2f8ad",
"treeHash": "7c4a6b17fbe9e655cac7d03a3c034456b8f60a1f",
"type": "github"
},
"original": {
"owner": "rust-lang",
"ref": "nightly",
"repo": "rust-analyzer",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

View file

@ -1,60 +0,0 @@
{
pkgs,
lib,
config,
inputs,
...
}:
let
pkgs-unstable = import inputs.nixpkgs-unstable {
system = pkgs.stdenv.system;
config.allowUnfree = true;
};
in
{
# https://devenv.sh/basics/
env.GREET = "devenv";
# https://devenv.sh/packages/
packages = [
pkgs-unstable.git
pkgs-unstable.rustup
pkgs-unstable.code-cursor
pkgs-unstable.curl
pkgs-unstable.jq
];
# https://devenv.sh/languages/
languages.rust = {
channel = "nightly";
components = [
"cargo"
"rust-src"
"rustc"
"clippy"
];
enable = true;
};
enterShell = ''
rustup component add clippy
'';
enterTest = ''
echo "Running tests"
git --version | grep --color=auto "${pkgs.git.version}"
cargo clippy -- -D warnings
'';
pre-commit.hooks = {
shellcheck.enable = true;
clippy = {
enable = true;
name = "clippy";
entry = "cargo clippy -- -D warnings";
files = "\\.rs$";
language = "system";
};
};
}

View file

@ -1,14 +0,0 @@
# yaml-language-server: $schema=https://devenv.sh/devenv.schema.json
inputs:
fenix:
url: github:nix-community/fenix
inputs:
nixpkgs:
follows: nixpkgs
nixpkgs:
url: github:cachix/devenv-nixpkgs/rolling
nixpkgs-unstable:
url: github:nixos/nixpkgs/nixpkgs-unstable
allowUnfree: true

130
flake.lock generated Normal file
View file

@ -0,0 +1,130 @@
{
"nodes": {
"crane": {
"locked": {
"lastModified": 1754112045,
"narHash": "sha256-ZgsUm1brDXTPSLK5kY5rkQkqqr2oM1Pd20oirH3Sgkk=",
"owner": "ipetkov",
"repo": "crane",
"rev": "b91f27c93d9d414a21092d85ed5ac9def8624b91",
"type": "github"
},
"original": {
"owner": "ipetkov",
"repo": "crane",
"type": "github"
}
},
"fenix": {
"inputs": {
"nixpkgs": "nixpkgs",
"rust-analyzer-src": "rust-analyzer-src"
},
"locked": {
"lastModified": 1754116698,
"narHash": "sha256-2cMDogXcqv975o191g0/6ZM0UolJ2X0ChuuLaNKbNAM=",
"owner": "nix-community",
"repo": "fenix",
"rev": "acdb7d8af407ea8aee21b028dc58b4bc51a7849a",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "fenix",
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1754091436,
"narHash": "sha256-XKqDMN1/Qj1DKivQvscI4vmHfDfvYR2pfuFOJiCeewM=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "67df8c627c2c39c41dbec76a1f201929929ab0bd",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1753939845,
"narHash": "sha256-K2ViRJfdVGE8tpJejs8Qpvvejks1+A4GQej/lBk5y7I=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "94def634a20494ee057c76998843c015909d6311",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1753579242,
"narHash": "sha256-zvaMGVn14/Zz8hnp4VWT9xVnhc8vuL3TStRqwk22biA=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "0f36c44e01a6129be94e3ade315a5883f0228a6e",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixpkgs.lib",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1754060105,
"narHash": "sha256-di5L6e5Iiv+oegS07j9h23FdqEpXn0ZQqMlDOEMw1EY=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "e7eabdc701d7dbb810fd91a97ec358caa4c1fc50",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"crane": "crane",
"fenix": "fenix",
"flake-parts": "flake-parts",
"nixpkgs": "nixpkgs_2"
}
},
"rust-analyzer-src": {
"flake": false,
"locked": {
"lastModified": 1753974682,
"narHash": "sha256-Ya4Ecj8JaKkapgYCCybzZBcypXpblhuG0hVDzdy2zyo=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "68e7ec90bf29c9b17d0dcdb3358de5dee7f4afb5",
"type": "github"
},
"original": {
"owner": "rust-lang",
"ref": "nightly",
"repo": "rust-analyzer",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

117
flake.nix Normal file
View file

@ -0,0 +1,117 @@
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
crane.url = "github:ipetkov/crane";
flake-parts.url = "github:hercules-ci/flake-parts";
fenix.url = "github:nix-community/fenix";
};
outputs =
inputs@{ flake-parts, ... }:
flake-parts.lib.mkFlake { inherit inputs; } {
systems = [
"aarch64-linux"
"aarch64-darwin"
"x86_64-darwin"
"x86_64-linux"
];
perSystem =
{ pkgs, system, ... }:
let
rustToolchain = inputs.fenix.packages.${system}.combine ([
(inputs.fenix.packages.${system}.complete.withComponents [
"cargo"
"clippy"
"rust-src"
"rustc"
"rustfmt"
])
inputs.fenix.packages.${system}.complete.rust-analyzer
inputs.fenix.packages.${system}.targets.wasm32-unknown-unknown.latest.rust-std
]);
craneLib = (inputs.crane.mkLib pkgs).overrideToolchain rustToolchain;
packages =
with pkgs;
[
dbus
libssh2
makeWrapper
openssl
openssl.dev
pkg-config
]
++ lib.optionals stdenv.isLinux [
systemd
patchelf
]
++ lib.optionals stdenv.isDarwin [
libiconv
darwin.apple_sdk.frameworks.Security
darwin.apple_sdk.frameworks.SystemConfiguration
darwin.apple_sdk.frameworks.AudioUnit
darwin.apple_sdk.frameworks.CoreAudio
];
commonArgs = {
src = ./.;
strictDeps = true;
nativeBuildInputs = packages;
buildInputs = packages;
CARGO_BUILD_INCREMENTAL = "false";
RUST_BACKTRACE = "1";
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath packages;
};
craneArgs = {
};
cargoArtifacts = craneLib.buildDepsOnly (
commonArgs
// craneArgs
// {
pname = "vanth-deps";
version = "0.1.0";
}
);
vanth = craneLib.buildPackage (
commonArgs
// craneArgs
// {
inherit cargoArtifacts;
pname = "vanth";
version = "0.1.0";
cargoExtraArgs = "--package vanth_cli";
postInstall = ''
if [ ! -f "$out/bin/vanth" ]; then
echo "Error: vanth binary not found in $out/bin/"
ls -la $out/bin/
exit 1
fi
''
+ pkgs.lib.optionalString pkgs.stdenv.isLinux ''
patchelf $out/bin/vanth --set-rpath ${pkgs.lib.makeLibraryPath packages}
wrapProgram $out/bin/vanth --set LD_LIBRARY_PATH "${pkgs.lib.makeLibraryPath packages}"
'';
}
);
in
{
packages = rec {
inherit vanth;
default = vanth;
};
devShells.default = craneLib.devShell {
inputsFrom = [ vanth ];
packages = packages;
RUST_SRC_PATH = "${rustToolchain}/lib/rustlib/src/rust/library";
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath packages;
};
};
};
}