use std::path::Path;
use clap::Parser;
use pijul_core::changestore::ChangeStore;
use pijul_core::{Base32, Hash, TxnT};
use crate::commands::common_opts::RepoPath;
#[derive(Parser, Debug)]
pub struct Approve {
#[clap(flatten)]
base: RepoPath,
#[clap(value_name = "HASH")]
hash: String,
#[clap(long = "identity")]
identity: Option<String>,
}
impl Approve {
pub fn repository_path(&mut self) -> Option<&Path> {
self.base.repo_path()
}
pub async fn run(mut self, config: &pijul_config::Config) -> Result<(), anyhow::Error> {
let repo = self.base.find_root()?;
let txn = repo.pristine.txn_begin()?;
let hash = if let Some(h) = Hash::from_base32(self.hash.as_bytes()) {
h
} else {
txn.hash_from_prefix(&self.hash)?.0
};
let mut change = repo.changes.get_change(&hash)?;
let identity_name = self
.identity
.unwrap_or(pijul_identity::choose_identity_name(config).await?);
let complete = pijul_identity::Complete::load(&identity_name)?;
let secret = complete.skey();
let public_key = complete.public_key.key.clone();
let sig = pijul_identity::sign_pem(&secret, &hash.to_bytes()).await?;
let entry = serde_json::json!({ "key": public_key, "signature": sig });
match change.unhashed {
None => {
change.unhashed = Some(serde_json::json!({ "approvals": [entry] }));
}
Some(ref mut v) => {
if let Some(approvals) = v.get_mut("approvals").and_then(|a| a.as_array_mut()) {
approvals.push(entry);
} else {
v["approvals"] = serde_json::json!([entry]);
}
}
}
let new_hash = repo
.changes
.save_change(&mut change, |_, _| Ok::<_, anyhow::Error>(()))?;
assert_eq!(
new_hash, hash,
"approve: hash changed after updating unhashed — this is a bug"
);
let mut stdout = std::io::stdout();
use std::io::Write;
writeln!(stdout, "Approved: {}", hash.to_base32())?;
Ok(())
}
}