Skip to main content

tap_cli/commands/
delivery.rs

1use crate::error::{Error, Result};
2use crate::output::{print_success, OutputFormat};
3use crate::tap_integration::TapIntegration;
4use clap::Subcommand;
5use serde::Serialize;
6
7#[derive(Subcommand, Debug)]
8pub enum DeliveryCommands {
9    /// List message deliveries
10    List {
11        /// Filter by recipient DID
12        #[arg(long, group = "filter")]
13        recipient: Option<String>,
14        /// Filter by message ID
15        #[arg(long, group = "filter")]
16        message: Option<String>,
17        /// Filter by thread ID
18        #[arg(long, group = "filter")]
19        thread: Option<String>,
20        /// Agent DID for storage lookup
21        #[arg(long)]
22        agent_did: Option<String>,
23        /// Maximum results
24        #[arg(long, default_value = "50")]
25        limit: u32,
26        /// Offset for pagination
27        #[arg(long, default_value = "0")]
28        offset: u32,
29    },
30}
31
32#[derive(Debug, Serialize)]
33struct DeliveryInfo {
34    id: i64,
35    message_id: String,
36    recipient_did: String,
37    status: String,
38    retry_count: i32,
39    delivery_type: String,
40    created_at: String,
41    updated_at: String,
42    delivered_at: Option<String>,
43    error_message: Option<String>,
44}
45
46#[derive(Debug, Serialize)]
47struct DeliveryListResponse {
48    deliveries: Vec<DeliveryInfo>,
49    total: usize,
50}
51
52fn to_delivery_info(d: &tap_node::storage::models::Delivery) -> DeliveryInfo {
53    DeliveryInfo {
54        id: d.id,
55        message_id: d.message_id.clone(),
56        recipient_did: d.recipient_did.clone(),
57        status: format!("{:?}", d.status),
58        retry_count: d.retry_count,
59        delivery_type: format!("{:?}", d.delivery_type),
60        created_at: d.created_at.clone(),
61        updated_at: d.updated_at.clone(),
62        delivered_at: d.delivered_at.clone(),
63        error_message: d.error_message.clone(),
64    }
65}
66
67pub async fn handle(
68    cmd: &DeliveryCommands,
69    format: OutputFormat,
70    default_agent_did: &str,
71    tap_integration: &TapIntegration,
72) -> Result<()> {
73    match cmd {
74        DeliveryCommands::List {
75            recipient,
76            message,
77            thread,
78            agent_did,
79            limit,
80            offset,
81        } => {
82            let effective_did = agent_did.as_deref().unwrap_or(default_agent_did);
83            let storage = tap_integration.storage_for_agent(effective_did).await?;
84
85            let deliveries = if let Some(recipient) = recipient {
86                storage
87                    .get_deliveries_by_recipient(recipient, *limit, *offset)
88                    .await?
89            } else if let Some(message_id) = message {
90                storage.get_deliveries_for_message(message_id).await?
91            } else if let Some(thread_id) = thread {
92                storage
93                    .get_deliveries_for_thread(thread_id, *limit, *offset)
94                    .await?
95            } else {
96                return Err(Error::invalid_parameter(
97                    "One of --recipient, --message, or --thread is required",
98                ));
99            };
100
101            let delivery_infos: Vec<DeliveryInfo> =
102                deliveries.iter().map(to_delivery_info).collect();
103
104            let response = DeliveryListResponse {
105                total: delivery_infos.len(),
106                deliveries: delivery_infos,
107            };
108            print_success(format, &response);
109            Ok(())
110        }
111    }
112}