BPTGTWCABG7ZZBUXAPMUNSEET6Q55KG2NMGVZMVKUKYOWMHIF64QC fn new() -> Self {Self {rooms: HashMap::new(),
fn new(config: &Config) -> anyhow::Result<Self> {std::fs::create_dir_all(&config.state_path).context("accessing state")?;let env = heed::EnvOpenOptions::new().max_dbs(20) // FIXME: https://github.com/Kerollmops/heed/issues/106.open(&config.state_path).map_err(|e| anyhow::anyhow!("opening state: {}", e))?;let user_time = env.create_database(Some("user-time")).map_err(|e| anyhow::anyhow!("opening state: {}", e))?;let time_user = env.create_database(Some("time-user")).map_err(|e| anyhow::anyhow!("opening state: {}", e))?;Ok(Self {env,user_time,time_user,})}fn update(&self, config: &Config, rooms: Rooms) {let mut txn = self.env.write_txn().unwrap();for (id, room) in rooms.join {self.update_room(&mut txn, &config, id, room);
txn.commit().unwrap();}fn least_active(&self) -> Option<(SystemTime, RoomId, UserId)> {let txn = self.env.read_txn().unwrap();let time_user = self.time_user.remap_key_type::<TimeUserEntry>();let ((time, key), ()) = time_user.first(&txn).unwrap()?;let time = SystemTime::UNIX_EPOCH + Duration::from_secs(u64::from_be(time));Some((time,RoomId::try_from(key.room).unwrap(),UserId::try_from(key.user).unwrap(),))
fn least_active(&self) -> Option<(SystemTime, RoomId, UserId)> {let (room, (time, user)) = self.rooms.iter().filter_map(|(id, room)| Some((id, room.least_active()?))).min_by_key(|&(_room, (time, _user))| time)?;Some((time, room.clone(), user.clone()))}}struct RoomState {users: HashMap<UserId, SystemTime>,activity: BTreeMap<SystemTime, Vec<UserId>>,}impl RoomState {fn new() -> Self {Self {users: HashMap::new(),activity: BTreeMap::new(),}}fn handle_event(&mut self, config: &Config, event: &serde_json::value::RawValue) {
fn handle_event(&self,txn: &mut heed::RwTxn<'_, '_>,config: &Config,room: &RoomId,event: &serde_json::value::RawValue,) {
fn update(&mut self, user: &UserId) {let time = self.users.entry(user.clone()).or_insert_with(SystemTime::now);if let btree_map::Entry::Occupied(mut activity) = self.activity.entry(*time) {activity.get_mut().retain(|x| x != user);if activity.get().is_empty() {activity.remove();}
fn refresh_user(&self, txn: &mut heed::RwTxn<'_, '_>, room: &RoomId, user: &UserId) {let key = Key {room: room.as_str(),user: user.as_str(),};let time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();let user_time = self.user_time.remap_key_type::<heed::types::SerdeBincode<Key>>();let time_user = self.time_user.remap_key_type::<TimeUserEntry>();let prev_time = user_time.get(txn, &key).unwrap();user_time.put(txn, &key, &time).unwrap();if let Some(prev_time) = prev_time {time_user.delete(txn, &(prev_time.to_be(), key)).unwrap();
let now = SystemTime::now();let idle = now.duration_since(*time).unwrap_or(Duration::new(0, 0));*time = now;trace!(id = %user, ?idle, "updated user");self.activity.entry(*time).or_default().push(user.clone());
time_user.put(txn, &(time.to_be(), key), &()).unwrap();trace!(id = %user, age = time - prev_time.unwrap_or(time), "refreshed user");
fn remove(&mut self, user: &UserId) {if let Some(time) = self.users.remove(user) {if let btree_map::Entry::Occupied(mut activity) = self.activity.entry(time) {activity.get_mut().retain(|x| x != user);if activity.get().is_empty() {activity.remove();}}}
fn forget_user(&self, room: &RoomId, user: &UserId) {let mut txn = self.env.write_txn().unwrap();self.forget_user_inner(&mut txn, room, user);txn.commit().unwrap();
fn least_active(&self) -> Option<(SystemTime, &UserId)> {let (&time, users) = self.activity.iter().next()?;Some((time, &users[0]))
fn forget_user_inner(&self, txn: &mut heed::RwTxn<'_, '_>, room: &RoomId, user: &UserId) {let key = Key {room: room.as_str(),user: user.as_str(),};let user_time = self.user_time.remap_key_type::<heed::types::SerdeBincode<Key>>();let time_user = self.time_user.remap_key_type::<TimeUserEntry>();let time = user_time.get(txn, &key).unwrap();user_time.delete(txn, &key).unwrap();if let Some(time) = time {time_user.delete(txn, &(time.to_be(), key)).unwrap();}trace!(id = %user, "forgot user");
heed = { version = "0.11", features = ["mdbx", "serde-bincode"], default-features = false }