use std::sync::atomic::{AtomicU64, Ordering};
use crate::comms::ids::{self, AgentId, RoomId};
use crate::comms::model::{Room, RoomScope, now_micros};
use crate::comms::scope::{self, ScopeChain};
pub(super) fn build_chain(remote: Option<String>, cwd: Option<std::path::PathBuf>) -> ScopeChain {
match cwd {
Some(cwd) => {
let repo = crate::git::Repo::discover(&cwd).ok();
let mut chain = scope::scope_chain(&cwd, repo.as_ref());
if chain.remote.is_none() {
chain.remote = remote;
}
chain
}
None => ScopeChain {
remote,
cwd: std::path::PathBuf::new(),
ancestors: Vec::new(),
session_id: None,
parent_agent: None,
},
}
}
pub(super) fn default_room_for(chain: &ScopeChain) -> Room {
let (room_id, scope, title) = match (&chain.remote, chain.cwd.as_os_str().is_empty()) {
(Some(remote), _) => (
RoomId::parse(sanitize_id(remote)).unwrap_or_else(|_| fallback_room()),
RoomScope::Remote(remote.clone()),
format!("workspace: {remote}"),
),
(None, false) => {
let path = chain.cwd.clone();
(
RoomId::parse(sanitize_id(&path.to_string_lossy()))
.unwrap_or_else(|_| fallback_room()),
RoomScope::PathPrefix(path.clone()),
format!("workspace: {}", path.display()),
)
}
(None, true) => (fallback_room(), RoomScope::Global, "global".to_string()),
};
Room {
room_id,
scope,
title,
created_at: now_micros(),
last_activity: 0,
}
}
pub(crate) fn repo_room_for(remote: Option<String>, cwd: Option<std::path::PathBuf>) -> Room {
let chain = ScopeChain {
remote,
cwd: cwd.unwrap_or_default(),
ancestors: Vec::new(),
session_id: None,
parent_agent: None,
};
default_room_for(&chain)
}
fn fallback_room() -> RoomId {
RoomId::parse("global").expect("`global` is a valid room id")
}
pub(super) fn sanitize_id(s: &str) -> String {
let mut out: String = s
.chars()
.map(|c| {
if c.is_ascii_alphanumeric() || matches!(c, '.' | '_' | ':' | '-') {
c
} else {
'-'
}
})
.collect();
if out.len() > ids::MAX_ID_LEN {
out.truncate(ids::MAX_ID_LEN);
}
if out.is_empty() {
out.push('x');
}
out
}
pub(super) fn mint_message_id(room: &RoomId, agent: &AgentId) -> String {
static COUNTER: AtomicU64 = AtomicU64::new(0);
let n = COUNTER.fetch_add(1, Ordering::Relaxed);
format!(
"{}:{}:{}:{}",
room.as_str(),
agent.as_str(),
now_micros(),
n
)
}