Skip to main content

co_didcomm/messages/headers/
didcomm.rs

1use std::collections::HashMap;
2
3use crate::{Error, PriorClaims, Thread};
4
5/// Collection of DIDComm message specific headers, will be flattened into DIDComm plain message
6/// according to [spec](https://datatracker.ietf.org/doc/html/draft-looker-jwm-01#section-4).
7#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
8pub struct DidCommHeader {
9    pub id: String,
10
11    #[serde(skip_serializing_if = "Option::is_none")]
12    pub thid: Option<String>,
13
14    #[serde(skip_serializing_if = "Option::is_none")]
15    pub pthid: Option<String>,
16
17    #[serde(rename = "type")]
18    pub m_type: String,
19
20    #[serde(default, skip_serializing_if = "Vec::is_empty")]
21    pub to: Vec<String>,
22
23    pub from: Option<String>,
24
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub created_time: Option<u64>,
27
28    #[serde(skip_serializing_if = "Option::is_none")]
29    pub expires_time: Option<u64>,
30    /// A JWT, with sub: new DID and iss: prior DID,
31    /// with a signature from a key authorized by prior DID.
32    #[serde(skip_serializing_if = "Option::is_none")]
33    from_prior: Option<PriorClaims>,
34
35    /// Optional thread decorator.
36    #[serde(skip_serializing_if = "Option::is_none", rename = "~thread")]
37    pub thread: Option<Thread>,
38    #[serde(flatten, skip_serializing_if = "HashMap::is_empty")]
39    pub(crate) other: HashMap<String, String>,
40}
41
42impl DidCommHeader {
43    /// Constructor function with ~default values.
44    pub fn new() -> Self {
45        DidCommHeader {
46            id: DidCommHeader::gen_random_id(),
47            thid: None,
48            pthid: None,
49            m_type: "JWM".into(),
50            to: vec![String::default()],
51            from: Some(String::default()),
52            created_time: None,
53            expires_time: None,
54            from_prior: None,
55            thread: None,
56            other: HashMap::new(),
57        }
58    }
59
60    /// Generates random `id`
61    /// TODO: Should this be public?
62    pub fn gen_random_id() -> String {
63        uuid::Uuid::new_v4().to_string()
64    }
65
66    /// Returns DIDComm message URI as defined by spec:
67    /// https://identity.foundation/didcomm-messaging/spec/#didcomm-message-uris
68    pub fn get_message_uri(&self) -> String {
69        format!(
70            "didcomm://{}{}{}",
71            self.id,
72            self.thid.clone().unwrap_or_default(),
73            self.pthid.clone().unwrap_or_default(),
74        )
75    }
76
77    /// Sets new message's header `thid` and `pthid` using sender's header.
78    /// It also adds `sender_header.from` into `to` set.
79    ///
80    /// # Parameters
81    ///
82    /// * `sender_header` - ref to header we're replying
83    pub fn reply_to(&mut self, sender_header: &Self) {
84        match sender_header.thread {
85            Some(ref thread) if thread.is_implicit_reply(&sender_header.id) => {
86                let thid = sender_header.thread.as_ref().unwrap().thid.clone();
87                // Do we need this?
88                self.thread = Some(Thread::implicit_reply(&thid));
89                self.thid = Some(thid);
90            }
91            _ => {
92                self.thid = sender_header.thid.clone();
93                self.pthid = sender_header.pthid.clone();
94            }
95        };
96        self.to.push(sender_header.from.clone().unwrap_or_default());
97    }
98
99    /// Getter method for `from_prior` retrieval
100    pub fn from_prior(&self) -> Option<&PriorClaims> {
101        self.from_prior.as_ref()
102    }
103
104    /// Creates set of DIDComm related headers with the static forward type
105    pub fn forward(
106        to: Vec<String>,
107        from: Option<String>,
108        expires_time: Option<u64>,
109    ) -> Result<Self, Error> {
110        Ok(DidCommHeader {
111            to,
112            from,
113            created_time: crate::helpers::now_epoch_secs(),
114            expires_time,
115            ..DidCommHeader::new()
116        })
117    }
118}
119
120impl Default for DidCommHeader {
121    fn default() -> Self {
122        DidCommHeader::new()
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129
130    #[test]
131    fn reply_to_can_use_decorate_if_present() {
132        let _header = DidCommHeader::default();
133    }
134}