use axum::{ http::StatusCode, response::{IntoResponse, Response}, }; use ed25519_dalek::VerifyingKey; use serde::{Deserialize, Serialize}; use std::{ collections::{HashMap, HashSet}, sync::Arc, }; use tokio::sync::Mutex; // doing everything in memory for now #[derive(Clone)] pub struct AppState { pub users: Arc>>, pub signup_keys: Arc>>, pub friend_requests: Arc>>, pub links: Arc>>, pub positions: Arc>>, pub admin_id: Arc>>, pub ring_buffer_cap: usize, } #[derive(Debug, Deserialize, Clone)] pub struct AuthData { pub user_id: String, pub signature: String, } pub struct RingBuffer { pub ring: Box<[Option]>, pub idx: usize, } #[derive(Clone, Serialize)] pub struct EncryptedPing(pub String); // represents a ring buffer for a directed friend connection (ex.: user1 sending to user2, which in that case it's only user1's positions) impl RingBuffer { pub fn new(capacity: usize) -> Self { return Self { ring: vec![None; capacity].into_boxed_slice(), idx: 0, }; } pub fn add(&mut self, p: EncryptedPing) { self.idx = (self.idx + 1) % self.ring.len(); self.ring[self.idx] = Some(p); } /// Returns a `Vec` of all the `encrypted_ping` values in the ring buffer, /// starting from `self.idx` and iterating **backwards**, wrapping around to the end, /// skipping `None` entries. /// /// # Notes /// - The first element in the returned vector corresponds to the current index (`self.idx`). /// - `None` entries are ignored. pub fn flatten(&self) -> Vec { let len = self.ring.len(); let mut result = Vec::with_capacity(len); for i in 0..len { let temp = (self.idx + len - i) % len; let position = &self.ring[temp]; match position { Some(p) => result.push(p.clone()), None => continue, } } return result; } } #[derive(serde::Deserialize)] pub struct CreateUserRequest { pub signup_key: String, pub pub_key_b64: String, // base64-encoded public key } #[derive(Debug, PartialEq, Eq)] pub struct User { pub id: String, pub pub_key: VerifyingKey, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Link(String, String); impl Link { pub fn new(a: String, b: String) -> Self { if a < b { Link(a, b) } else { Link(b, a) } // normalize order } } #[derive(Serialize)] pub struct CreateAccountResponse { pub user_id: String, pub is_admin: bool, } #[derive(Deserialize)] pub struct PingPayload { pub receiver_id: String, pub encrypted_ping: String, } pub struct PlainBool(pub bool); impl IntoResponse for PlainBool { fn into_response(self) -> Response { self.0.to_string().into_response() } } #[derive(Serialize)] pub struct EncryptedPingVec(pub Vec); impl IntoResponse for EncryptedPingVec { fn into_response(self) -> Response { axum::Json(self.0).into_response() } } #[derive(Debug)] pub struct SrvErr { pub msg: String, pub cause: Option, } impl IntoResponse for SrvErr { fn into_response(self) -> Response { // Log once here (this runs only for real errors) match &self.cause { Some(c) => eprintln!("[ERR] {} | cause: {}", self.msg, c), None => eprintln!("[ERR] {}", self.msg), } let body = if cfg!(debug_assertions) { match &self.cause { Some(c) => format!("{} | cause: {}", self.msg, c), None => self.msg.clone(), } } else { self.msg.clone() }; (StatusCode::INTERNAL_SERVER_ERROR, body).into_response() } } /// Central policy: what gets logged, what gets returned. pub fn mk_srv_err(msg: impl Into, cause: Option) -> SrvErr { SrvErr { msg: msg.into(), cause, } } #[macro_export] macro_rules! SrvErr { ($msg:expr) => { $crate::mk_srv_err($msg, None) }; ($msg:expr, $err:expr) => { $crate::mk_srv_err($msg, Some(format!("{:?}", $err))) }; } #[macro_export] macro_rules! ReqBail { ($msg:expr) => {{ return Err($crate::SrvErr!($msg)); }}; ($msg:expr, $err:expr) => {{ return Err($crate::SrvErr!($msg, $err)); }}; }