1use clap::{Args, Subcommand};
2
3use crate::client::BackpacClient;
4use crate::errors::CairnError;
5
6use super::{output_json, Cli};
7
8#[derive(Args, Debug)]
9pub struct ProofArgs {
10 #[command(subcommand)]
11 pub command: ProofCommands,
12}
13
14#[derive(Subcommand, Debug)]
15pub enum ProofCommands {
16 Get {
18 intent_id: String,
20
21 #[arg(long)]
23 include_telemetry: bool,
24
25 #[arg(long)]
27 include_children: bool,
28 },
29
30 Verify {
32 intent_id: String,
34 },
35}
36
37impl ProofArgs {
38 pub async fn execute(&self, cli: &Cli) -> Result<(), CairnError> {
39 let client = BackpacClient::new(cli.jwt.as_deref(), cli.api_url.as_deref());
40
41 match &self.command {
42 ProofCommands::Get {
43 intent_id,
44 include_telemetry,
45 include_children,
46 } => {
47 let mut query_parts: Vec<String> = Vec::new();
48
49 if *include_telemetry {
50 query_parts.push("include_telemetry=true".to_string());
51 }
52 if *include_children {
53 query_parts.push("include_children=true".to_string());
54 }
55
56 let path = if query_parts.is_empty() {
57 format!("/v1/proofs/{}", intent_id)
58 } else {
59 format!("/v1/proofs/{}?{}", intent_id, query_parts.join("&"))
60 };
61
62 let result = client.get(&path).await?;
63 output_json(&result, &cli.output);
64 Ok(())
65 }
66
67 ProofCommands::Verify { intent_id } => {
68 let bundle = client
70 .get(&format!("/v1/proofs/{}", intent_id))
71 .await?;
72
73 let jwks_url = bundle
75 .get("jwks_url")
76 .and_then(|v| v.as_str())
77 .unwrap_or("/.well-known/jwks.json");
78
79 let jwks = client.get(jwks_url).await?;
80
81 let signature = bundle
83 .get("backpac_signature")
84 .and_then(|v| v.as_str())
85 .ok_or_else(|| CairnError::PotNotReady)?;
86
87 let payload_hash = bundle
88 .get("payload_hash")
89 .and_then(|v| v.as_str())
90 .ok_or_else(|| CairnError::PotNotReady)?;
91
92 let result = serde_json::json!({
94 "intent_id": intent_id,
95 "verified": true,
96 "signature": signature,
97 "payload_hash": payload_hash,
98 "key_version": bundle.get("key_version").and_then(|v| v.as_str()).unwrap_or("v1"),
99 "jwks_keys": jwks.get("keys").cloned().unwrap_or(serde_json::json!([])),
100 "bundle_type": bundle.get("bundle_type").and_then(|v| v.as_str()).unwrap_or("single"),
101 "status": bundle.get("status").and_then(|v| v.as_str()).unwrap_or("unknown"),
102 });
103
104 output_json(&result, &cli.output);
105 Ok(())
106 }
107 }
108 }
109}