1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
/// A model is a struct that represents a row in a relational database table.
/// Using the `[derive(ormlite::Model)]` macro, it will acquire the following traits:
///
///  - `ormlite::Model`, giving it direct database access, e.g. `insert`, `update_all_fields`, etc.
///  - `ormlite::HasModelBuilder`, letting it build partials, so you can insert or update some
///    fields instead of all of them at once, e.g. `model.name("John").update()`
///  - `ormlite::TableMeta`, which you typically don't use directly, but provides table metadata
///    (e.g. table name)
///
use crate::Result;
use crate::SelectQueryBuilder;
use futures::future::BoxFuture;

/// A struct that is `Insert` is expected to have same fields as the model, excluding fields
/// that have sane defaults at the database level. Concretely, if you have a Person struct:
/// #[derive(ormlite::Model)]
/// struct Person {
///     id: i32,
///     name: String,
///     age: i32,
/// }
///
/// Then the `Insert` struct looks like:
/// struct InsertPerson {
///     name: String,
///     age: i32,
/// }
pub trait Insert<DB>
where
    Self: Sized + Send + Sync,
    DB: sqlx::Database,
{
    type Model;
    fn insert<'e, A>(self, conn: A) -> BoxFuture<'e, Result<Self::Model>>
    where
        A: 'e + Send + sqlx::Acquire<'e, Database = DB>;
}

/// A struct that implements `ModelBuilder` implements the builder pattern for a model.
pub trait ModelBuilder<'a, DB>
where
    Self: Sized + Send + Sync,
    DB: sqlx::Database,
{
    type Model;

    fn insert<'e: 'a, E>(self, db: E) -> BoxFuture<'a, Result<Self::Model>>
    where
        E: 'e + sqlx::Executor<'e, Database = DB>;

    fn update<'e: 'a, E>(self, db: E) -> BoxFuture<'a, Result<Self::Model>>
    where
        E: 'e + sqlx::Executor<'e, Database = DB>;

    /// All fields that will be modified in the query.
    fn modified_fields(&self) -> Vec<&'static str>;

    /// Build the model, don't insert or update it.
    fn build(self) -> Self::Model;
}

/// The core trait. a struct that implements `Model` can also implement `HasModelBuilder`, (and is required to implement `Insertable`)
pub trait Model<DB>
where
    DB: sqlx::Database,
    Self: Sized + TableMeta,
{
    type ModelBuilder<'a>: ModelBuilder<'a, DB>
    where
        Self: 'a;

    /// Insert the model into the database.
    fn insert<'a, A>(self, conn: A) -> crate::insert::Insertion<'a, A, Self, DB>
    where
        A: 'a + Send + sqlx::Acquire<'a, Database = DB>,
        Self: Send;

    /// `Model` objects can't track what fields are updated, so this method will update all fields.
    /// If you want to update only some fields, use `update_partial` instead.
    fn update_all_fields<'e, E>(self, db: E) -> BoxFuture<'e, Result<Self>>
    where
        E: 'e + Send + sqlx::Executor<'e, Database = DB>;

    fn delete<'e, E>(self, db: E) -> BoxFuture<'e, Result<()>>
    where
        E: 'e + sqlx::Executor<'e, Database = DB>;

    /// Get by primary key.
    fn fetch_one<'e, 'a, Arg, E>(id: Arg, db: E) -> BoxFuture<'e, Result<Self>>
    where
        'a: 'e,
        E: 'e + sqlx::Executor<'e, Database = DB>,
        Arg: 'a + Send + sqlx::Encode<'a, DB> + sqlx::Type<DB>;

    /// If query building isn't meeting your needs, use this method to query the table using raw SQL.
    fn query(query: &str) -> sqlx::query::QueryAs<DB, Self, DB::Arguments<'_>>;

    /// Create a `SelectQueryBuilder` to build a query.
    fn select<'args>() -> SelectQueryBuilder<'args, DB, Self>;

    /// Create a builder-pattern object to update one or more columns.
    /// You can also use `update_all_fields` to update all columns.
    fn update_partial(&self) -> Self::ModelBuilder<'_>;

    fn builder() -> Self::ModelBuilder<'static>;
}

pub trait TableMeta {
    fn table_name() -> &'static str;
    fn table_columns() -> &'static [&'static str];
    fn primary_key() -> Option<&'static str>;
}