93 lines
4.9 KiB
Rust
93 lines
4.9 KiB
Rust
//! Git branch tools.
|
|
|
|
use super::ctx::GitToolCtx;
|
|
use agent::ToolRegistry;
|
|
|
|
async fn git_branch_list_exec(ctx: GitToolCtx, args: serde_json::Value) -> Result<serde_json::Value, String> {
|
|
let p: serde_json::Map<String, serde_json::Value> = serde_json::from_value(args).map_err(|e| e.to_string())?;
|
|
let project_name = p.get("project_name").and_then(|v| v.as_str()).ok_or("missing project_name")?;
|
|
let repo_name = p.get("repo_name").and_then(|v| v.as_str()).ok_or("missing repo_name")?;
|
|
let remote_only = p.get("remote_only").and_then(|v| v.as_bool()).unwrap_or(false);
|
|
|
|
let domain = ctx.open_repo(project_name, repo_name).await?;
|
|
let branches = domain.branch_list(remote_only).map_err(|e| e.to_string())?;
|
|
|
|
let result: Vec<_> = branches.iter().map(|b| {
|
|
let oid = b.oid.to_string();
|
|
serde_json::json!({
|
|
"name": b.name, "oid": oid.clone(), "short_oid": oid.get(..7).unwrap_or(&oid).to_string(),
|
|
"is_head": b.is_head, "is_remote": b.is_remote, "is_current": b.is_current,
|
|
"upstream": b.upstream.clone()
|
|
})
|
|
}).collect();
|
|
|
|
Ok(serde_json::to_value(result).map_err(|e| e.to_string())?)
|
|
}
|
|
|
|
async fn git_branch_info_exec(ctx: GitToolCtx, args: serde_json::Value) -> Result<serde_json::Value, String> {
|
|
let p: serde_json::Map<String, serde_json::Value> = serde_json::from_value(args).map_err(|e| e.to_string())?;
|
|
let project_name = p.get("project_name").and_then(|v| v.as_str()).ok_or("missing project_name")?;
|
|
let repo_name = p.get("repo_name").and_then(|v| v.as_str()).ok_or("missing repo_name")?;
|
|
let name = p.get("name").and_then(|v| v.as_str()).ok_or("missing name")?;
|
|
|
|
let domain = ctx.open_repo(project_name, repo_name).await?;
|
|
let info = domain.branch_get(name).map_err(|e| e.to_string())?;
|
|
|
|
let ahead_behind = if let Some(ref upstream) = info.upstream {
|
|
let (ahead, behind) = domain.branch_ahead_behind(name, upstream).unwrap_or((0, 0));
|
|
Some(serde_json::json!({ "ahead": ahead, "behind": behind }))
|
|
} else { None };
|
|
|
|
Ok(serde_json::json!({
|
|
"branch": { "name": info.name, "oid": info.oid.to_string(), "is_head": info.is_head,
|
|
"is_remote": info.is_remote, "is_current": info.is_current, "upstream": info.upstream },
|
|
"ahead_behind": ahead_behind
|
|
}))
|
|
}
|
|
|
|
async fn git_branches_merged_exec(ctx: GitToolCtx, args: serde_json::Value) -> Result<serde_json::Value, String> {
|
|
let p: serde_json::Map<String, serde_json::Value> = serde_json::from_value(args).map_err(|e| e.to_string())?;
|
|
let project_name = p.get("project_name").and_then(|v| v.as_str()).ok_or("missing project_name")?;
|
|
let repo_name = p.get("repo_name").and_then(|v| v.as_str()).ok_or("missing repo_name")?;
|
|
let branch = p.get("branch").and_then(|v| v.as_str()).ok_or("missing branch")?;
|
|
let into = p.get("into").and_then(|v| v.as_str()).map(|s| s.to_string()).unwrap_or_else(|| "main".to_string());
|
|
|
|
let domain = ctx.open_repo(project_name, repo_name).await?;
|
|
let is_merged = domain.branch_is_merged(branch, &into).map_err(|e| e.to_string())?;
|
|
let merge_base = domain.merge_base(&git::commit::types::CommitOid::new(branch), &git::commit::types::CommitOid::new(&into))
|
|
.map(|oid| oid.to_string()).ok();
|
|
|
|
Ok(serde_json::json!({ "branch": branch, "into": into, "is_merged": is_merged, "merge_base": merge_base }))
|
|
}
|
|
|
|
async fn git_branch_diff_exec(ctx: GitToolCtx, args: serde_json::Value) -> Result<serde_json::Value, String> {
|
|
let p: serde_json::Map<String, serde_json::Value> = serde_json::from_value(args).map_err(|e| e.to_string())?;
|
|
let project_name = p.get("project_name").and_then(|v| v.as_str()).ok_or("missing project_name")?;
|
|
let repo_name = p.get("repo_name").and_then(|v| v.as_str()).ok_or("missing repo_name")?;
|
|
let local = p.get("local").and_then(|v| v.as_str()).ok_or("missing local")?;
|
|
let remote = p.get("remote").and_then(|v| v.as_str()).unwrap_or(local).to_string();
|
|
|
|
let domain = ctx.open_repo(project_name, repo_name).await?;
|
|
let diff = domain.branch_diff(local, &remote).map_err(|e| e.to_string())?;
|
|
|
|
Ok(serde_json::json!({ "ahead": diff.ahead, "behind": diff.behind, "diverged": diff.diverged }))
|
|
}
|
|
|
|
macro_rules! register_fn {
|
|
($registry:expr, $name:expr, $exec:expr) => {
|
|
let handler_fn = move |ctx: agent::ToolContext, args: serde_json::Value| async move {
|
|
let gctx = super::ctx::GitToolCtx::new(ctx);
|
|
$exec(gctx, args)
|
|
.await
|
|
.map_err(agent::ToolError::ExecutionError)
|
|
};
|
|
$registry.register_fn($name, handler_fn);
|
|
};
|
|
}
|
|
|
|
pub fn register_git_tools(registry: &mut ToolRegistry) {
|
|
register_fn!(registry, "git_branch_list", git_branch_list_exec);
|
|
register_fn!(registry, "git_branch_info", git_branch_info_exec);
|
|
register_fn!(registry, "git_branches_merged", git_branches_merged_exec);
|
|
register_fn!(registry, "git_branch_diff", git_branch_diff_exec);
|
|
} |