diff --git a/Cargo.lock b/Cargo.lock index 61b0130..914c91b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -94,6 +94,7 @@ dependencies = [ name = "button" version = "0.1.0" dependencies = [ + "anyhow", "axum", "futures", "rand 0.10.1", diff --git a/Cargo.toml b/Cargo.toml index afa5fd7..5c8450b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] +anyhow = "1.0.102" axum = { version = "0.8.9", features = ["ws"] } futures = "0.3.32" rand = "0.10.1" diff --git a/src/main.rs b/src/main.rs index c07c40b..043c5dd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -57,24 +57,28 @@ const PATH_PINGSCORES: &str = "pingscores.json"; const MAX_LEADERBOARD: usize = 20; #[tokio::main] -async fn main() { - fn read_file de::Deserialize<'de>>(file_path: &str) -> Arc> { - let file_contents: String = fs::read_to_string(file_path).unwrap(); - Arc::new(Mutex::new(serde_json::from_str(&file_contents).unwrap())) +async fn main() -> anyhow::Result<()> { + fn read_file de::Deserialize<'de>>( + file_path: &str, + ) -> anyhow::Result>> { + let file_contents: String = fs::read_to_string(file_path)?; + Ok(Arc::new(Mutex::new(serde_json::from_str(&file_contents)?))) } /// Makes the vector at `vec` one with a capacity of exactly [`MAX_LEADERBOARD`] if `vec` is /// smaller or equal. fn exact_leaderboard(mut vec: impl DerefMut>) { - let mut old_vec = std::mem::replace(&mut *vec, Vec::with_capacity(MAX_LEADERBOARD)); - old_vec.drain(..).for_each(|e| vec.push(e)); + let old_vec = std::mem::replace(&mut *vec, Vec::with_capacity(MAX_LEADERBOARD)); + for e in old_vec { + vec.push(e); + } } - let hiscores: Arc>> = read_file(PATH_HISCORES); + let hiscores: Arc>> = read_file(PATH_HISCORES)?; exact_leaderboard(hiscores.lock().await); - let loscores: Arc>> = read_file(PATH_LOSCORES); + let loscores: Arc>> = read_file(PATH_LOSCORES)?; exact_leaderboard(loscores.lock().await); - let pingscores: Arc>> = read_file(PATH_PINGSCORES); + let pingscores: Arc>> = read_file(PATH_PINGSCORES)?; let (tx, rx) = mpsc::channel::(1024); @@ -91,17 +95,19 @@ async fn main() { tokio::spawn(async move { // write pingscores every 30s loop { - sleep(Duration::from_millis(30000)).await; + sleep(Duration::from_secs(30)).await; let pingscores = pingscores.lock().await; - let file_contents: String = serde_json::to_string(&pingscores.clone()).unwrap(); + let file_contents: String = serde_json::to_string(&pingscores.clone()) + .expect("failed to serialize pingscores"); drop(pingscores); let mut file = fs::OpenOptions::new() .write(true) .truncate(true) .open(PATH_PINGSCORES) - .unwrap(); - file.write_all(file_contents.as_bytes()).unwrap(); - file.flush().unwrap(); + .expect("failed to open pingscores file"); + file.write_all(file_contents.as_bytes()) + .expect("failed to write pingscores"); + drop(file); } }); } @@ -117,11 +123,13 @@ async fn main() { pingscores: Arc::clone(&pingscores), }); - let listener = tokio::net::TcpListener::bind("0.0.0.0:8084").await.unwrap(); + let listener = tokio::net::TcpListener::bind("0.0.0.0:8084").await?; println!("http://0.0.0.0:8084"); - axum::serve(listener, app).await.unwrap(); + axum::serve(listener, app).await?; + + Ok(()) } async fn index() -> Html<&'static str> { @@ -144,7 +152,7 @@ async fn handle_hiscores( name: &str, score: u32, file_path: &str, - ) { + ) -> anyhow::Result<()> { let scoretable = &mut *scoretable_lock; if let Some(index_to_insert_at) = scoretable.iter().position(|e| score > e.score) { @@ -161,15 +169,14 @@ async fn handle_hiscores( scoretable.push(push_out); } - let file_contents: String = serde_json::to_string(&*scoretable_lock).unwrap(); + let file_contents: String = serde_json::to_string(&*scoretable_lock)?; drop(scoretable_lock); let mut file = fs::OpenOptions::new() .write(true) .truncate(true) - .open(file_path) - .unwrap(); - file.write_all(file_contents.as_bytes()).unwrap(); - file.flush().unwrap(); + .open(file_path)?; + file.write_all(file_contents.as_bytes())?; + file.flush()?; } else if scoretable.len() < MAX_LEADERBOARD { println!("New {score_name} {score} by {name}"); scoretable.push(Entry { @@ -177,20 +184,24 @@ async fn handle_hiscores( person: name.to_string(), }); } + + Ok(()) } // Panic galore let mut hiscores_lock = hiscores.lock().await; hiscores_lock.sort(); hiscores_lock.reverse(); - let file_contents: String = serde_json::to_string(&hiscores_lock.clone()).unwrap(); + let file_contents: String = + serde_json::to_string(&hiscores_lock.clone()).expect("failed to serialize hiscores"); drop(hiscores_lock); let mut file = fs::OpenOptions::new() .write(true) .truncate(true) .open(PATH_HISCORES) - .unwrap(); - file.write_all(file_contents.as_bytes()).unwrap(); + .expect("failed to open hiscores"); + file.write_all(file_contents.as_bytes()) + .expect("failed to write hiscores"); drop(file); loop { @@ -205,7 +216,8 @@ async fn handle_hiscores( &name, hiscore_pingscore, PATH_HISCORES, - ); + ) + .expect("failed to update hiscores"); // Pingscore let mut pingscores = pingscores.lock().await; @@ -213,7 +225,7 @@ async fn handle_hiscores( if hiscore_pingscore > pingscores.get(&*name).unwrap_or(&(0, 0)).1 { pingscores.entry(name.to_string()).or_insert((0, 0)).1 = hiscore_pingscore; println!("{name} new PB: {hiscore_pingscore}"); - }; + } pingscores.entry(name.to_string()).or_insert((0, 0)).0 += 1; // reset count drop(pingscores); } @@ -225,7 +237,8 @@ async fn handle_hiscores( &name, loscore, PATH_LOSCORES, - ); + ) + .expect("failed to update loscores"); } } } @@ -254,14 +267,19 @@ async fn handle_socket( let mut value: u32 = 0; let msg = { - let hiscores = hiscores.lock().await; - let loscores = loscores.lock().await; - let pingscores = pingscores.lock().await; - json!({ "hiscores": &*hiscores, "loscores": &*loscores, "pingscores": &*pingscores}) - .to_string() + json!({ + "hiscores": &*hiscores.lock().await, + "loscores": &*loscores.lock().await, + "pingscores": &*pingscores.lock().await + }) + .to_string() }; - let name: Arc = match socket.next().await.unwrap().unwrap() { + let Some(name) = socket.next().await else { + eprintln!("user gave no username"); + return; + }; + let name: Arc = match name.expect("failed to recv socket msg") { Message::Text(text) if let text = text.to_string() && validate_name(&text) => @@ -292,7 +310,7 @@ async fn handle_socket( }) .await; resets += 1; - value = 0 + value = 0; } // 1/3 chance of failing else {