Two teams try and fill in any horizontal, vertical, or diagonal line on a bingo board by playing maps on osu!
osu.bingo
osu
1use std::collections::HashMap;
2
3use tokio::sync::{RwLock, RwLockWriteGuard};
4
5use serde::{Serialize, de::DeserializeOwned};
6use serde_json::Value;
7use sqlx::PgPool;
8
9use super::schema::*;
10
11pub struct Loader {
12 pool: PgPool,
13 cache: HashMap<String, Value>,
14}
15
16impl<'a> Loader {
17 fn new(pool: PgPool) -> Self {
18 Loader {
19 pool,
20 cache: HashMap::new(),
21 }
22 }
23
24 pub async fn get_public_games(&mut self) -> Result<Vec<BingoGame>, String> {
25 get_cached_value(String::from("all_games"), &mut self.cache, async || {
26 sqlx::query_as("SELECT * FROM bingo_game WHERE public = true;")
27 .fetch_all(&self.pool)
28 .await
29 .map_err(|e| e.to_string())
30 })
31 .await
32 }
33
34 pub async fn get_game_squares(&mut self, id: &str) -> Result<Vec<BingoSquare>, String> {
35 get_cached_value(format!("game_squares_{id}"), &mut self.cache, async || {
36 sqlx::query_as("SELECT * FROM bingo_square WHERE game_id = $1;")
37 .bind(id)
38 .fetch_all(&self.pool)
39 .await
40 .map_err(|e| e.to_string())
41 })
42 .await
43 }
44
45 pub async fn get_map_squares(&mut self, id: i32) -> Result<Vec<BingoSquare>, String> {
46 get_cached_value(format!("map_squares_{id}"), &mut self.cache, async || {
47 sqlx::query_as("SELECT * FROM bingo_square WHERE map_id = $1;")
48 .bind(id)
49 .fetch_all(&self.pool)
50 .await
51 .map_err(|e| e.to_string())
52 })
53 .await
54 }
55
56 // TODO: Authorization
57 pub async fn get_game_by_id(&mut self, id: &str) -> Result<BingoGame, String> {
58 get_cached_value(format!("game_{}", id), &mut self.cache, async || {
59 sqlx::query_as("SELECT * FROM bingo_game WHERE id = $1;")
60 .bind(&id)
61 .fetch_one(&self.pool)
62 .await
63 .map_err(|e| e.to_string())
64 })
65 .await
66 }
67
68 pub async fn get_game_users(&mut self, id: &str) -> Result<Vec<GameUser>, String> {
69 get_cached_value(
70 format!("game_gameusers_{id}"),
71 &mut self.cache,
72 async || {
73 sqlx::query_as("SELECT * FROM game_user WHERE game_id = $1;")
74 .bind(id)
75 .fetch_all(&self.pool)
76 .await
77 .map_err(|e| e.to_string())
78 },
79 )
80 .await
81 }
82
83 pub async fn get_template_from_id(&mut self, id: &str) -> Result<Template, String> {
84 get_cached_value(format!("{id}"), &mut self.cache, async || {
85 sqlx::query_as("SELECT * FROM template WHERE id = $1;")
86 .bind(&id)
87 .fetch_one(&self.pool)
88 .await
89 .map_err(|e| e.to_string())
90 })
91 .await
92 }
93
94 pub async fn get_square_scores(&mut self, id: &str) -> Result<Vec<Score>, String> {
95 get_cached_value(format!("square_scores_{id}"), &mut self.cache, async || {
96 sqlx::query_as("SELECT * FROM score WHERE square_id = $1;")
97 .bind(id)
98 .fetch_all(&self.pool)
99 .await
100 .map_err(|e| e.to_string())
101 })
102 .await
103 }
104
105 pub async fn get_gameuser_by_id(&mut self, id: &str) -> Result<GameUser, String> {
106 get_cached_value(format!("{id}"), &mut self.cache, async || {
107 sqlx::query_as("SELECT * FROM gameuser where id = $1;")
108 .bind(id)
109 .fetch_one(&self.pool)
110 .await
111 .map_err(|e| e.to_string())
112 })
113 .await
114 }
115
116 pub async fn get_user_by_id(&mut self, id: i32) -> Result<User, String> {
117 get_cached_value(format!("user_{id}"), &mut self.cache, async || {
118 sqlx::query_as("SELECT * FROM public.user where id = $1;")
119 .bind(id)
120 .fetch_one(&self.pool)
121 .await
122 .map_err(|e| e.to_string())
123 })
124 .await
125 }
126
127 pub async fn get_user_gameusers(&mut self, id: i32) -> Result<Vec<GameUser>, String> {
128 get_cached_value(
129 format!("user_gameusers_{id}"),
130 &mut self.cache,
131 async || {
132 sqlx::query_as("SELECT * FROM gameuser where user_id = $1;")
133 .bind(id)
134 .fetch_all(&self.pool)
135 .await
136 .map_err(|e| e.to_string())
137 },
138 )
139 .await
140 }
141
142 pub async fn get_user_chats(&mut self, id: i32) -> Result<Vec<Chat>, String> {
143 get_cached_value(format!("user_chats_{id}"), &mut self.cache, async || {
144 sqlx::query_as("SELECT * FROM chat where user_id = $1;")
145 .bind(id)
146 .fetch_all(&self.pool)
147 .await
148 .map_err(|e| e.to_string())
149 })
150 .await
151 }
152
153 pub async fn get_user_templates(&mut self, id: i32) -> Result<Vec<Template>, String> {
154 get_cached_value(
155 format!("user_templates_{id}"),
156 &mut self.cache,
157 async || {
158 sqlx::query_as("SELECT * FROM template where owner_id = $1;")
159 .bind(id)
160 .fetch_all(&self.pool)
161 .await
162 .map_err(|e| e.to_string())
163 },
164 )
165 .await
166 }
167
168 pub async fn get_map_by_id(&mut self, id: i32) -> Result<Map, String> {
169 get_cached_value(format!("map_{id}"), &mut self.cache, async || {
170 sqlx::query_as("SELECT * FROM map where id = $1;")
171 .bind(id)
172 .fetch_one(&self.pool)
173 .await
174 .map_err(|e| e.to_string())
175 })
176 .await
177 }
178
179 pub async fn get_map_stats(&mut self, id: i32) -> Result<Vec<MapStats>, String> {
180 get_cached_value(format!("stats_{id}"), &mut self.cache, async || {
181 sqlx::query_as("SELECT * FROM map_stats where map_id = $1;")
182 .bind(id)
183 .fetch_all(&self.pool)
184 .await
185 .map_err(|e| e.to_string())
186 })
187 .await
188 }
189
190 pub async fn get_mappool_by_id(&mut self, id: &str) -> Result<Mappool, String> {
191 get_cached_value(format!("{id}"), &mut self.cache, async || {
192 sqlx::query_as("SELECT * FROM mappool where id = $1;")
193 .bind(id)
194 .fetch_one(&self.pool)
195 .await
196 .map_err(|e| e.to_string())
197 })
198 .await
199 }
200
201 pub async fn get_square_by_id(&mut self, id: &str) -> Result<BingoSquare, String> {
202 get_cached_value(format!("{id}"), &mut self.cache, async || {
203 sqlx::query_as("SELECT * FROM bingo_square where id = $1;")
204 .bind(id)
205 .fetch_one(&self.pool)
206 .await
207 .map_err(|e| e.to_string())
208 })
209 .await
210 }
211
212 pub async fn get_maps_in_mappool(&mut self, id: &str) -> Result<Vec<MapInPool>, String> {
213 get_cached_value(format!("{id}"), &mut self.cache, async || {
214 sqlx::query_as("SELECT * FROM map_in_pool where pool_id = $1;")
215 .bind(id)
216 .fetch_all(&self.pool)
217 .await
218 .map_err(|e| e.to_string())
219 })
220 .await
221 }
222
223 pub async fn get_mapinpool_by_map(&mut self, id: i32) -> Result<Vec<MapInPool>, String> {
224 get_cached_value(format!("stats_{id}"), &mut self.cache, async || {
225 sqlx::query_as("SELECT * FROM map_in_pool where map_id = $1;")
226 .bind(id)
227 .fetch_all(&self.pool)
228 .await
229 .map_err(|e| e.to_string())
230 })
231 .await
232 }
233
234 pub async fn get_mapinpool_by_pool(&mut self, id: &str) -> Result<Vec<MapInPool>, String> {
235 get_cached_value(format!("stats_{id}"), &mut self.cache, async || {
236 sqlx::query_as("SELECT * FROM map_in_pool where pool_id = $1;")
237 .bind(id)
238 .fetch_all(&self.pool)
239 .await
240 .map_err(|e| e.to_string())
241 })
242 .await
243 }
244}
245
246async fn get_cached_value<T, F>(
247 query_key: String,
248 cache: &mut HashMap<String, Value>,
249 fetcher: F,
250) -> Result<T, String>
251where
252 T: Serialize + DeserializeOwned,
253 F: AsyncFnOnce() -> Result<T, String>,
254{
255 if !cache.contains_key(&query_key) {
256 let value: T = fetcher().await?;
257
258 cache.insert(
259 query_key.clone(),
260 serde_json::to_value(value).map_err(|x| x.to_string())?,
261 );
262 }
263
264 let query = cache.get(&query_key);
265 if let Some(content) = query {
266 return serde_json::from_value(content.clone()).map_err(|x| x.to_string());
267 }
268
269 return Err(String::from("Insert into hashmap must have failed"));
270}
271
272pub async fn pool() -> PgPool {
273 let db_url = std::env::var("DATABASE_URL").unwrap_or_else(|_| {
274 eprintln!("Environment variable DATABASE_URL should be set");
275 std::process::exit(1);
276 });
277
278 PgPool::connect(&db_url).await.unwrap()
279}
280
281pub struct DataContext(RwLock<Loader>);
282impl DataContext {
283 pub fn new(pool: PgPool) -> Self {
284 DataContext(RwLock::new(Loader::new(pool)))
285 }
286 pub async fn acquire(&self) -> RwLockWriteGuard<'_, Loader> {
287 self.0.write().await
288 }
289}
290
291impl juniper::Context for DataContext {}