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 }