use std::collections::HashSet;
use std::path::Path;
use super::manager::{ManagedError, SessionManager};
use super::record::ManagedSessionId;
impl SessionManager {
pub(crate) async fn resolve_session_name(
&self,
name_hint: Option<&str>,
github_project: Option<&str>,
cwd: &Path,
extra_collision: impl Fn(&str) -> bool,
) -> Result<String, ManagedError> {
let mut existing_names = self.names_for_serial_allocation().await?;
let leaf_hint = name_hint.map(|h| crate::core::names::leaf_slug_from_dir(Path::new(h)));
const MAX_NAME_ALLOCATION_ATTEMPTS: u8 = 5;
for _ in 0..MAX_NAME_ALLOCATION_ATTEMPTS {
let candidate = if let Some(ref leaf) = leaf_hint {
crate::core::names::build_session_name(leaf, &existing_names)?
} else {
crate::core::names::build_managed_session_name(
github_project,
cwd,
&existing_names,
)?
};
if self.tmux.session_exists(&candidate) || extra_collision(&candidate) {
existing_names.push(candidate);
continue;
}
return Ok(candidate);
}
Err(ManagedError::NameCollision(format!(
"no free `tm-<leaf>-NN` name after {MAX_NAME_ALLOCATION_ATTEMPTS} attempts \
(sustained concurrent creates for the same project)"
)))
}
pub(crate) fn taken_name_set(
live_tmux_names: &[String],
records: &[super::record::SessionRecord],
exclude_id: Option<&ManagedSessionId>,
) -> HashSet<String> {
let mut taken: HashSet<String> = live_tmux_names.iter().cloned().collect();
taken.extend(
records
.iter()
.filter(|r| exclude_id != Some(&r.id) && !r.state.is_terminal())
.map(|r| r.tmux_name.clone()),
);
taken
}
pub(crate) fn dedupe_name_against(candidate: &str, taken: &HashSet<String>) -> String {
let result =
trusty_common::session_naming::dedupe_by_ordinal(candidate, |c| taken.contains(c));
if result.chars().count() <= MAX_SESSION_NAME_LEN {
return result;
}
let short: String = candidate
.chars()
.take(MAX_SESSION_NAME_LEN - SUFFIX_HEADROOM)
.collect();
trusty_common::session_naming::dedupe_by_ordinal(&short, |c| taken.contains(c))
}
pub(crate) async fn dedupe_session_name(
&self,
candidate: &str,
exclude_id: Option<&ManagedSessionId>,
) -> Result<String, ManagedError> {
let live_names = self
.tmux
.list_sessions()
.map_err(|e| ManagedError::TmuxUnavailable(e.to_string()))?;
let records = self.list().await;
let taken = Self::taken_name_set(&live_names, &records, exclude_id);
Ok(Self::dedupe_name_against(candidate, &taken))
}
}
pub(crate) const MAX_SESSION_NAME_LEN: usize = 64;
const SUFFIX_HEADROOM: usize = 16;