// // in_memory_server.rs // use std::collections::HashMap; // use std::hash::Hash; // use std::sync::Arc; // use nkode_rs::nkode_core::policy::NKodePolicy; // use tokio::sync::RwLock; // // use crate::models::app::{ // CodeLoginData, CodeLoginSession, Icon, KeyLoginSession, ClientAuthAPI, // }; // use crate::models::email::Email; // use crate::models::opaque::UserSecretKey; // // // #[derive(Clone)] // pub struct InMemoryServer { // state: Arc>, // policy: NKodePolicy, // icon_pool: Vec, // new_key_session: FKeySess, // new_code_session: FCodeSess, // } // // struct State { // users: HashMap, // key_sessions: HashMap, // code_sessions: HashMap, // } // // struct UserRecord { // secret_key: UserSecretKey, // code: Option, // } // // struct StoredCode { // passcode: Vec, // data: CodeLoginData, // } // // impl Default for State { // fn default() -> Self { // Self { // users: HashMap::new(), // key_sessions: HashMap::new(), // code_sessions: HashMap::new(), // } // } // } // // impl InMemoryServer { // /// `icon_pool` is what `get_new_icons()` returns (cloned) each time. // /// `new_key_session` and `new_code_session` let you decide how sessions are created // /// without changing the ServerAPI trait or guessing constructors. // pub fn new( // policy: NKodePolicy, // icon_pool: Vec, // new_key_session: FKeySess, // new_code_session: FCodeSess, // ) -> Self { // Self { // state: Arc::new(RwLock::new(State::default())), // policy, // icon_pool, // new_key_session, // new_code_session, // } // } // } // // impl InMemoryServer // where // // bounds needed for HashMap keys/values and cloning across calls // Email: Eq + Hash + Clone, // UserSecretKey: Clone + PartialEq, // Icon: Clone, // CodeLoginData: Clone, // KeyLoginSession: Eq + Hash + Clone, // CodeLoginSession: Eq + Hash + Clone, // FKeySess: Fn() -> KeyLoginSession + Send + Sync + 'static, // FCodeSess: Fn() -> CodeLoginSession + Send + Sync + 'static, // { // fn err(msg: impl Into) -> Result<(), String> { // Err(msg.into()) // } // // fn ok(v: T) -> Result { // Ok(v) // } // // async fn email_from_key_session(&self, sess: &KeyLoginSession) -> Result { // let st = self.state.read().await; // st.key_sessions // .get(sess) // .cloned() // .ok_or_else(|| "invalid key login session".to_string()) // } // } // // impl ClientAuthAPI for InMemoryServer // where // Email: Eq + Hash + Clone, // UserSecretKey: Clone + PartialEq, // Icon: Clone, // CodeLoginData: Clone, // KeyLoginSession: Eq + Hash + Clone, // CodeLoginSession: Eq + Hash + Clone, // // FKeySess: Fn() -> KeyLoginSession + Send + Sync + 'static, // FCodeSess: Fn() -> CodeLoginSession + Send + Sync + 'static, // { // async fn register_key(&self, email: &Email, secret_key: &UserSecretKey) -> Result<(), String> { // let mut st = self.state.write().await; // // if st.users.contains_key(email) { // return Err("email already registered".to_string()); // } // // st.users.insert( // email.clone(), // UserRecord { // secret_key: secret_key.clone(), // code: None, // }, // ); // // Ok(()) // } // // async fn login_key(&self, email: &Email, secret_key: &UserSecretKey) -> Result { // let mut st = self.state.write().await; // // let user = st.users.get(email).ok_or_else(|| "unknown email".to_string())?; // if &user.secret_key != secret_key { // return Err("invalid secret key".to_string()); // } // let sess = (self.new_key_session)(); // st.key_sessions.insert(sess.clone(), email.clone()); // Ok(sess) // } // // async fn is_code_registered(&self, key_login_session: &KeyLoginSession) -> Result { // let email = self.email_from_key_session(key_login_session).await?; // let st = self.state.read().await; // let user = st.users.get(&email).ok_or_else(|| "unknown email".to_string())?; // Ok(user.code.is_some()) // } // // async fn get_policy(&self) -> Result { // Ok(self.policy.clone()) // } // // async fn get_new_icons(&self, key_login_session: &KeyLoginSession) -> Result, String> { // // Validate session (mimics auth gate) // let _email = self.email_from_key_session(key_login_session).await?; // Ok(self.icon_pool.clone()) // } // // async fn register_code( // &self, // email: &Email, // passcode: &[u64], // key_login_session: &KeyLoginSession, // data: &CodeLoginData, // ) -> Result<(), String> { // let sess_email = self.email_from_key_session(key_login_session).await?; // if &sess_email != email { // return Err("session email mismatch".to_string()); // } // let mut st = self.state.write().await; // let user = st.users.get_mut(email).ok_or_else(|| "unknown email".to_string())?; // user.code = Some(StoredCode { // passcode: passcode.to_vec(), // data: data.clone(), // }); // Ok(()) // } // // async fn get_login_data(&self, key_login_session: &KeyLoginSession) -> Result { // let email = self.email_from_key_session(key_login_session).await?; // let st = self.state.read().await; // let user = st.users.get(&email).ok_or_else(|| "unknown email".to_string())?; // let code = user.code.as_ref().ok_or_else(|| "code not registered".to_string())?; // Ok(code.data.clone()) // } // // async fn login_code(&self, email: &Email, passcode: &[u64]) -> Result { // let mut st = self.state.write().await; // let user = st.users.get(email).ok_or_else(|| "unknown email".to_string())?; // let code = user.code.as_ref().ok_or_else(|| "code not registered".to_string())?; // if code.passcode.as_slice() != passcode { // return Err("invalid passcode".to_string()); // } // let sess = (self.new_code_session)(); // st.code_sessions.insert(sess.clone(), email.clone()); // Ok(sess) // } // }