use std::sync::Arc;
use serde_json::{Value, json};
use crate::daemon::state::DaemonState;
use crate::project::record::Project;
use crate::project::resolver;
pub async fn project_list(state: &Arc<DaemonState>) -> Result<Value, String> {
let registry = state.project_registry().await;
let projects = registry
.list()
.await
.map_err(|e| format!("project_list: registry error: {e}"))?;
let values = projects
.into_iter()
.map(|p| serde_json::to_value(p).map_err(|e| format!("project_list: serialize error: {e}")))
.collect::<Result<Vec<Value>, String>>()?;
let count = values.len();
Ok(json!({ "projects": values, "count": count }))
}
#[allow(clippy::too_many_arguments)]
pub async fn project_register(
state: &Arc<DaemonState>,
name: &str,
repo_url: &str,
default_branch: Option<&str>,
stack_hint: Option<&str>,
tags: Option<Vec<String>>,
description: Option<&str>,
gh_user: Option<&str>,
) -> Result<Value, String> {
let project = Project {
name: name.to_string(),
repo_url: repo_url.to_string(),
default_branch: default_branch.unwrap_or("main").to_string(),
stack_hint: stack_hint.map(str::to_string),
tags: tags.unwrap_or_default(),
description: description.map(str::to_string),
gh_user: gh_user.map(str::to_string),
};
let registry = state.project_registry().await;
registry
.register(project.clone())
.await
.map_err(|e| format!("project_register: registry error: {e}"))?;
serde_json::to_value(&project).map_err(|e| e.to_string())
}
pub async fn project_get(state: &Arc<DaemonState>, name: &str) -> Result<Value, String> {
let registry = state.project_registry().await;
let project = registry
.get(name)
.await
.map_err(|e| format!("project `{name}` not found: {e}"))?;
serde_json::to_value(&project).map_err(|e| e.to_string())
}
pub async fn project_resolve(state: &Arc<DaemonState>, query: &str) -> Result<Value, String> {
let registry = state.project_registry().await;
let projects = registry
.list()
.await
.map_err(|e| format!("project_resolve: registry error: {e}"))?;
match resolver::resolve_project(query, &projects) {
Ok(resolution) => {
let primary_val = resolution.primary.as_ref().map(|m| {
json!({
"project": serde_json::to_value(&m.project)
.unwrap_or(Value::Null),
"confidence": m.confidence,
"reason": m.reason.label(),
})
});
let all_matches: Vec<Value> = resolution
.matches
.iter()
.map(|m| {
json!({
"project": serde_json::to_value(&m.project)
.unwrap_or(Value::Null),
"confidence": m.confidence,
"reason": m.reason.label(),
})
})
.collect();
Ok(json!({
"primary": primary_val,
"needs_disambiguation": resolution.needs_disambiguation(),
"matches": all_matches,
}))
}
Err(resolver::ResolverError::NoMatch { query: q }) => Ok(json!({
"primary": null,
"needs_disambiguation": false,
"matches": [],
"error": format!("no project matched query: {q:?}"),
})),
Err(resolver::ResolverError::EmptyRegistry) => Ok(json!({
"primary": null,
"needs_disambiguation": false,
"matches": [],
"error": "project registry is empty; register at least one project first",
})),
}
}