A simplistic framework based on TAO, Facebook's distributed database for Social Graph.
Requires Rust 1.45+. Without entity_macros
, may compile and run for
older versions of Rust.
Import Entity into your project by adding the following line to your
Cargo.toml. entity_macros
contains the macros needed to derive and/or
transform your data to be compatible with supported databases and queries.
[dependencies]
entity = "0.1"
Several features come out-of-the-box such as the inmemory database and macros used for more concise entity creation. See the feature flag section for more details.
use entity::*;
#[simple_ent]
struct User {
name: String,
age: u8,
#[ent(edge(type = "User"))]
friends: Vec<Id>,
}
let db = InmemoryDatabase::default();
let alice = UserBuilder::default()
.name(String::from("Alice"))
.age(30)
.friends(Vec::new())
.build()
.unwrap();
Entity utilizes a simplistic CRUD database API to manage instances of entity objects.
Out of the box, Entity provides an in-memory database available that can be used for small-scale usage of objects. This is powered by a HashMap and Arc to be thread-safe.
use entity::*;
let db = InmemoryDatabase::default();
Additionally, Entity can support sled
for a lightweight, transactional database by adding the feature sled_db
.
use entity::*;
let db = SledDatabase::new(
// Requires importing sled yourself as this API wraps around a sled::Db
sled::open("my_db").unwrap()
);
If you would prefer to implement your own database wrapper, you can implement the Database trait.
use entity::*;
// Database must be cloneable and should not be expensive to do so. For
// instance, a database struct could contain fields that are wrapped in
// Arc to make them thread safe and cheap to clone as a reference is maintained
#[derive(Clone)]
pub struct MyDatabase;
impl Database for MyDatabase {
/// Retrieves a copy of a single, generic ent with the corresponding id
fn get(&self, id: Id) -> DatabaseResult<Option<Box<dyn Ent>>> {
todo!()
}
/// Removes the ent with the corresponding id, triggering edge
/// processing for all disconnected ents. Returns a boolean indicating
/// if an ent was removed.
fn remove(&self, id: Id) -> DatabaseResult<bool> {
todo!()
}
/// Inserts a new ent using its id as the primary index, overwriting
/// any ent with a matching id. If the ent's id is set to the ephemeral
/// id (of 0), a unique id will be assigned to the ent prior to being
/// inserted.
///
/// The ent's id is returned after being inserted.
fn insert(&self, ent: Box<dyn Ent>) -> DatabaseResult<Id> {
todo!()
}
/// Performs a retrieval of multiple ents of any type
fn get_all(&self, ids: Vec<Id>) -> DatabaseResult<Vec<Box<dyn Ent>>> {
todo!()
}
/// Finds all generic ents that match the query
fn find_all(&self, query: Query) -> DatabaseResult<Vec<Box<dyn Ent>>> {
todo!()
}
}
At the core of Entity is defining your data structures. Out of the box, this library provides one pre-defined structure that can hold arbitrary fields and edges, UntypedEnt. This implements the rather lengthy Ent trait that is the backbone of the Entity framework.
use entity::*;
let ent = UntypedEnt::from_collections(
// An id unique to the ent; providing 0 as the id will have it replaced
// with a unique id upon being saved in a database
123,
// Fields associated with the ent instance (like a struct field)
vec![
Field::new("field1", 456),
Field::new("field2", "some text"),
],
// Edges to other ents by their ids
vec![
Edge::new("edge1", 456),
],
);
Normally, you would prefer to implement your own strongly-typed data structure instead of using the dynamic one above. To do this, you have three options:
- Implement the Ent trait for your struct
- Derive an implementation using the special macro available from
entity_macros
or via the featuremacros
on theentity
crate - Transform a struct using the
simple_ent
attribute macro available fromentity_macros
or via the featuremacros
on theentity
create
For the below example, we'll assume that you have added the macros
feature
to the entity
crate.
use entity::{Ent, Id, Database, simple_ent};
// All entities must be cloneable
#[derive(Clone, Ent)]
pub struct MyEnt {
// One field in the ent must be marked as its id
#[ent(id)]
id: Id,
// One field in the ent must be marked as its database
// reference, which is used for loading/saving/refreshing
#[ent(database)]
database: Option<Box<dyn Database>>,
// One field must be marked as the ent's creation timestamp
#[ent(created)]
created: u64,
// One field must be marked as the ent's last updated timestamp
#[ent(last_updated)]
last_updated: u64,
// Any field that belongs as data to this ent is marked as such
#[ent(field)]
field1: u32,
#[ent(field)]
field2: String,
// Any association to other ents is marked as an edge with the
// struct field being an Option<Id>, Id, or Vec<Id>
#[ent(edge(type = "MyEnt"))]
maybe_mine: Option<Id>,
#[ent(edge(type = "SomeOtherEnt"))]
other: Id,
}
// The simple_ent attribute macro will modify a struct by injecting the
// needed fields and deriving Clone and/or Ent where needed
#[simple_ent]
pub struct SomeOtherEnt {
#[ent(field)]
my_field: f64,
}
TODO
Requires entity_macros
or enabling the macros
feature for
entity
.
TODO
Entity provides a few feature flags:
inmemory_db
(enabled by default) - Enables the in-memory database for use with ent objects. This does not bring inserde-1
by default, but including that feature will also support persisting the database to the filesystem.sled_db
- Enables the sled database for use with ent objects. Because of the nature of sled, this will also pull inserde-1
.serde-1
- Provides serde serialization module and associated functionality for ents through the use of typetag. This will require that all ents implement Serialize and Deserialize.macros
(enabled by default) - Importing macros fromentity_macros
directly from entity.