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 chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3
4use crate::database::DbContext;
5
6#[derive(sqlx::FromRow, Deserialize, Serialize)]
7pub struct PgUser {
8 pub id: i32,
9
10 pub username: String,
11 pub country_code: String,
12 pub country_name: String,
13
14 pub cover_url: String,
15 pub avatar_url: String,
16
17 pub pp: f32,
18 pub global_rank: Option<i32>,
19 pub country_rank: Option<i32>,
20
21 pub total_score: i64,
22 pub ranked_score: i64,
23 pub hit_accuracy: f32,
24 pub play_count: i32,
25 pub level: i32,
26 pub level_progress: i32,
27
28 pub last_refreshed: DateTime<Utc>,
29}
30
31impl DbContext {
32 pub async fn set_user(&mut self, user: PgUser) -> Result<PgUser, ()> {
33 let pool = match self.get_pg_connection().await {
34 Some(x) => x,
35 None => return Err(()),
36 };
37 let PgUser {
38 id,
39 username,
40 country_code,
41 country_name,
42 cover_url,
43 avatar_url,
44 pp,
45 global_rank,
46 country_rank,
47 total_score,
48 ranked_score,
49 hit_accuracy,
50 play_count,
51 level,
52 level_progress,
53 last_refreshed,
54 } = user.into();
55
56 let user_chk: Option<PgUser> =
57 match sqlx::query_as("SELECT * FROM public.user WHERE id = $1")
58 .bind(&id)
59 .fetch_optional(pool)
60 .await
61 {
62 Ok(x) => x,
63 Err(e) => {
64 log::error!("Failed to check for existing users: {e}");
65 return Err(());
66 }
67 };
68
69 if user_chk.is_none() {
70 match sqlx::query_as("INSERT INTO public.user (id, username, country_code, country_name, cover_url, avatar_url, pp, global_rank, country_rank, total_score, ranked_score, hit_accuracy, play_count, level, level_progress, last_refreshed) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16) RETURNING *;")
71 .bind(&id)
72 .bind(&username)
73 .bind(&country_code)
74 .bind(&country_name)
75 .bind(&cover_url)
76 .bind(&avatar_url)
77 .bind(&pp)
78 .bind(&global_rank)
79 .bind(&country_rank)
80 .bind(&total_score)
81 .bind(&ranked_score)
82 .bind(&hit_accuracy)
83 .bind(&play_count)
84 .bind(&level)
85 .bind(&level_progress)
86 .bind(&last_refreshed)
87 .fetch_one(pool)
88 .await {
89 Ok(x) => Ok(x),
90 Err(e) => {
91 log::error!("Failed to insert user object: {e}");
92 Err(())
93 }
94 }
95 } else {
96 match sqlx::query_as("UPDATE public.user SET id = $1, username = $2, country_code = $3, country_name = $4, cover_url = $5, avatar_url = $6, pp = $7, global_rank = $8, country_rank = $9, total_score = $10, ranked_score = $11, hit_accuracy = $12, play_count = $13, level = $14, level_progress = $15, last_refreshed = $16 WHERE id = $1")
97 .bind(&id)
98 .bind(&username)
99 .bind(&country_code)
100 .bind(&country_name)
101 .bind(&cover_url)
102 .bind(&avatar_url)
103 .bind(&pp)
104 .bind(&global_rank)
105 .bind(&country_rank)
106 .bind(&total_score)
107 .bind(&ranked_score)
108 .bind(&hit_accuracy)
109 .bind(&play_count)
110 .bind(&level)
111 .bind(&level_progress)
112 .bind(&last_refreshed)
113 .fetch_one(pool)
114 .await {
115 Ok(x) => Ok(x),
116 Err(e) => {
117 log::error!("Failed to insert user object: {e}");
118 Err(())
119 }
120 }
121 }
122 }
123}
124
125impl TryFrom<bingolib::structs::User> for PgUser {
126 type Error = TypeConversionError;
127
128 fn try_from(value: bingolib::structs::User) -> Result<Self, TypeConversionError> {
129 let bingolib::structs::User {
130 id,
131 username,
132 country_code,
133 country_name,
134 cover_url,
135 avatar_url,
136 pp,
137 global_rank,
138 country_rank,
139 total_score,
140 ranked_score,
141 hit_accuracy,
142 play_count,
143 level,
144 level_progress,
145 last_refreshed,
146 } = value;
147 let user = PgUser {
148 id: u32_to_i32(id, "id")?,
149 username,
150 country_code,
151 country_name,
152 cover_url,
153 avatar_url,
154 pp,
155 global_rank: global_rank
156 .map(|x| u32_to_i32(x, "global_rank"))
157 .transpose()?,
158 country_rank: country_rank
159 .map(|x| u32_to_i32(x, "country_rank"))
160 .transpose()?,
161 total_score: u64_to_i64(total_score, "total_score")?,
162 ranked_score: u64_to_i64(ranked_score, "ranked_score")?,
163 hit_accuracy,
164 play_count: u32_to_i32(play_count, "play_count")?,
165 level: u32_to_i32(level, "level")?,
166 level_progress: u32_to_i32(level_progress, "level_progress")?,
167 last_refreshed,
168 };
169
170 Ok(user)
171 }
172}
173
174#[derive(Debug)]
175pub struct TypeConversionError {
176 pub property: String,
177}
178
179fn u32_to_i32(x: u32, property: &str) -> Result<i32, TypeConversionError> {
180 match i32::try_from(x) {
181 Ok(x) => Ok(x),
182 Err(_) => Err(TypeConversionError {
183 property: property.into(),
184 }),
185 }
186}
187
188fn u64_to_i64(x: u64, property: &str) -> Result<i64, TypeConversionError> {
189 match i64::try_from(x) {
190 Ok(x) => Ok(x),
191 Err(_) => Err(TypeConversionError {
192 property: property.into(),
193 }),
194 }
195}