nil_server_database/database/
mod.rs1mod game;
5mod user;
6
7use crate::error::Result;
8use crate::migration::run_pending_migrations;
9use crate::model::game::{Game, GameWithBlob, NewGame};
10use crate::model::user::{NewUser, User};
11use crate::sql_types::duration::Duration;
12use crate::sql_types::game_id::GameId;
13use crate::sql_types::hashed_password::HashedPassword;
14use crate::sql_types::id::UserId;
15use crate::sql_types::player_id::PlayerId;
16use diesel::prelude::*;
17use itertools::Itertools;
18use nil_crypto::password::Password;
19use std::sync::Arc;
20use std::sync::nonpoison::{Mutex, MutexGuard};
21use tokio::task::spawn_blocking;
22
23#[must_use]
24#[derive(Clone)]
25pub struct BlockingDatabase(Arc<Mutex<SqliteConnection>>);
26
27impl BlockingDatabase {
28 fn new(url: &str) -> Result<Self> {
29 let mut conn = SqliteConnection::establish(url)?;
30 run_pending_migrations(&mut conn)?;
31 Ok(Self(Arc::new(Mutex::new(conn))))
32 }
33
34 fn conn(&self) -> MutexGuard<'_, SqliteConnection> {
35 self.0.lock()
36 }
37}
38
39#[must_use]
40#[derive(Clone)]
41pub struct Database(BlockingDatabase);
42
43impl Database {
44 pub async fn new(url: &str) -> Result<Self> {
45 let url = url.to_owned();
46 spawn_blocking(move || BlockingDatabase::new(&url).map(Self)).await?
47 }
48
49 pub fn blocking(&self) -> BlockingDatabase {
50 self.0.clone()
51 }
52
53 async fn with_blocking<F, T>(&self, f: F) -> Result<T>
54 where
55 F: FnOnce(BlockingDatabase) -> Result<T> + Send + 'static,
56 T: Send + 'static,
57 {
58 let blocking = self.blocking();
59 spawn_blocking(move || f(blocking)).await?
60 }
61
62 pub async fn count_games(&self) -> Result<i64> {
63 self
64 .with_blocking(|db| db.count_games())
65 .await
66 }
67
68 pub async fn count_games_by_user(&self, id: impl Into<PlayerId>) -> Result<i64> {
69 let id: PlayerId = id.into();
70 self
71 .with_blocking(move |db| db.count_games_by_user(id))
72 .await
73 }
74
75 pub async fn count_games_by_user_id(&self, id: impl Into<UserId>) -> Result<i64> {
76 let id: UserId = id.into();
77 self
78 .with_blocking(move |db| db.count_games_by_user_id(id))
79 .await
80 }
81
82 pub async fn create_game(&self, new: NewGame) -> Result<usize> {
83 self
84 .with_blocking(move |db| db.create_game(&new))
85 .await
86 }
87
88 pub async fn create_user(&self, new: NewUser) -> Result<usize> {
89 self
90 .with_blocking(move |db| db.create_user(&new))
91 .await
92 }
93
94 pub async fn delete_game(&self, id: impl Into<GameId>) -> Result<usize> {
95 let id: GameId = id.into();
96 self
97 .with_blocking(move |db| db.delete_game(id))
98 .await
99 }
100
101 pub async fn delete_games<I>(&self, games: I) -> Result<usize>
102 where
103 I: IntoIterator<Item = GameId>,
104 {
105 let games = games.into_iter().collect_vec();
106 self
107 .with_blocking(move |db| db.delete_games(&games))
108 .await
109 }
110
111 pub async fn game_exists(&self, id: impl Into<GameId>) -> Result<bool> {
112 let id: GameId = id.into();
113 self
114 .with_blocking(move |db| db.game_exists(id))
115 .await
116 }
117
118 pub async fn get_game(&self, id: impl Into<GameId>) -> Result<Game> {
119 let id: GameId = id.into();
120 self
121 .with_blocking(move |db| db.get_game(id))
122 .await
123 }
124
125 pub async fn get_game_creator(&self, id: impl Into<GameId>) -> Result<PlayerId> {
126 let id: GameId = id.into();
127 self
128 .with_blocking(move |db| db.get_game_creator(id))
129 .await
130 }
131
132 pub async fn get_game_ids(&self) -> Result<Vec<GameId>> {
133 self
134 .with_blocking(|db| db.get_game_ids())
135 .await
136 }
137
138 pub async fn get_game_password(&self, id: impl Into<GameId>) -> Result<Option<HashedPassword>> {
139 let id: GameId = id.into();
140 self
141 .with_blocking(move |db| db.get_game_password(id))
142 .await
143 }
144
145 pub async fn get_game_round_duration(&self, id: impl Into<GameId>) -> Result<Option<Duration>> {
146 let id: GameId = id.into();
147 self
148 .with_blocking(move |db| db.get_game_round_duration(id))
149 .await
150 }
151
152 pub async fn get_game_with_blob(&self, id: impl Into<GameId>) -> Result<GameWithBlob> {
153 let id: GameId = id.into();
154 self
155 .with_blocking(move |db| db.get_game_with_blob(id))
156 .await
157 }
158
159 pub async fn get_games(&self) -> Result<Vec<Game>> {
160 self.with_blocking(|db| db.get_games()).await
161 }
162
163 pub async fn get_games_with_blob(&self) -> Result<Vec<GameWithBlob>> {
164 self
165 .with_blocking(|db| db.get_games_with_blob())
166 .await
167 }
168
169 pub async fn get_user(&self, id: impl Into<PlayerId>) -> Result<User> {
170 let id: PlayerId = id.into();
171 self
172 .with_blocking(move |db| db.get_user(id))
173 .await
174 }
175
176 pub async fn get_user_by_id(&self, id: impl Into<UserId>) -> Result<User> {
177 let id: UserId = id.into();
178 self
179 .with_blocking(move |db| db.get_user_by_id(id))
180 .await
181 }
182
183 pub async fn get_user_id(&self, id: impl Into<PlayerId>) -> Result<UserId> {
184 let id: PlayerId = id.into();
185 self
186 .with_blocking(move |db| db.get_user_id(id))
187 .await
188 }
189
190 pub async fn get_user_player_id(&self, id: impl Into<UserId>) -> Result<PlayerId> {
191 let id: UserId = id.into();
192 self
193 .with_blocking(move |db| db.get_user_player_id(id))
194 .await
195 }
196
197 pub async fn update_game_blob(&self, id: impl Into<GameId>, blob: Vec<u8>) -> Result<usize> {
198 let id: GameId = id.into();
199 self
200 .with_blocking(move |db| db.update_game_blob(id, &blob))
201 .await
202 }
203
204 pub async fn user_exists(&self, id: impl Into<PlayerId>) -> Result<bool> {
205 let id: PlayerId = id.into();
206 self
207 .with_blocking(move |db| db.user_exists(&id))
208 .await
209 }
210
211 pub async fn verify_game_password(
212 &self,
213 id: impl Into<GameId>,
214 password: Option<Password>,
215 ) -> Result<bool> {
216 let id: GameId = id.into();
217 self
218 .with_blocking(move |db| db.verify_game_password(id, password.as_ref()))
219 .await
220 }
221
222 pub async fn was_game_created_by(
223 &self,
224 game_id: impl Into<GameId>,
225 player_id: impl Into<PlayerId>,
226 ) -> Result<bool> {
227 let game_id: GameId = game_id.into();
228 let player_id: PlayerId = player_id.into();
229 self
230 .with_blocking(move |db| db.was_game_created_by(game_id, &player_id))
231 .await
232 }
233}