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