1use chrono::Utc;
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9
10use crate::didcomm::PlainMessage;
11use crate::error::{Error, Result};
12use crate::impl_tap_message;
13use crate::message::tap_message_trait::TapMessageBody;
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct ErrorBody {
18 pub code: String,
20
21 pub description: String,
23
24 #[serde(skip_serializing_if = "Option::is_none")]
26 pub original_message_id: Option<String>,
27
28 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
30 pub metadata: HashMap<String, serde_json::Value>,
31}
32
33impl ErrorBody {
34 pub fn new(code: &str, description: &str) -> Self {
36 Self {
37 code: code.to_string(),
38 description: description.to_string(),
39 original_message_id: None,
40 metadata: HashMap::new(),
41 }
42 }
43
44 pub fn with_original_message(code: &str, description: &str, original_message_id: &str) -> Self {
46 Self {
47 code: code.to_string(),
48 description: description.to_string(),
49 original_message_id: Some(original_message_id.to_string()),
50 metadata: HashMap::new(),
51 }
52 }
53
54 pub fn with_metadata(mut self, key: &str, value: serde_json::Value) -> Self {
56 self.metadata.insert(key.to_string(), value);
57 self
58 }
59}
60
61impl TapMessageBody for ErrorBody {
62 fn message_type() -> &'static str {
63 "https://tap.rsvp/schema/1.0#error"
64 }
65
66 fn validate(&self) -> Result<()> {
67 if self.code.is_empty() {
68 return Err(Error::Validation(
69 "Error code is required in ErrorBody".to_string(),
70 ));
71 }
72
73 if self.description.is_empty() {
74 return Err(Error::Validation(
75 "Error description is required in ErrorBody".to_string(),
76 ));
77 }
78
79 Ok(())
80 }
81
82 fn to_didcomm(&self, from_did: &str) -> Result<PlainMessage> {
83 let mut body_json =
85 serde_json::to_value(self).map_err(|e| Error::SerializationError(e.to_string()))?;
86
87 if let Some(body_obj) = body_json.as_object_mut() {
89 body_obj.insert(
90 "@type".to_string(),
91 serde_json::Value::String(Self::message_type().to_string()),
92 );
93 }
94
95 let id = uuid::Uuid::new_v4().to_string();
97 let created_time = Utc::now().timestamp() as u64;
98
99 let from = from_did.to_string();
101
102 let thid = self.original_message_id.clone();
104
105 let message = PlainMessage {
107 id,
108 typ: "application/didcomm-plain+json".to_string(),
109 type_: Self::message_type().to_string(),
110 from,
111 to: Vec::new(), thid,
113 pthid: None,
114 created_time: Some(created_time),
115 expires_time: None,
116 extra_headers: std::collections::HashMap::new(),
117 from_prior: None,
118 body: body_json,
119 attachments: None,
120 };
121
122 Ok(message)
123 }
124}
125
126impl_tap_message!(ErrorBody, generated_id);