[−][src]Crate ergol
This crate provides the #[ergol]
macro. It allows to persist the data in a
database. For example, you just have to write
use ergol::prelude::*; #[ergol] pub struct User { #[id] pub id: i32, #[unique] pub username: String, pub password: String, pub age: Option<i32>, }
and the #[ergol]
macro will generate most of the code you will need. You'll
then be able to run code like the following:
// Drop the user table if it exists User::drop_table().execute(&client).await.ok(); // Create the user table User::create_table().execute(&client).await?; // Create a user and save it into the database let mut user: User = User::create("thomas", "pa$$w0rd", Some(28)).save(&client).await?; // Change some of its fields *user.age.as_mut().unwrap() += 1; // Update the user in the database user.save(&client).await?; // Fetch a user by its username thanks to the unique attribute let user: Option<User> = User::get_by_username("thomas", &client).await?; // Select all users let users: Vec<User> = User::select().execute(&client).await?;
Many-to-one and one-to-one relationships
Let's say you want a user to be able to have projects. You can use the
#[many_to_one]
attribute in order to do so. Just add:
#[ergol] pub struct Project { #[id] pub id: i32, pub name: String, #[many_to_one(projects)] pub owner: User, }
Once you have defined this struct, many more functions become available:
// Drop the user table if it exists Project::drop_table().execute(&client).await.ok(); User::drop_table().execute(&client).await.ok(); // Create the user table User::create_table().execute(&client).await?; Project::create_table().execute(&client).await?; // Create two users and save them into the database let thomas: User = User::create("thomas", "pa$$w0rd", 28).save(&client).await?; User::create("nicolas", "pa$$w0rd", 28).save(&client).await?; // Create some projects for the user let project: Project = Project::create("My first project", &thomas).save(&client).await?; Project::create("My second project", &thomas).save(&client).await?; // You can easily find all projects from the user let projects: Vec<Project> = thomas.projects(&client).await?; // You can also find the owner of a project let owner: User = projects[0].owner(&client).await?;
You can similarly have one-to-one relationship between a user and a project by
using the #[one_to_one]
attribute:
#[ergol] pub struct Project { #[id] pub id: i32, pub name: String, #[one_to_one(project)] pub owner: User, }
This will add the UNIQUE
attribute in the database and make the project
method only return an option:
// You can easily find a user's project let project: Option<Project> = thomas.project(&client).await?;
Note that that way, a project has exactly one owner, but a user can have no project.
Many-to-many relationships
This macro also supports many-to-many relationships. In order to do so, you
need to use the #[many_to_many]
attribute:
#[ergol] pub struct Project { #[id] pub id: i32, pub name: String, #[many_to_many(visible_projects)] pub authorized_users: User, }
The same way, you will have plenty of functions that you will be able to use to manage your objects:
// Find some users in the database let thomas = User::get_by_username("thomas", &client).await?.unwrap(); let nicolas = User::get_by_username("nicolas", &client).await?.unwrap(); // Create a project let first_project = Project::create("My first project").save(&client).await?; // Thomas can access this project first_project.add_authorized_user(&thomas, &client).await?; // The other way round nicolas.add_visible_project(&first_project, &client).await?; // The second project can only be used by thomas let second_project = Project::create("My second project").save(&client).await?; thomas.add_visible_project(&second_project, &client).await?; // The third project can only be used by nicolas. let third_project = Project::create("My third project").save(&client).await?; third_project.add_authorized_user(&nicolas, &client).await?; // You can easily retrieve all projects available for a certain user let projects: Vec<Project> = thomas.visible_projects(&client).await?; // And you can easily retrieve all users that have access to a certain project let users: Vec<User> = first_project.authorized_users(&client).await?; // You can easily remove a user from a project let _: bool = first_project.remove_authorized_user(&thomas, &client).await?; // Or vice-versa let _: bool = nicolas.remove_visible_project(&first_project, &client).await?; // The remove functions return true if they successfully removed something.
Limitations
For the moment, we still have plenty of limitations:
-
this crate only works with tokio-postgres
-
there is no support for migrations
-
the names of the structs you use in
#[ergol]
must be used previously, e.g.ⓘThis example is not testedmod user { use ergol::prelude::*; #[ergol] pub struct User { #[id] pub id: i32, } } use ergol::prelude::*; #[ergol] pub struct Project { #[id] pub id: i32, #[many_to_one(projects)] pub owner: user::User, // this will not work }
mod user { use ergol::prelude::*; #[ergol] pub struct User { #[id] pub id: i32, } } use user::User; use ergol::prelude::*; #[ergol] pub struct Project { #[id] pub id: i32, #[many_to_one(projects)] pub owner: User, // this will work }
Re-exports
pub use async_trait; |
pub use bytes; |
pub use tokio; |
pub use tokio_postgres; |
Modules
pg | This module contains the types for postgres. |
prelude | The prelude contains the macros and usefull traits. |
query | This crate contains all the necessary queries. |
relation | This module contains the struct that we use to represent the relationships between linked tables. |
Traits
ToTable | Any type that should be transformed into a table should implement this trait. |
Attribute Macros
ergol |
Derive Macros
PgEnum | Any enum that has no field on any variant can derive |