Skip to main content

nil_server_database/database/
mod.rs

1// Copyright (C) Call of Nil contributors
2// SPDX-License-Identifier: AGPL-3.0-only
3
4mod 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::db_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::db_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<db_PlayerId>) -> Result<i64> {
69    let id: db_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<db_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(
146    &self,
147    id: impl Into<GameId>,
148  ) -> Result<Option<db_Duration>> {
149    let id: GameId = id.into();
150    self
151      .with_blocking(move |db| db.get_game_round_duration(id))
152      .await
153  }
154
155  pub async fn get_game_with_blob(&self, id: impl Into<GameId>) -> Result<GameWithBlob> {
156    let id: GameId = id.into();
157    self
158      .with_blocking(move |db| db.get_game_with_blob(id))
159      .await
160  }
161
162  pub async fn get_games(&self) -> Result<Vec<Game>> {
163    self.with_blocking(|db| db.get_games()).await
164  }
165
166  pub async fn get_games_with_blob(&self) -> Result<Vec<GameWithBlob>> {
167    self
168      .with_blocking(|db| db.get_games_with_blob())
169      .await
170  }
171
172  pub async fn get_user(&self, id: impl Into<db_PlayerId>) -> Result<User> {
173    let id: db_PlayerId = id.into();
174    self
175      .with_blocking(move |db| db.get_user(id))
176      .await
177  }
178
179  pub async fn get_user_by_id(&self, id: impl Into<UserId>) -> Result<User> {
180    let id: UserId = id.into();
181    self
182      .with_blocking(move |db| db.get_user_by_id(id))
183      .await
184  }
185
186  pub async fn get_user_id(&self, id: impl Into<db_PlayerId>) -> Result<UserId> {
187    let id: db_PlayerId = id.into();
188    self
189      .with_blocking(move |db| db.get_user_id(id))
190      .await
191  }
192
193  pub async fn get_user_player_id(&self, id: impl Into<UserId>) -> Result<db_PlayerId> {
194    let id: UserId = id.into();
195    self
196      .with_blocking(move |db| db.get_user_player_id(id))
197      .await
198  }
199
200  pub async fn update_game_blob(&self, id: impl Into<GameId>, blob: Vec<u8>) -> Result<usize> {
201    let id: GameId = id.into();
202    self
203      .with_blocking(move |db| db.update_game_blob(id, &blob))
204      .await
205  }
206
207  pub async fn user_exists(&self, id: impl Into<db_PlayerId>) -> Result<bool> {
208    let id: db_PlayerId = id.into();
209    self
210      .with_blocking(move |db| db.user_exists(&id))
211      .await
212  }
213
214  pub async fn verify_game_password(
215    &self,
216    id: impl Into<GameId>,
217    password: Option<Password>,
218  ) -> Result<bool> {
219    let id: GameId = id.into();
220    self
221      .with_blocking(move |db| db.verify_game_password(id, password.as_ref()))
222      .await
223  }
224
225  pub async fn was_game_created_by(
226    &self,
227    game_id: impl Into<GameId>,
228    player_id: impl Into<db_PlayerId>,
229  ) -> Result<bool> {
230    let game_id: GameId = game_id.into();
231    let player_id: db_PlayerId = player_id.into();
232    self
233      .with_blocking(move |db| db.was_game_created_by(game_id, &player_id))
234      .await
235  }
236}