rust_query/lazy.rs
1use std::{cell::OnceCell, fmt::Debug, ops::Deref};
2
3#[cfg(doc)]
4use crate::FromExpr;
5use crate::{Table, TableRow, Transaction, value::SecretFromSql};
6
7/// [Lazy] can be used to read any column of a table row and its parents.
8/// Columns are loaded on demand, one row at at time.
9///
10/// As an example, if you have two tables `Post` and `User`:
11/// ```
12/// # #[rust_query::migration::schema(Schema)]
13/// # pub mod vN {
14/// # pub struct Post {
15/// # pub author: User,
16/// # }
17/// # pub struct User {
18/// # pub name: String,
19/// # }
20/// # }
21/// # use rust_query::Lazy;
22/// # use v0::*;
23/// fn foo(post: Lazy<Post>) {
24/// let user = &post.author; // If the `post` row was not retrieved yet, then it is retrieved now to read the `user` column.
25/// let user_id = user.table_row(); // This doesn't access the database because the `user` id was already read from the `post` row.
26/// let user_name = &user.name; // If the `user` row was not retrieved yet, then it is retrieved now to read the `name` column.
27/// }
28/// ```
29///
30/// Note that [Lazy] borrows the transaction immutably.
31/// This means that it is not possible to keep a [Lazy] value when doing inserts or updates.
32/// Here are some alternatives to solve this problem:
33/// - [Copy]/[Clone] the columns that you need from the [Lazy] value before doing inserts and or updates.
34/// - Another option is to use [Lazy::table_row] to retrieve an owned [TableRow].
35/// This can then be used to create [crate::Expr] referencing the table columns for use in queries.
36/// - If you need many columns in a struct, then consider [derive@crate::FromExpr].
37pub struct Lazy<'transaction, T: Table> {
38 pub(crate) id: TableRow<T>,
39 pub(crate) lazy: OnceCell<Box<T::Lazy<'transaction>>>,
40 pub(crate) txn: &'transaction Transaction<T::Schema>,
41}
42
43impl<T: Table> Debug for Lazy<'_, T> {
44 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45 f.debug_tuple("Lazy").field(&self.id).finish()
46 }
47}
48
49impl<'transaction, T: Table> Lazy<'transaction, T> {
50 /// Get an owned [TableRow] out of this [Lazy] value.
51 ///
52 /// If you don't care about deleting the row then you probably want to
53 /// immediately use [TableRow::into_expr](crate::FromExpr::from_expr) on the returned [TableRow].
54 pub fn table_row(&self) -> TableRow<T> {
55 self.id
56 }
57}
58
59impl<'transaction, T: Table> Clone for Lazy<'transaction, T> {
60 fn clone(&self) -> Self {
61 Self {
62 id: self.id,
63 lazy: OnceCell::new(),
64 txn: self.txn,
65 }
66 }
67}
68
69impl<'transaction, T: Table> Deref for Lazy<'transaction, T> {
70 type Target = T::Lazy<'transaction>;
71
72 fn deref(&self) -> &Self::Target {
73 self.lazy
74 .get_or_init(|| Box::new(T::get_lazy(self.txn, self.id)))
75 }
76}
77
78impl<'transaction, T: Table> SecretFromSql for Lazy<'transaction, T> {
79 fn from_sql(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self> {
80 Ok(Self {
81 id: TableRow::from_sql(value)?,
82 lazy: OnceCell::new(),
83 txn: Transaction::new_ref(),
84 })
85 }
86}