GraphLink
GraphLink is a memory-safe, relational in-memory graph database library for Rust, inspired by the ergonomics of Ruby on Rails' ActiveRecord.
It lets you define complex data schemas with associations (like has_many, belongs_to, and has_many :through) and automatically handles memory allocation, relational integrity, and secondary indices.
Install
cargo add graphlink
Usage
GraphLink lets you link together existing structured data using in-memory, database-inspired references.
Use it as a layer on top of your existing data to easily link related data without pointers or reference counters.
To set up GraphLink, use the define_schema! macro to create a store struct containing all related data.
You'll need to update the model structs to match the relationships described in the schema definition, and that's it!
Here's an example:
use graphlink::{define_schema, BelongsTo, HasMany};
define_schema! {
store: Store;
model Library {
has_many Checkout;
has_many Patron through Checkout;
}
model Patron {
index unique email;
has_many Checkout;
}
model Checkout {
belongs_to Library (on_delete = cascade);
belongs_to Patron (on_delete = restrict);
}
}
pub struct Library {
pub name: String,
pub checkouts: HasMany<CheckoutId>,
}
pub struct Patron {
pub email: String,
pub checkouts: HasMany<CheckoutId>,
}
impl Patron {
pub fn new(email: impl Into<String>) -> Self {
Self {
email: email.into(),
checkouts: HasMany::new(),
}
}
}
pub struct Checkout {
pub title: String,
pub library: BelongsTo<LibraryId>,
pub patron: BelongsTo<PatronId>,
}
fn main() {
let mut store = Store::new();
let library_id = store.add_library(Library {
name: "City Library".into(),
checkouts: HasMany::new(),
});
let alice_id = store.add_patron(Patron::new("alice@example.com"));
let bob_id = store.add_patron(Patron::new("bob@example.com"));
store.add_checkout(Checkout {
title: "Rust Atomics and Locks".into(),
library: BelongsTo::new(library_id),
patron: BelongsTo::new(alice_id),
});
store.add_checkout(Checkout {
title: "Zero To Production".into(),
library: BelongsTo::new(library_id),
patron: BelongsTo::new(bob_id),
});
if let Some(library) = store.library(library_id) {
let emails: Vec<&str> = library
.patrons()
.map(|patron| patron.data.email.as_str())
.collect();
println!("Patrons with checkouts: {:?}", emails);
}
if let Some(alice) = store.get_patron_by_email("alice@example.com") {
println!("Found patron: {}", alice.data.email);
}
let _ = store.update_patron(alice_id, |patron| {
patron.email = "alicia@example.com".into();
});
let _ = store.remove_library(library_id);
}