tap_msg/message/
reject.rs

1//! Reject message type for the Transaction Authorization Protocol.
2//!
3//! This module defines the Reject message type, which is used
4//! for rejecting transactions in the TAP protocol.
5
6use serde::{Deserialize, Serialize};
7
8use crate::error::{Error, Result};
9use crate::TapMessage;
10
11/// Reject message body (TAIP-4).
12#[derive(Debug, Clone, Serialize, Deserialize, TapMessage)]
13#[tap(message_type = "https://tap.rsvp/schema/1.0#Reject")]
14pub struct Reject {
15    /// ID of the transaction being rejected.
16    #[tap(thread_id)]
17    pub transaction_id: String,
18
19    /// Reason for rejection.
20    #[serde(skip_serializing_if = "Option::is_none", default)]
21    pub reason: Option<String>,
22}
23
24impl Reject {
25    /// Create a new Reject message
26    pub fn new(transaction_id: &str, reason: &str) -> Self {
27        Self {
28            transaction_id: transaction_id.to_string(),
29            reason: Some(reason.to_string()),
30        }
31    }
32
33    /// Create a minimal Reject message (for testing/special cases)
34    pub fn minimal(transaction_id: &str) -> Self {
35        Self {
36            transaction_id: transaction_id.to_string(),
37            reason: None,
38        }
39    }
40}
41
42impl Reject {
43    /// Custom validation for Reject messages
44    pub fn validate_reject(&self) -> Result<()> {
45        if self.transaction_id.is_empty() {
46            return Err(Error::Validation(
47                "Transaction ID is required in Reject".to_string(),
48            ));
49        }
50
51        // Note: reason is now optional to support minimal test cases
52        // In production use, a reason should typically be provided
53        if let Some(ref reason) = self.reason {
54            if reason.is_empty() {
55                return Err(Error::Validation(
56                    "Reason cannot be empty when provided".to_string(),
57                ));
58            }
59        }
60
61        Ok(())
62    }
63}