use std::sync::Arc;
use tokio::process::Command;
use crate::{
config::Config, error::AppError, lock::LockManager, metadata::MetadataManager, r2::R2Client,
sync::SyncEngine,
};
pub async fn run_session(
project: &str,
config: Arc<Config>,
r2: Arc<R2Client>,
) -> Result<(), AppError> {
if !config.reaper.binary_path.exists() {
return Err(AppError::ReaperNotFound {
path: config.reaper.binary_path.display().to_string(),
});
}
let lm = LockManager::new(Arc::clone(&r2), Arc::clone(&config));
let sync_engine = SyncEngine::new(Arc::clone(&r2));
let metadata_manager = MetadataManager::new(Arc::clone(&r2));
let local_dir = config.local.working_dir.join(project);
println!("Acquiring lock for {}...", project);
let lock_guard = lm.acquire(project).await?;
println!("Pulling latest files...");
std::fs::create_dir_all(&local_dir).map_err(|e| AppError::IoError {
path: local_dir.display().to_string(),
source: e,
})?;
sync_engine.pull(project, &local_dir).await?;
let rpp_path = local_dir.join(format!("{}.rpp", project));
println!("Launching Reaper...");
let mut child = Command::new(&config.reaper.binary_path)
.arg(&rpp_path)
.spawn()
.map_err(|e| AppError::ReaperSpawnFailed(e.to_string()))?;
println!(
"Reaper launched (PID {}). Waiting for Reaper to exit...",
child
.id()
.map(|id| id.to_string())
.unwrap_or_else(|| "unknown".to_string())
);
let status = child
.wait()
.await
.map_err(|e| AppError::ReaperSpawnFailed(e.to_string()))?;
println!("Reaper exited ({}). Pushing changes...", status);
let push_result = sync_engine.push(project, &local_dir).await;
match push_result {
Ok(summary) => {
if let Err(e) = metadata_manager
.record_push(
project,
&config.identity.user,
(summary.files_uploaded + summary.files_skipped) as u32,
summary.total_bytes,
)
.await
{
eprintln!("Warning: failed to update project metadata: {}", e);
}
drop(lock_guard);
println!("Session complete. Lock released.");
Ok(())
}
Err(e) => {
std::mem::forget(lock_guard);
eprintln!(
"Reaper exited. Push failed: {}\n\n\
Your lock on {} is still held. Your local changes are safe.\n\
To retry: whirlwind push {}\n\
To give up: whirlwind unlock {}",
e, project, project, project
);
Err(e)
}
}
}