use std::io::Write;
use std::path::PathBuf;
use clap::Args;
use pointbreak::model::ActorId;
use pointbreak::session::{
ACTOR_ATTRIBUTES_LOCAL_REL_PATH, ACTOR_ATTRIBUTES_REL_PATH, ActorAttributesStageOutcome,
ActorAttributesWriteRecord, ensure_shore_gitignore, stage_actor_attributes,
};
use serde::Serialize;
use crate::cli::json::DiagnosticDocument;
use crate::cli::output;
#[derive(Debug, Args)]
pub(super) struct AttestArgs {
actor: String,
#[arg(long)]
kind: String,
#[arg(long = "role")]
roles: Vec<String>,
#[arg(long)]
comment: Option<String>,
#[arg(long)]
local: bool,
#[arg(long, default_value = ".")]
repo: PathBuf,
#[arg(long)]
pretty: bool,
#[command(flatten)]
format_args: output::FormatArgs,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct AttestBody {
actor: String,
kind: String,
roles: Vec<String>,
comment: Option<String>,
path: String,
local: bool,
changed: bool,
}
pub(super) fn run(
args: AttestArgs,
stdout: &mut dyn Write,
stderr: &mut dyn Write,
) -> Result<(), Box<dyn std::error::Error>> {
let actor = ActorId::new(&args.actor);
let attrs = ActorAttributesWriteRecord::new(args.kind.clone())
.with_roles(args.roles.clone())
.with_comment(args.comment.clone());
let worktree_root =
pointbreak::git::git_worktree_root(&args.repo).unwrap_or_else(|_| args.repo.clone());
let rel = if args.local {
ACTOR_ATTRIBUTES_LOCAL_REL_PATH
} else {
ACTOR_ATTRIBUTES_REL_PATH
};
let path = worktree_root.join(rel);
if args.local {
ensure_shore_gitignore(&worktree_root)?; let _ = writeln!(
stderr,
"note: this local entry fully replaces any committed attributes for {} locally",
actor.as_str()
);
}
let ActorAttributesStageOutcome { changed } = stage_actor_attributes(&path, &actor, &attrs)?;
let _ = writeln!(
stderr,
"staged {}; review and `git commit` it to apply.",
path.display()
);
let body = AttestBody {
actor: actor.as_str().to_owned(),
kind: args.kind,
roles: args.roles,
comment: args.comment,
path: path.display().to_string(),
local: args.local,
changed,
};
let document = DiagnosticDocument::new("shore.identity-attest", body, Vec::new());
let format = output::resolve_format(
args.format_args.explicit(args.pretty),
output::OutputFormat::Json,
)?;
output::write_document_json_fallback(stdout, format, &document)
}