tap_msg/message/
connection.rs1use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9use crate::error::{Error, Result};
10use crate::message::tap_message_trait::{TapMessage as TapMessageTrait, TapMessageBody};
11use crate::TapMessage;
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct TransactionLimits {
16 pub max_amount: Option<String>,
18
19 pub max_total_amount: Option<String>,
21
22 pub max_transactions: Option<u64>,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct ConnectionConstraints {
29 pub transaction_limits: Option<TransactionLimits>,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize, TapMessage)]
35#[tap(
36 message_type = "https://tap.rsvp/schema/1.0#Connect",
37 initiator,
38 authorizable
39)]
40pub struct Connect {
41 #[tap(transaction_id)]
43 pub transaction_id: String,
44
45 pub agent_id: String,
47
48 #[serde(rename = "for")]
50 pub for_: String,
51
52 #[serde(skip_serializing_if = "Option::is_none")]
54 pub role: Option<String>,
55
56 #[serde(skip_serializing_if = "Option::is_none")]
58 pub constraints: Option<ConnectionConstraints>,
59}
60
61impl Connect {
62 pub fn new(transaction_id: &str, agent_id: &str, for_id: &str, role: Option<&str>) -> Self {
64 Self {
65 transaction_id: transaction_id.to_string(),
66 agent_id: agent_id.to_string(),
67 for_: for_id.to_string(),
68 role: role.map(|s| s.to_string()),
69 constraints: None,
70 }
71 }
72
73 pub fn with_constraints(mut self, constraints: ConnectionConstraints) -> Self {
75 self.constraints = Some(constraints);
76 self
77 }
78}
79
80impl Connect {
81 pub fn validate_connect(&self) -> Result<()> {
83 if self.transaction_id.is_empty() {
84 return Err(Error::Validation("transaction_id is required".to_string()));
85 }
86 if self.agent_id.is_empty() {
87 return Err(Error::Validation("agent_id is required".to_string()));
88 }
89 if self.for_.is_empty() {
90 return Err(Error::Validation("for is required".to_string()));
91 }
92 Ok(())
93 }
94
95 pub fn validate(&self) -> Result<()> {
97 self.validate_connect()
98 }
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize, TapMessage)]
103#[tap(message_type = "https://tap.rsvp/schema/1.0#OutOfBand")]
104pub struct OutOfBand {
105 pub goal_code: String,
107
108 pub goal: String,
110
111 pub service: String,
113
114 #[serde(skip_serializing_if = "Option::is_none")]
116 pub accept: Option<Vec<String>>,
117
118 #[serde(skip_serializing_if = "Option::is_none")]
120 pub handshake_protocols: Option<Vec<String>>,
121
122 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
124 pub metadata: HashMap<String, serde_json::Value>,
125}
126
127impl OutOfBand {
128 pub fn new(goal_code: String, goal: String, service: String) -> Self {
130 Self {
131 goal_code,
132 goal,
133 service,
134 accept: None,
135 handshake_protocols: None,
136 metadata: HashMap::new(),
137 }
138 }
139}
140
141#[derive(Debug, Clone, Serialize, Deserialize, TapMessage)]
143#[tap(message_type = "https://tap.rsvp/schema/1.0#AuthorizationRequired")]
144pub struct AuthorizationRequired {
145 pub url: String,
147
148 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
150 pub metadata: HashMap<String, serde_json::Value>,
151}
152
153impl AuthorizationRequired {
154 pub fn new(url: String, expires: String) -> Self {
156 let mut metadata = HashMap::new();
157 metadata.insert("expires".to_string(), serde_json::Value::String(expires));
158
159 Self { url, metadata }
160 }
161
162 pub fn add_metadata(mut self, key: &str, value: serde_json::Value) -> Self {
164 self.metadata.insert(key.to_string(), value);
165 self
166 }
167}
168
169impl OutOfBand {
170 pub fn validate_out_of_band(&self) -> Result<()> {
172 if self.goal_code.is_empty() {
173 return Err(Error::Validation("Goal code is required".to_string()));
174 }
175
176 if self.service.is_empty() {
177 return Err(Error::Validation("Service is required".to_string()));
178 }
179
180 Ok(())
181 }
182
183 pub fn validate(&self) -> Result<()> {
185 self.validate_out_of_band()
186 }
187}
188
189impl AuthorizationRequired {
190 pub fn validate_authorization_required(&self) -> Result<()> {
192 if self.url.is_empty() {
193 return Err(Error::Validation(
194 "Authorization URL is required".to_string(),
195 ));
196 }
197
198 if let Some(expires) = self.metadata.get("expires") {
200 if let Some(expires_str) = expires.as_str() {
201 if !expires_str.contains('T') || !expires_str.contains(':') {
203 return Err(Error::Validation(
204 "Invalid expiry date format. Expected ISO8601/RFC3339 format".to_string(),
205 ));
206 }
207 }
208 }
209
210 Ok(())
211 }
212
213 pub fn validate(&self) -> Result<()> {
215 self.validate_authorization_required()
216 }
217}