use std::path::{Path, PathBuf};
use std::sync::Arc;
use serde::Deserialize;
use trusty_common::uds::server::RpcRouter;
use crate::core::project::ProjectInfo;
use crate::daemon::api::types::{
DiscoverProjectsResponse, DiscoveredProjectInfo, ProjectsResponse,
};
use crate::daemon::error::DaemonError;
use crate::daemon::managed_routes::ProjectStatusResponse;
use crate::daemon::managed_routes::project_registry_routes as reg;
use crate::daemon::managed_routes::project_status;
use crate::daemon::state::DaemonState;
use crate::project::Project;
use super::NoParams;
#[derive(Debug, Deserialize)]
pub struct NameParams {
pub name: String,
}
#[derive(Debug, Deserialize)]
pub struct PatchProjectParams {
pub name: String,
#[serde(flatten)]
pub body: reg::PatchProjectBody,
}
#[derive(Debug, Deserialize)]
pub struct PathParams {
pub path: PathBuf,
}
pub fn register_project_op(state: &Arc<DaemonState>, path: PathBuf) -> ProjectInfo {
crate::core::git_maintenance::disable_and_log(&path);
crate::session_manager::worktree_marker_migration::prepare_checkout(&path);
let info = state.register_project(path.clone());
let store_dir = crate::project::registry_data_dir_under(state.framework_root());
let name = crate::project::project_owning_checkout(&store_dir, &path)
.unwrap_or_else(|| info.name.clone());
let report = crate::project::backfill_checkout(&store_dir, &name, &path, chrono::Utc::now());
if report != crate::project::BackfillReport::default() {
tracing::info!(
project = %name,
checkout = %path.display(),
recorded = report.recorded,
already_recorded = report.already_recorded,
claimed_by_another = report.claimed_by_another,
skipped = report.skipped,
"#7588: `tm project init` adopted pre-existing worktrees too"
);
}
info
}
pub fn list_projects_op(state: &Arc<DaemonState>) -> ProjectsResponse {
ProjectsResponse {
projects: state.list_projects(),
}
}
pub fn current_project_op(
state: &Arc<DaemonState>,
path: &Path,
) -> Result<ProjectInfo, DaemonError> {
state
.project(path)
.ok_or_else(|| DaemonError::SessionNotFound {
id: path.display().to_string(),
})
}
pub fn discover_projects_op() -> DiscoverProjectsResponse {
let projects = crate::core::project_discovery::ProjectDiscovery::discover()
.into_iter()
.map(|p| DiscoveredProjectInfo {
path: p.path.display().to_string(),
session_count: p.session_count,
last_session: p.last_session.map(system_time_to_iso8601),
})
.collect();
DiscoverProjectsResponse { projects }
}
fn system_time_to_iso8601(time: std::time::SystemTime) -> String {
let datetime: chrono::DateTime<chrono::Utc> = time.into();
datetime.to_rfc3339()
}
pub fn register(router: RpcRouter, state: &Arc<DaemonState>) -> RpcRouter {
let held = Arc::clone(state);
let r = router.typed::<NoParams, ProjectsResponse, _, _>("mpm.projects.list", move |_| {
let s = Arc::clone(&held);
async move { Ok(list_projects_op(&s)) }
});
let held = Arc::clone(state);
let r = r.typed::<PathParams, ProjectInfo, _, _>("mpm.projects.register", move |p| {
let s = Arc::clone(&held);
async move { Ok(register_project_op(&s, p.path)) }
});
let held = Arc::clone(state);
let r = r.typed::<PathParams, ProjectInfo, _, _>("mpm.projects.current", move |p| {
let s = Arc::clone(&held);
async move { current_project_op(&s, &p.path).map_err(Into::into) }
});
let r = r.typed::<NoParams, DiscoverProjectsResponse, _, _>(
"mpm.projects.discover",
move |_| async move { Ok(discover_projects_op()) },
);
let held = Arc::clone(state);
let r = r.typed::<NoParams, reg::ProjectsListResponse, _, _>(
"mpm.projects.registry.list",
move |_| {
let s = Arc::clone(&held);
async move { reg::list_projects_registry_op(&s).await.map_err(Into::into) }
},
);
let held = Arc::clone(state);
let r = r.typed::<reg::RegisterProjectBody, crate::daemon::project_adoption::RegisterProjectResponse, _, _>(
"mpm.projects.registry.register",
move |p| {
let s = Arc::clone(&held);
async move {
reg::register_project_registry_op(&s, p)
.await
.map_err(Into::into)
}
},
);
let held = Arc::clone(state);
let r = r.typed::<NameParams, Project, _, _>("mpm.projects.registry.get", move |p| {
let s = Arc::clone(&held);
async move {
reg::get_project_registry_op(&s, &p.name)
.await
.map_err(Into::into)
}
});
let held = Arc::clone(state);
let r = r.typed::<PatchProjectParams, Project, _, _>("mpm.projects.registry.patch", move |p| {
let s = Arc::clone(&held);
async move {
reg::patch_project_registry_op(&s, &p.name, p.body)
.await
.map_err(Into::into)
}
});
let held = Arc::clone(state);
r.typed::<NameParams, ProjectStatusResponse, _, _>("mpm.projects.status", move |p| {
let s = Arc::clone(&held);
async move {
project_status::project_status_op(&s, &p.name)
.await
.map_err(Into::into)
}
})
}