sequelite/model/
relation.rs1use 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
9pub 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 pub fn id(id: impl Into<i64>) -> Self {
116 Relation {
117 related_key: Some(id.into()),
118 ..Default::default()
119 }
120 }
121
122 pub fn model(model: &M) -> Self {
124 Relation {
125 related_key: Some(model.get_id()),
126 ..Default::default()
127 }
128 }
129
130 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 pub fn get_id(&self) -> Option<i64> {
156 self.related_key
157 }
158
159 pub fn try_get(&self) -> Option<&M> {
161 self.related.as_ref()
162 }
163
164 pub fn try_take(&mut self) -> Option<M> {
166 self.related.take()
167 }
168
169 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 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 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 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#[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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
267pub enum ColumnRelationAction {
268 Restrict,
270 Cascade,
272 SetNull,
274 SetDefault,
276 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}