use crate::attach::AttachAction;
use crate::definition::SessionDefinition;
use crate::index::{rank_mixed, Ranked};
use crate::instance_registry::InstanceRegistry;
use crate::record::SessionRecord;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UnionRow {
pub label: String,
pub live: bool,
pub action: AttachAction,
}
#[must_use]
pub fn union_view(
records: &[SessionRecord],
defs: &[SessionDefinition],
registry: &InstanceRegistry,
query: &str,
now: u64,
) -> Vec<UnionRow> {
let mut items: Vec<Ranked> = records.iter().map(Ranked::Live).collect();
for d in defs {
if registry.instance_count(d.def_id) == 0 {
items.push(Ranked::Latent(d));
}
}
rank_mixed(items, query, now)
.into_iter()
.map(|r| match r {
Ranked::Live(rec) => UnionRow {
label: rec.display_name(),
live: true,
action: AttachAction::SwitchTo(rec.id),
},
Ranked::Latent(d) => UnionRow {
label: d.display_name(),
live: false,
action: AttachAction::Instantiate(d.def_id),
},
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::record::NameStyle;
use ishou_tokens::SessionNameStyle;
use std::path::PathBuf;
use tear_types::SessionId;
fn rec(id: &str, root: &str, visits: u32, last_seen: u64) -> SessionRecord {
let mut r = SessionRecord::for_project(
SessionId::from_seed(id),
PathBuf::from(root),
SessionNameStyle::Emoji,
last_seen,
);
r.visits = visits;
r
}
fn preset(root: &str, last_seen: u64) -> SessionDefinition {
SessionDefinition::single_pane(root, "/bin/zsh", NameStyle::Emoji, last_seen)
}
#[test]
fn live_and_latent_get_the_right_badges_and_verbs() {
let live = rec("live", "/code/running", 1, 100);
let p = preset("/code/preset", 100);
let registry = InstanceRegistry::new(); let rows = union_view(std::slice::from_ref(&live), std::slice::from_ref(&p), ®istry, "", 100);
assert_eq!(rows.len(), 2);
let live_row = rows.iter().find(|r| r.live).unwrap();
assert!(matches!(live_row.action, AttachAction::SwitchTo(_)));
let latent_row = rows.iter().find(|r| !r.live).unwrap();
assert_eq!(latent_row.action, AttachAction::Instantiate(p.def_id));
}
#[test]
fn a_preset_with_a_live_instance_is_not_a_duplicate_latent_row() {
let p = preset("/code/x", 100);
let mut registry = InstanceRegistry::new();
registry.register(p.def_id, SessionId(99)); let rows = union_view(&[], std::slice::from_ref(&p), ®istry, "", 100);
assert!(rows.is_empty());
}
#[test]
fn rows_are_ranked_by_one_order_live_and_latent_interleaved() {
let live = rec("l", "/code/live", 1, 100);
let mut p = preset("/code/preset", 100);
p.visits = 50;
let registry = InstanceRegistry::new();
let rows = union_view(std::slice::from_ref(&live), std::slice::from_ref(&p), ®istry, "", 100);
assert_eq!(rows.len(), 2);
assert!(!rows[0].live, "high-frecency latent preset ranks first");
assert!(rows[1].live);
}
}