use crate::actions::{GlobalArgs, text_manipulation::input_prompt_for};
use crate::types::context::BergContext;
use clap::Parser;
use forgejo_api::structs::VerifyGPGKeyOption;
use miette::{Context, IntoDiagnostic};
#[derive(Parser, Debug)]
pub struct VerifyGpgArgs {
key_id: String,
#[arg(short, long)]
armored_signature: Option<String>,
}
impl VerifyGpgArgs {
pub async fn run(self, global_args: GlobalArgs) -> miette::Result<()> {
let ctx = BergContext::new(self, global_args).await?;
match ctx.global_args.non_interactive {
true => verify_non_interactive(ctx).await,
false => verify_interactive(ctx).await,
}
}
}
async fn verify_interactive(ctx: BergContext<VerifyGpgArgs>) -> miette::Result<()> {
let signature = match ctx.args.armored_signature.clone() {
Some(signature) => signature,
None => {
let token = ctx
.client
.get_verification_token()
.await
.into_diagnostic()
.context("Could not get verification token from API!")?;
let key_id = ctx.args.key_id.clone();
let text = [
"Please sign the following verification token and then send it back to the API".to_string(),
format!("Step 1: Sign with `echo \"{token}\" | gpg -a --default-key {key_id} --detach-sig`"),
].join("\n");
println!("{text}");
inquire::Text::new(input_prompt_for("Step 2: Enter the signature here:").as_str())
.prompt()
.into_diagnostic()?
}
};
send_signature(&ctx, signature).await?;
Ok(())
}
async fn verify_non_interactive(ctx: BergContext<VerifyGpgArgs>) -> miette::Result<()> {
match ctx.args.armored_signature.clone() {
Some(signature) => send_signature(&ctx, signature).await,
None => get_token(&ctx).await,
}
}
async fn get_token(ctx: &BergContext<VerifyGpgArgs>) -> miette::Result<()> {
let token = ctx
.client
.get_verification_token()
.await
.into_diagnostic()
.context("Could not get verification token from API!")?;
let key_id = ctx.args.key_id.clone();
let text = [
"Please sign the following verification token and then send it back to the API".to_string(),
format!("Step 1: Sign with `echo \"{token}\" | gpg -a --default-key {key_id} --detach-sig`"),
format!("Step 2: Send it back via `berg keys gpg verify {key_id} --armored-signature <RESULT OF STEP 1> --non-interactive`")
].join("\n");
println!("{text}");
Ok(())
}
async fn send_signature(
ctx: &BergContext<VerifyGpgArgs>,
signature: impl AsRef<str>,
) -> miette::Result<()> {
let options = VerifyGPGKeyOption {
armored_signature: Some(signature.as_ref().to_string()),
key_id: ctx.args.key_id.clone(),
};
ctx.client
.user_verify_gpg_key(options)
.await
.into_diagnostic()
.map_err(|e| {
miette::miette!(
"{err}",
err = textwrap::refill(
e.to_string().as_str(),
ctx.global_args.max_width.unwrap_or(80) as usize
)
)
})
.context("Could not send verification token to API!")
.map(|_| ())
}