12 unstable releases (3 breaking)

0.3.2 Apr 24, 2021
0.3.1 Apr 24, 2021
0.2.5 Apr 3, 2021
0.2.2 Mar 20, 2021
0.0.0 Nov 8, 2020

#673 in Data structures

40 downloads per month
Used in 7 crates (6 directly)

MIT/Apache

305KB
5K SLoC

entity-rs: Library & macros for entity data structures

Build Status Crates.io Docs.rs entity: rustc 1.49+ entity_macros: rustc 1.49+

A simplistic framework based on TAO, Facebook's distributed database for Social Graph.

Requires Rust 1.49+.

Getting Started

Installation

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.3.0"

For most use cases, you will want to also import the macros, which will bring in the entity_macros crate:

[dependencies]
entity = { version = "0.3.0", features = ["macros"] }

Defining data using macros

The following is an example of defining a User data structure that contains an age and name field as well as references to many other User instances under the edge name friends.

use entity::simple_ent;

#[simple_ent]
struct User {
    name: String,
    age: u8,

    #[ent(edge)]
    friends: Vec<User>,
}

This generates a variety of trait implementations and additional data structures to work with the User struct against any database.

Loading an ent by id

use entity::EntLoader;

let db = /* entity::WeakDatabaseRc instance */;

// Loads the user from the provided database reference
let user = User::load_from_db_strict(db, 123).expect("Database available");

// Loads the user from the globally-set database
let user = User::load_strict(123).expect("Database available");

Accessing ent fields

Every object implementing the Ent trait is able to access a variety of common data including abstract field information:

use entity::{Ent, Primitive, Value};

let user = /* User instance */;

// Access a list of all field names
assert_eq!(user.field_names(), vec![String::from("name"), String::from("age")]);

// Access individual field values, which are exposed using entity's
// generic Value enum
assert_eq!(user.field("name"), Some(Value::Text(/* ... */)));
assert_eq!(user.field("age"), Some(Value::Primitive(Primitive::Number(/* ... */))));

When using macros to generate an ent, typed accessors are also provided as seen below:

let user = /* User instance */;

// Accesses fields and returns a reference to their actual type NOT
// wrapped in entity's generic Value enum
assert_eq!(user.get_name(), &String::from("..."));
assert_eq!(user.get_age(), &123);

Accessing ent edges

Every object implementing the Ent trait is able to access a variety of abstract edge information:

use entity::{Ent, EdgeValue};

let user = /* User instance */;

// Access a list of all edge names
assert_eq!(user.edge_names(), vec![String::from("friends")]);

// Access specific edge information (does not load it)
assert_eq!(user.edge("friends"), Some(EdgeValue::Many(vec![124, 125, /* ... */])));

// Load an edge by name, returning a Vec<Box<dyn Ent>>
let friends: Vec<Box<dyn Ent>> = user.load_edge("friends").expect("Database available");

When using macros to generate an ent, typed edge accessors are also provided as seen below:

let user = /* User instance */;

// Access the ids of ents referenced by the edge
assert_eq!(user.my_friends_ids(), vec![124, 125, /* ... */]);

// Load the ents referenced by the edge into memory
let friends: Vec<User> = user.load_friends().expect("Database available");

Querying an ent

Alongside loading ents by their ids, the full suite that entity-rs provides also includes the ability to query arbitrary data structures using a concept of queries and associated predicates:

use entity::{EntQuery, Predicate as P, Query};

let db = /* WeakDatabaseRc instance */;

// Produce a new query to search for ents with an age field that is 18 or higher
let ents: Vec<Box<dyn Ent>> = Query::default()
  .where_field("age", P::greater_than_or_equals(18))
  .execute_with_db(db)
  .expect("Database available");

// If the global database has been configured, can also be used in this manner
let ents: Vec<Box<dyn Ent>> = Query::default()
  .where_field("age", P::greater_than_or_equals(18))
  .execute()
  .expect("Database available");

When using macros to generate an ent, a companion query struct that provides stricter types on queries is also created using the name {Ent}Query:

use entity::{EntQuery, TypedPredicate as P};

let db = /* WeakDatabaseRc instance */;

// Produce a new query to search for ents with an age field that is 18 or higher
let users: Vec<User> = User::query()
  .where_age(P::greater_than_or_equals(18))
  .execute_with_db(db)
  .expect("Database available");

// If the global database has been configured, can also be used in this manner
let users: Vec<User> = User::query()
  .where_age(P::greater_than_or_equals(18))
  .execute()
  .expect("Database available");

Examples

  • async-graphql: example of using entity-rs with async-graphql
  • inmemory: example of using entity-rs with a custom inmemory database
  • sled: example of using entity-rs with sled

Feature Flags

Entity provides a few feature flags:

  • global - Enables use of a database stored as a global variable, providing shortcuts in creating and retrieving ents.
  • macros - Enables macros for deriving ents and exposing a cleaner declarative API for ents. (Imports entity_macros directly)
  • 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.
    • Requires serde and typetag to be included in dependencies.

Dependencies

~0.7–1.8MB
~34K SLoC