use std::collections::HashMap;
use std::path::PathBuf;
use strop_core::id::{Arena, WorkspaceId, WorkspaceKind};
use strop_workspace::Filesystem;
pub struct WorkspaceContext {
pub filesystem: Filesystem,
pub root: Option<PathBuf>,
pub incarnation: u64,
}
#[derive(Default)]
pub struct WorkspaceRegistry {
arena: Arena<WorkspaceKind, WorkspaceContext>,
by_filesystem: HashMap<Filesystem, WorkspaceId>,
}
impl WorkspaceRegistry {
pub fn bind(&mut self, filesystem: Filesystem, root: Option<PathBuf>) -> WorkspaceId {
if let Some(id) = self.by_filesystem.get(&filesystem) {
return *id;
}
let id = self.arena.insert(WorkspaceContext {
filesystem: filesystem.clone(),
root,
incarnation: 0,
});
self.by_filesystem.insert(filesystem, id);
id
}
pub fn note_disconnect(&mut self, filesystem: &Filesystem) {
if let Some(id) = self.by_filesystem.get(filesystem) {
if let Some(context) = self.arena.get_mut(*id) {
context.incarnation += 1;
}
}
}
pub fn iter(&self) -> impl Iterator<Item = (WorkspaceId, &WorkspaceContext)> {
self.arena.iter()
}
}
#[cfg(test)]
mod tests {
use super::*;
use strop_workspace::RemoteEndpoint;
#[test]
fn rebinding_keeps_identity_and_disconnect_bumps_incarnation() {
let mut registry = WorkspaceRegistry::default();
let local = registry.bind(Filesystem::Local, Some(PathBuf::from("/work")));
assert_eq!(registry.bind(Filesystem::Local, None), local, "idempotent");
let endpoint = RemoteEndpoint::parse("ssh://dev@example.com:2222").unwrap();
let remote = registry.bind(Filesystem::Remote(endpoint.clone()), None);
assert_ne!(local, remote, "namespaces never share a slot");
let remote_fs = Filesystem::Remote(endpoint.clone());
let incarnation = |registry: &WorkspaceRegistry| {
registry
.iter()
.find(|(_, context)| context.filesystem == remote_fs)
.map(|(_, context)| context.incarnation)
};
assert_eq!(incarnation(®istry), Some(0));
registry.note_disconnect(&remote_fs);
assert_eq!(incarnation(®istry), Some(1));
assert_eq!(
registry.bind(Filesystem::Remote(endpoint), None),
remote,
"reconnect reuses the slot with the bumped incarnation"
);
let other = RemoteEndpoint::parse("ssh://other.example.com").unwrap();
registry.note_disconnect(&Filesystem::Remote(other));
}
}