use std::collections::BTreeSet;
use std::path::Path;
use serde::Serialize;
use tau_harness::runtime_dir::RunningSession;
use crate::CliError;
#[derive(Serialize)]
struct SessionListJsonEntry<'a> {
session_id: &'a tau_proto::SessionId,
project_root: &'a Path,
}
pub(crate) fn run(args: &crate::cli::SessionListArgs) -> Result<(), CliError> {
let snapshot = tau_harness::runtime_dir::list_running_sessions_tolerant()?;
let output = render(&snapshot.sessions, args)?;
crate::line_output::write_stdout(&output)?;
if let Some(warning) = incomplete_claim_warning(snapshot.incomplete_claims) {
eprintln!("{warning}");
}
Ok(())
}
fn incomplete_claim_warning(incomplete_claims: usize) -> Option<String> {
match incomplete_claims {
0 => None,
1 => Some(
"warning: omitted 1 contended runtime claim that did not complete compatible exact-session admission"
.to_owned(),
),
count => Some(format!(
"warning: omitted {count} contended runtime claims that did not complete compatible exact-session admission"
)),
}
}
fn render(
sessions: &[RunningSession],
args: &crate::cli::SessionListArgs,
) -> Result<String, CliError> {
let matching = sessions
.iter()
.filter(|session| {
args.dir
.as_deref()
.is_none_or(|directory| session.project_root == directory)
})
.collect::<Vec<_>>();
if args.json {
let rows = matching
.into_iter()
.map(|session| SessionListJsonEntry {
session_id: &session.session_id,
project_root: &session.project_root,
})
.collect::<Vec<_>>();
let mut output = serde_json::to_string(&rows).map_err(|error| {
CliError::Participant(format!("failed to serialize running sessions: {error}"))
})?;
output.push('\n');
return Ok(output);
}
let session_ids = matching
.into_iter()
.map(|session| session.session_id.as_str())
.collect::<BTreeSet<_>>();
let mut output = String::new();
for session_id in session_ids {
output.push_str(&crate::line_output::escape_field(session_id));
output.push('\n');
}
Ok(output)
}
#[cfg(test)]
#[path = "list_sessions/tests.rs"]
mod tests;