rpa 0.3.0

Like JPA (In Java) Library to access database.
Documentation
<div align="center">
    <img src="logo.png" height="200">
</div>

<a href="https://crates.io/crates/rpa">![RPA Version](https://img.shields.io/badge/crates.io-v0.3.0-orange.svg?longCache=true)</a>


This is a library that contains some macros and derives to help the 
developer to access database using [Rocket](https://rocket.rs/ "Rocket") and [Diesel](http://diesel.rs/ "Diesel"). 
This is only for projects with Rocket and Diesel (Rest API). 
This version works with all the Diesel supported databases (MySQL, PostgreSQL and SQLite).

#### The idea

Because we don't have any current solution like JPA (Java Persistence API) in Rust, 
we decided to make one using macros and derives. Basically using
a macro you can generate all the methods to access the database using Diesel
framework without the need to implement all the methods for each type.

#### Modules
This library has 2 modules, one for [derives](https://gitlab.com/gexuy/public-libraries/rust/rpa_modules/rpa_derives) 
and another for [macros](https://gitlab.com/gexuy/public-libraries/rust/rpa_modules/rpa_macros), we did this 
because Cargo doesn't let us publish if we don't separate our modules for now.
In the future we could put everything in just one repository if Cargo let us do it.

#### Dependencies
* [Rocket]https://rocket.rs/ "Rocket"
* [Serde]https://serde.rs/ "Serde"
* [Diesel]http://diesel.rs/ "Diesel"

In your project you need to import these dependencies:

```toml
# Rocket Webserver
[dependencies.rocket]
version = "0.4.1"
[dependencies.rocket_contrib]
version = "0.4.1"
default-features = false
features = ["json", "diesel_mysql_pool"]
#features = ["json", "diesel_postgres_pool"] if you use PostgreSQL
#features = ["json", "diesel_sqlite_pool"] if you use SQLite

# Json Serialize and Deserialize
[dependencies.serde]
version = "1.0.92"
features = ["derive"]

[dependencies.serde_json]
version = "1.0.39"

# For connecting with the MySQL database
[dependencies.diesel]
version = "1.4.2"
features = ["chrono"]

[dependencies.type-info]
version = "0.2.1"

[dependencies.type-info-derive]
version = "0.2.0"


# You can import this dependency like this or download this repo and import it manually
[dependencies.rpa]
version = "0.3.0"
```

#### How to use it

You need to make a model structure, let's take this example using the Hero model:

```rust
#[table_name = "heroes"]
#[derive(
        AsChangeset,
        Serialize,
        Deserialize,
        Queryable,
        Insertable,
        TypeInfo,
        Debug,
        Clone,
        Rpa
)]
pub struct Hero {
    pub id: String,
    pub first_name: String,
    pub last_name: String,
}
```

As you can see here, we have a structure called Hero with some macros above. 
This macros are going to make magic for us. We have AsChangeset, 
Queryable and Insertable macros from Diesel, which are needed to access the database,
also we have Serialize, Deserialize from Serde that are needed for Json Deserialization and Serialization. 
We have TypeInfo, Debug and Clone derives, those are used by our library.
Finally you can see the Rpa macro, that's a custom macro that will generate the basics 
to let us access the database. You can see the methods available under the file 
**rpa_macros/src/database/rpa.rs** in the Rpa trait in the [rpa_macros](https://gitlab.com/gexuy/public-libraries/rust/rpa_modules/rpa_macros)
 module. For the associations we can't define those methods in a trait because those are dynamic but
we have comments on how the library generates those methods.

After we define the entity structure we can now call the generated methods like this:
```rust
use rpa::{RpaError, Rpa};
use diesel::MysqlConnection;
use rocket_contrib::json::Json;
...

let result: Result<Hero, RpaError> = Hero::find(&hero_id, &*_connection); // to find
let json: Json<Hero> = result.unwrap().into_json(); // to convert into serde json
let hero: Hero = Hero::from_json(json); // to convert from serde json
...
```

**NOTE: &*_connection in all the examples is the connection instance for the database. 
In Rocket that's injected by the framework with fairings and you need to initialize it. 
(see the rocket [documentation](https://api.rocket.rs/v0.4/rocket_contrib/databases/index.html)
 for more information on how to do it).**

For now we can only assume that the schema location will always be in **src/schema.rs** of the parent project. 
Also all the structure id's are assumed as Strings. We should improve that in the future to support more types.

#### Associations

We can use [associations](https://docs.diesel.rs/diesel/associations/index.html) from [Diesel](http://diesel.rs/ "Diesel")
to map relationships. Diesel is not like other ORM's because we don't have 
nested structures as results, instead you have to query the parent structures and then with the instance of the parent type 
query all the children. Is better this way because we can avoid a lot of problems that nesting structures can have 
and also it's more efficient (we don't query data that we don't need).
We provide a way to use these associations in this library, here is how:

```rust
use rpa::Rpa;

#[table_name = "hero"]
#[derive(
        AsChangeset,
        Serialize,
        Deserialize,
        Queryable,
        Insertable,
        Identifiable,
        TypeInfo,
        Rpa,
        Debug,
        Clone
)]
pub struct Hero {
    pub id: String,
    pub first_name: String,
    pub last_name: String
}
```

As you can see we added Identifiable derive, which is needed by Diesel to map our structures.
Now we need to have another structure to relate, like this:

```rust
use rpa::Rpa;
use crate::core::models::hero::Hero;

#[table_name = "post"]
#[belongs_to(Hero, foreign_key = "hero_id")]
#[derive(
    AsChangeset,
    Serialize,
    Deserialize,
    Queryable,
    Insertable,
    Identifiable,
    Associations,
    TypeInfo,
    Rpa,
    Debug,
    Clone
)]
pub struct Post {
    pub id: String,
    pub hero_id: String,
    pub text: String
}
```

We have a structure Post with some reference to the Hero structure and 
the Identifiable derive but we need to add the Associations derive too. This is
a Diesel requirement as you can see in the [associations](https://docs.diesel.rs/diesel/associations/) documentation.

After you follow the steps above you have available methods that you can use like this:

```rust
let hero: Hero = Hero::find(&hero_id, &*_connection).unwrap();
let posts: Vec<Post> = Post::find_for_hero(&vec![hero.clone()], &*_connection).unwrap();
let grouped_posts: Vec<(Hero, Vec<Post>)> = Post::find_grouped_by_hero(&heroes, &*_connection).unwrap();
``` 

Some methods are generated when you use Rpa, find_for_{parent_name_lowercase} that searches for all the children owned by the parent or parents.
Other method is called find_grouped_by_{parent_name_lowercase} that works like the above method but it groups the results by parents.

### Different databases
By default Rpa uses Mysql but if you want to use another database we support Diesel databases, those are PostgreSQL and SQLite for now.
To change the default database into another you can use an attribute called connection_type. 
That attribute can have only one of these 3 values ("MYSQL", "POSTGRESQL", "SQLITE") per structure.
Here is how you can use it:

```rust
extern crate chrono;

use chrono::NaiveDateTime;
use rpa as Rpa;
use crate::core::models::custom_formats::custom_date_format;

#[table_name = "hero"]
#[connection_type="POSTGRESQL"]
#[derive(
        AsChangeset,
        Serialize,
        Deserialize,
        Queryable,
        Insertable,
        Identifiable,
        TypeInfo,
        Rpa,
        Debug,
        Clone
)]
pub struct Hero {
    pub id: String,
    pub first_name: String,
    pub last_name: String,
    #[serde(with = "custom_date_format")]
    pub birth_date: NaiveDateTime
}
```
Also you need to change your Cargo.toml to use **diesel_postgres_pool** in **rocket_contrib** dependency in this case to make it work.
You can have multiple databases using this library but you need to be careful with the mixes, you **CAN NOT** mix different structures in different DB's.

## Next Steps
   * Generate Schemas from structures.
   * Add more useful methods to query data.
   * Add some support like Spring has in Java for JPARepositories.