use crate::core::entity::{Instance, InstanceId, OrganizationId};
fn newest_first(instances: &[Instance]) -> Vec<&Instance> {
let mut sorted: Vec<&Instance> = instances.iter().collect();
sorted.sort_by(|a, b| b.view.updated_at.cmp(&a.view.updated_at));
sorted
}
pub fn to_reopen<'a>(
instances: &'a [Instance],
me: &InstanceId,
organization: Option<&OrganizationId>,
) -> Option<&'a Instance> {
let candidates: Vec<&Instance> = newest_first(instances)
.into_iter()
.filter(|i| i.id.pid != me.pid || i.closed())
.filter(|i| match (&i.view.organization, organization) {
(Some(theirs), Some(ours)) => theirs.id == *ours,
_ => true,
})
.collect();
let quit_last = candidates
.iter()
.filter(|i| i.closed())
.max_by(|a, b| a.view.closed_at.cmp(&b.view.closed_at))
.copied();
quit_last.or(candidates.first().copied())
}
pub fn to_report(instances: &[Instance]) -> Option<(&Instance, bool)> {
let sorted = newest_first(instances);
match sorted.iter().find(|i| i.running && !i.closed()) {
Some(open) => Some((open, true)),
None => sorted.first().map(|i| (*i, false)),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::entity::snapshot::ViewSnapshot;
fn instance(pid: u32, updated: u32, closed: Option<u32>, running: bool) -> Instance {
let at = |hour: u32| format!("2026-09-25T{hour:02}:00:00Z");
let mut view: ViewSnapshot = serde_json::from_str(&format!(
r#"{{"version":1,"workspace":"/repo","cwd":"/repo","pid":{pid},
"updated_at":"{}","destination":{{"kind":"my_issues"}},"screen":"issue_list",
"group_by":"status"}}"#,
at(updated)
))
.unwrap();
view.closed_at = closed.map(at);
Instance {
id: InstanceId {
workspace: "/repo".into(),
pid,
},
view,
running,
}
}
fn me(pid: u32) -> InstanceId {
InstanceId {
workspace: "/repo".into(),
pid,
}
}
#[test]
fn the_instance_that_quit_last_is_reopened() {
let instances = [
instance(1, 9, Some(9), false),
instance(2, 10, Some(10), false),
instance(3, 11, None, false),
];
assert_eq!(to_reopen(&instances, &me(99), None).unwrap().id.pid, 2);
}
#[test]
fn without_a_clean_quit_the_last_view_recorded_is_reopened() {
let instances = [instance(1, 9, None, false), instance(2, 11, None, false)];
assert_eq!(to_reopen(&instances, &me(99), None).unwrap().id.pid, 2);
}
#[test]
fn a_record_under_my_own_pid_is_passed_over_unless_it_quit() {
let stale = [instance(7, 11, None, true), instance(1, 9, None, false)];
assert_eq!(to_reopen(&stale, &me(7), None).unwrap().id.pid, 1);
let quit = [instance(7, 11, Some(11), false)];
assert_eq!(to_reopen(&quit, &me(7), None).unwrap().id.pid, 7);
}
#[test]
fn a_view_of_another_linear_workspace_is_not_reopened() {
let of = |pid, closed: u32, org: &str| {
let mut i = instance(pid, closed, Some(closed), false);
i.view.organization = Some(crate::core::entity::snapshot::OrganizationRef {
id: org.into(),
name: org.into(),
url_key: org.into(),
});
i
};
let instances = [of(1, 8, "acme"), of(2, 9, "globex")];
let acme = OrganizationId::from("acme");
assert_eq!(
to_reopen(&instances, &me(99), Some(&acme)).unwrap().id.pid,
1
);
let initech = OrganizationId::from("initech");
assert!(to_reopen(&instances, &me(99), Some(&initech)).is_none());
assert_eq!(to_reopen(&instances, &me(99), None).unwrap().id.pid, 2);
}
#[test]
fn with_no_record_nothing_is_reopened() {
assert!(to_reopen(&[], &me(1), None).is_none());
}
#[test]
fn an_agent_reads_the_open_instance_that_moved_last() {
let instances = [
instance(1, 9, None, true),
instance(2, 10, None, true),
instance(3, 11, Some(11), false),
];
let (shown, open) = to_report(&instances).unwrap();
assert_eq!((shown.id.pid, open), (2, true));
}
#[test]
fn with_none_open_the_last_view_is_reported_as_closed() {
let instances = [instance(1, 9, Some(9), false), instance(2, 10, None, false)];
let (shown, open) = to_report(&instances).unwrap();
assert_eq!((shown.id.pid, open), (2, false));
}
}