Two teams try and fill in any horizontal, vertical, or diagonal line on a bingo board by playing maps on osu! osu.bingo
osu
at microservice 195 lines 6.0 kB view raw
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}