sequelite/model/
relation.rs

1use std::fmt::Debug;
2
3use rusqlite::{types::FromSql, ToSql};
4
5use crate::{IntoSqlite, prelude::{Executable, Connection, ColumnQueryFilterImpl}};
6
7use super::{Model, query::ModelQuery, Column};
8
9/// Represents a relation between two models.
10/// 
11/// ## Example use
12/// ```rust
13/// use sequelite::prelude::*;
14/// 
15/// #[derive(Debug, Model)]
16/// struct User {
17///     id: Option<i32>,
18///     name: String,
19/// }
20/// 
21/// #[derive(Debug, Model)]
22/// struct Post {
23///     id: Option<i32>,
24///     title: String,
25///     body: String,
26/// 
27///     author: Relation<User>
28/// }
29/// 
30/// let mut conn = Connection::new_memory().unwrap();
31/// conn.register::<User>().unwrap();
32/// conn.register::<Post>().unwrap();
33/// conn.migrate();
34/// 
35/// let user_id = User {
36///     id: None,
37///     name: "John Doe".to_string(),
38/// }.insert(&conn).unwrap();
39/// 
40/// let post_id = Post {
41///     id: None,
42///     title: "Hello world!".to_string(),
43///     body: "This is my first post!".to_string(),
44///     author: Relation::id(user_id)
45/// }.insert(&conn).unwrap();
46/// 
47/// let post = Post::select().filter(Post::author.ref_::<User>(user_id)).exec(&conn).unwrap().pop().unwrap();
48/// assert_eq!(post_id, post.author.get_id());
49/// ```
50pub struct Relation<M> where M: Model {
51    related_key: Option<i64>,
52    related: Option<M>,
53
54    marker: std::marker::PhantomData<M>
55}
56
57impl<M: Model> ToSql for Relation<M> {
58    fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
59        Ok(rusqlite::types::ToSqlOutput::Owned(rusqlite::types::Value::Integer(self.related_key.unwrap_or(0))))
60    }
61}
62
63impl<M: Model> Clone for Relation<M> {
64    fn clone(&self) -> Self {
65        Relation {
66            related_key: self.related_key,
67            related: None,
68
69            marker: Default::default()
70        }
71    }
72}
73
74impl<M: Model> FromSql for Relation<M> {
75    fn column_result(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self> {
76        Ok(Relation {
77            related_key: value.as_i64().ok(),
78            related: None,
79
80            marker: Default::default()
81        })
82    }
83}
84
85impl<M> Default for Relation<M> where M: Model {
86    fn default() -> Self {
87        Self {
88            related_key: None,
89            related: None,
90
91            marker: Default::default()
92        }
93    }
94}
95
96impl<M: Model + Debug> Debug for Relation<M> {
97    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98        if self.related.is_none() {
99            f.debug_struct("UnfetchedRelation")
100                .field("table", &M::table_name())
101                .field("id", &self.related_key.unwrap())
102                .finish()
103        } else {
104            f.debug_struct("Relation")
105                .field("table", &M::table_name())
106                .field("id", &self.related_key.unwrap())
107                .field("model", &self.related.as_ref().unwrap())
108                .finish()
109        }
110    }
111}
112
113impl<M: Model> Relation<M> {
114    /// Create a new relation from an id in the related table.
115    pub fn id(id: impl Into<i64>) -> Self {
116        Relation {
117            related_key: Some(id.into()),
118            ..Default::default()
119        }
120    }
121
122    /// Create a new relation from a model that is already in the database.
123    pub fn model(model: &M) -> Self {
124        Relation {
125            related_key: Some(model.get_id()),
126            ..Default::default()
127        }
128    }
129
130    /// This function is used to fetch the related model from the database.
131    /// It should not be called manually unless you know what you're doing.
132    pub fn parse_from_row(row: &rusqlite::Row, offset: usize, idx: usize, counter: &mut usize, is_joined: bool) -> Self {
133        if !is_joined {
134            let related_key = row.get::<_, i64>(offset + idx);
135
136            return Relation {
137                related_key: Some(related_key.unwrap()),
138                ..Default::default()
139            }
140        }
141
142
143        let related = M::parse_row(row, offset + *counter, &Vec::new());
144        *counter += M::count_columns();
145        
146        Relation {
147            related_key: Some(related.get_id()),
148            related: Some(related),
149
150            marker: Default::default()
151        }
152    }
153
154    /// Get the id of the related model
155    pub fn get_id(&self) -> Option<i64> {
156        self.related_key
157    }
158
159    /// Try to get the related model if it's already loaded
160    pub fn try_get(&self) -> Option<&M> {
161        self.related.as_ref()
162    }
163
164    /// Try to get the related model taking it out if it's already loaded
165    pub fn try_take(&mut self) -> Option<M> {
166        self.related.take()
167    }
168
169    /// Get the related model if it's already loaded, otherwise fetch it from the database
170    pub fn get(&mut self, conn: &Connection) -> rusqlite::Result<&M> {
171        if self.related.is_none() {
172            self.fetch(conn)?;
173        }
174
175        Ok(self.related.as_ref().unwrap())
176    }
177
178    /// Get the related model taking it out if it's already loaded, otherwise fetch it from the database
179    pub fn take(&mut self, conn: &Connection) -> rusqlite::Result<M> {
180        if self.related.is_none() {
181            self.fetch(conn)?;
182        }
183
184        Ok(self.related.take().unwrap())
185    }
186
187    /// Fetch the related model from the database
188    pub fn fetch(&mut self, conn: &Connection) -> rusqlite::Result<&M> {
189        let select_query = ModelQuery::<M>::select()
190            .filter(M::id_column().eq(self.get_id()))
191            .limit(1);
192
193        if self.related.is_none() {
194            self.related = Some(select_query.exec(conn).unwrap().into_iter().next().unwrap());
195        }
196
197        Ok(self.related.as_ref().unwrap())
198    }
199
200    /// Fetch the related model from the database and take it out
201    pub fn fetch_once(&self, conn: &Connection) -> rusqlite::Result<M> {
202        let select_query = ModelQuery::<M>::select()
203            .filter(M::id_column().eq(self.get_id()))
204            .limit(1);
205
206        Ok(select_query.exec(conn).unwrap().into_iter().next().unwrap())
207    }
208}
209
210/// Internally used by a column
211#[derive(Debug, Clone, Copy)]
212pub struct ColumnRelation<'a> {
213    pub table: &'a str,
214    pub column: &'a str,
215
216    pub local_table: &'a str,
217
218    pub foreign_key_column: &'a Column<'static>,
219    pub local_key_column_name: &'a str,
220    
221    pub on_delete: ColumnRelationAction,
222    pub on_update: ColumnRelationAction,
223}
224
225impl<'a> ColumnRelation<'a> {
226    pub const fn new(table: &'a str, local_table: &'a str, column: &'a str, ref_col: &'static Column<'static>, local_col: &'a str) -> Self {
227        ColumnRelation {
228            table,
229            column,
230            local_table,
231            foreign_key_column: ref_col,
232            local_key_column_name: local_col,
233            on_delete: ColumnRelationAction::Restrict,
234            on_update: ColumnRelationAction::Restrict,
235        }
236    }
237
238    pub const fn on_delete(mut self, action: ColumnRelationAction) -> Self {
239        self.on_delete = action;
240        self
241    }
242
243    pub const fn on_update(mut self, action: ColumnRelationAction) -> Self {
244        self.on_update = action;
245        self
246    }
247}
248
249impl<'a> IntoSqlite for ColumnRelation<'a> {
250    fn into_sqlite(&self) -> String {
251        let mut sql = format!("REFERENCES {}({})", self.table, self.column);
252
253        if self.on_delete != ColumnRelationAction::Restrict {
254            sql.push_str(&format!(" ON DELETE {}", self.on_delete.into_sqlite()));
255        }
256
257        if self.on_update != ColumnRelationAction::Restrict {
258            sql.push_str(&format!(" ON UPDATE {}", self.on_update.into_sqlite()));
259        }
260
261        sql
262    }
263}
264
265/// The action that should be performed when a referenced row is deleted or updated.
266#[derive(Debug, Clone, Copy, PartialEq, Eq)]
267pub enum ColumnRelationAction {
268    /// The default action. The database will not allow the deletion or update of the referenced row.
269    Restrict,
270    /// The database will delete or update the row from the table if that row is deleted or updated from the parent table.
271    Cascade,
272    /// The database will set the foreign key column or columns in the referencing row to NULL if that row is deleted or updated from the parent table.
273    SetNull,
274    /// The database will set the foreign key column or columns in the referencing row to the default value if that row is deleted or updated from the parent table.
275    SetDefault,
276    /// Just ignore and do nothing
277    NoAction,
278}
279
280impl IntoSqlite for ColumnRelationAction {
281    fn into_sqlite(&self) -> String {
282        match self {
283            ColumnRelationAction::Restrict => "RESTRICT".to_string(),
284            ColumnRelationAction::Cascade => "CASCADE".to_string(),
285            ColumnRelationAction::SetNull => "SET NULL".to_string(),
286            ColumnRelationAction::SetDefault => "SET DEFAULT".to_string(),
287            ColumnRelationAction::NoAction => "NO ACTION".to_string(),
288        }
289    }
290}