chuchi_postgres/
db.rs

1use crate::{
2	Connection, Database, Error,
3	connection::{ConnectionOwned, Transaction},
4	database::DatabaseError,
5};
6
7/// This might contain a database or none.
8///
9/// This will be usefull to mocking the database with a memory
10/// database.
11#[derive(Debug, Clone)]
12#[cfg_attr(feature = "chuchi", derive(chuchi::Resource))]
13pub struct Db {
14	pg: Option<Database>,
15}
16
17impl Db {
18	pub fn new_memory() -> Self {
19		Self { pg: None }
20	}
21
22	pub async fn get(&self) -> Result<ConnOwned, DatabaseError> {
23		match &self.pg {
24			Some(pg) => Ok(ConnOwned {
25				pg: Some(pg.get().await?),
26			}),
27			None => Ok(ConnOwned { pg: None }),
28		}
29	}
30}
31
32impl From<Database> for Db {
33	fn from(pg: Database) -> Self {
34		Self { pg: Some(pg) }
35	}
36}
37
38#[derive(Debug)]
39pub struct ConnOwned {
40	pg: Option<ConnectionOwned>,
41}
42
43impl ConnOwned {
44	// connection
45	pub fn conn(&self) -> Conn {
46		Conn {
47			pg: self.pg.as_ref().map(|pg| pg.connection()),
48		}
49	}
50
51	// or transaction
52	#[allow(dead_code)]
53	pub async fn trans(&mut self) -> Result<Trans, Error> {
54		match &mut self.pg {
55			Some(pg) => Ok(Trans {
56				pg: Some(pg.transaction().await?),
57			}),
58			None => Ok(Trans { pg: None }),
59		}
60	}
61}
62
63#[cfg(feature = "chuchi")]
64mod impl_chuchi {
65	use chuchi::{
66		extractor::Extractor, extractor_extract, extractor_prepare,
67		extractor_validate,
68	};
69
70	use super::*;
71
72	impl<'a, R> Extractor<'a, R> for ConnOwned {
73		type Error = DatabaseError;
74		type Prepared = Self;
75
76		extractor_validate!(|validate| {
77			assert!(validate.resources.exists::<Db>(), "Db resource not found");
78		});
79
80		extractor_prepare!(|prepare| {
81			let db = prepare.resources.get::<Db>().unwrap();
82			db.get().await
83		});
84
85		extractor_extract!(|extract| { Ok(extract.prepared) });
86	}
87}
88
89/// This might contain a connection or none.
90#[derive(Debug, Clone, Copy)]
91pub struct Conn<'a> {
92	pg: Option<Connection<'a>>,
93}
94
95impl<'a> Conn<'a> {
96	/// Create a new connection.
97	pub fn new_memory() -> Self {
98		Self { pg: None }
99	}
100
101	/// Get the connection.
102	///
103	/// ## Panics
104	/// If the connection is not set.
105	pub fn pg(self) -> Connection<'a> {
106		self.pg.unwrap()
107	}
108}
109
110/// This might contain a transaction or none.
111#[derive(Debug)]
112pub struct Trans<'a> {
113	pg: Option<Transaction<'a>>,
114}
115
116impl<'a> Trans<'a> {
117	/// Get the connection of the transaction.
118	pub fn conn(&self) -> Conn {
119		Conn {
120			pg: self.pg.as_ref().map(|pg| pg.connection()),
121		}
122	}
123
124	/// Commit the transaction.
125	///
126	/// ## Note
127	/// Does nothing if it contains a memory Conn
128	pub async fn commit(self) -> Result<(), Error> {
129		match self.pg {
130			Some(pg) => pg.commit().await,
131			None => Ok(()),
132		}
133	}
134
135	/// Rollback the transaction.
136	///
137	/// ## Panics
138	/// If the transaction is not set / this is a memory Conn
139	pub async fn rollback(self) -> Result<(), Error> {
140		match self.pg {
141			Some(pg) => pg.commit().await,
142			None => panic!("rollback not supported"),
143		}
144	}
145}