use std::sync::Arc;
use serde_json::{Value, json};
use crate::daemon::managed_routes::{SpawnParams, record_to_json, spawn_managed};
use crate::daemon::state::DaemonState;
use crate::session_manager::{ManagedError, ManagedSessionId};
fn parse_managed_id(raw: &str) -> Result<ManagedSessionId, String> {
raw.parse::<uuid::Uuid>()
.map(ManagedSessionId::from)
.map_err(|_| format!("`{raw}` is not a valid managed session id (expected a UUID)"))
}
fn managed_err(e: ManagedError) -> String {
e.to_string()
}
pub async fn session_new(
state: &Arc<DaemonState>,
repo_url: &str,
git_ref: &str,
task: &str,
name_hint: Option<&str>,
runtime: Option<&str>,
) -> Result<Value, String> {
let params = SpawnParams {
repo_url: repo_url.to_string(),
git_ref: git_ref.to_string(),
task: task.to_string(),
name_hint: name_hint.map(str::to_string),
runtime: runtime.map(str::to_string),
};
let record = spawn_managed(state, params).await?;
Ok(record_to_json(&record))
}
pub async fn session_stop(state: &Arc<DaemonState>, session_id: &str) -> Result<Value, String> {
let id = parse_managed_id(session_id)?;
let mgr = state.session_manager().await;
mgr.stop(&id)
.await
.map(|r| record_to_json(&r))
.map_err(managed_err)
}
pub async fn session_resume(state: &Arc<DaemonState>, session_id: &str) -> Result<Value, String> {
let id = parse_managed_id(session_id)?;
let record = crate::daemon::managed_routes::resume_managed(state, &id)
.await
.map_err(|e| e.to_string())?;
Ok(record_to_json(&record))
}
pub async fn session_decommission(
state: &Arc<DaemonState>,
session_id: &str,
) -> Result<Value, String> {
let id = parse_managed_id(session_id)?;
let mgr = state.session_manager().await;
mgr.decommission(&id)
.await
.map(|r| record_to_json(&r))
.map_err(managed_err)
}
pub async fn session_activity(
state: &Arc<DaemonState>,
session_id: &str,
lines: u32,
) -> Result<Value, String> {
let id = parse_managed_id(session_id)?;
let mgr = state.session_manager().await;
let record = mgr.get(&id).await.map_err(managed_err)?;
let raw_pane = mgr.capture_pane(&id, lines).await.unwrap_or_default();
let runtime_active = mgr.tmux_driver().session_exists(&record.tmux_name);
Ok(json!({
"id": record.id.to_string(),
"name": record.tmux_name,
"state": record.state.to_string(),
"raw_pane": raw_pane,
"lines": lines,
"runtime_active": runtime_active,
"pending_decision": record.pending_decision,
"proposed_default": record.proposed_default,
}))
}
pub async fn session_send(
state: &Arc<DaemonState>,
session_id: &str,
text: &str,
) -> Result<Value, String> {
let id = parse_managed_id(session_id)?;
let mgr = state.session_manager().await;
let record = mgr.get(&id).await.map_err(managed_err)?;
mgr.send_input(&id, text).await.map_err(managed_err)?;
Ok(json!({
"id": record.id.to_string(),
"sent": true,
"tmux_name": record.tmux_name,
}))
}
#[cfg(test)]
mod tests {
use super::*;
fn state() -> Arc<DaemonState> {
DaemonState::shared()
}
#[test]
fn parse_managed_id_rejects_garbage() {
let err = parse_managed_id("not-a-uuid").unwrap_err();
assert!(err.contains("not a valid managed session id"), "{err}");
}
#[tokio::test]
async fn unknown_id_errors_for_all_single_id_tools() {
let s = state();
let id = uuid::Uuid::new_v4().to_string();
assert!(
session_stop(&s, &id)
.await
.unwrap_err()
.contains("not found")
);
assert!(
session_resume(&s, &id)
.await
.unwrap_err()
.contains("not found")
);
assert!(
session_decommission(&s, &id)
.await
.unwrap_err()
.contains("not found")
);
assert!(
session_activity(&s, &id, 60)
.await
.unwrap_err()
.contains("not found")
);
assert!(
session_send(&s, &id, "hi")
.await
.unwrap_err()
.contains("not found")
);
}
#[tokio::test]
async fn garbage_id_rejected_for_all_single_id_tools() {
let s = state();
for r in [
session_stop(&s, "xx").await,
session_resume(&s, "xx").await,
session_decommission(&s, "xx").await,
session_activity(&s, "xx", 60).await,
session_send(&s, "xx", "t").await,
] {
assert!(r.unwrap_err().contains("valid managed session id"));
}
}
#[tokio::test]
async fn session_new_invalid_runtime_errors() {
let s = state();
let err = session_new(
&s,
"https://example.com/r.git",
"main",
"t",
None,
Some("bogus"),
)
.await
.unwrap_err();
assert!(err.contains("unknown runtime"), "{err}");
}
}