xmtp_content_types/
multi_remote_attachment.rs1use std::collections::HashMap;
2
3use crate::{CodecError, ContentCodec};
4use prost::Message;
5use xmtp_proto::xmtp::mls::message_contents::{
6 ContentTypeId, EncodedContent, content_types::MultiRemoteAttachment,
7};
8
9pub struct MultiRemoteAttachmentCodec {}
10
11impl MultiRemoteAttachmentCodec {
12 const AUTHORITY_ID: &'static str = "xmtp.org";
13 pub const TYPE_ID: &'static str = "multiRemoteStaticAttachment";
14 pub const MAJOR_VERSION: u32 = 1;
15 pub const MINOR_VERSION: u32 = 0;
16}
17
18impl MultiRemoteAttachmentCodec {
19 fn fallback(_: &MultiRemoteAttachment) -> Option<String> {
20 Some(
21 "Can't display this content. This app doesn't support multiple remote attachments."
22 .to_string(),
23 )
24 }
25}
26
27impl ContentCodec<MultiRemoteAttachment> for MultiRemoteAttachmentCodec {
28 fn content_type() -> ContentTypeId {
29 ContentTypeId {
30 authority_id: MultiRemoteAttachmentCodec::AUTHORITY_ID.to_string(),
31 type_id: MultiRemoteAttachmentCodec::TYPE_ID.to_string(),
32 version_major: MultiRemoteAttachmentCodec::MAJOR_VERSION,
33 version_minor: MultiRemoteAttachmentCodec::MINOR_VERSION,
34 }
35 }
36
37 fn encode(data: MultiRemoteAttachment) -> Result<EncodedContent, CodecError> {
38 for attachment in &data.attachments {
39 crate::remote_attachment::validate_remote_attachment(attachment)
40 .map_err(|reason| CodecError::Encode(reason.into()))?;
41 }
42 let mut buf = Vec::new();
43 data.encode(&mut buf)
44 .map_err(|e| CodecError::Encode(e.to_string()))?;
45
46 Ok(EncodedContent {
47 r#type: Some(MultiRemoteAttachmentCodec::content_type()),
48 parameters: HashMap::new(),
49 fallback: Self::fallback(&data),
50 compression: None,
51 content: buf,
52 })
53 }
54
55 fn decode(content: EncodedContent) -> Result<MultiRemoteAttachment, CodecError> {
56 let mut decoded = MultiRemoteAttachment::decode(content.content.as_slice())
57 .map_err(|e| CodecError::Decode(e.to_string()))?;
58
59 for attachment in &mut decoded.attachments {
60 crate::remote_attachment::normalize_decoded_scheme(attachment);
61 crate::remote_attachment::validate_remote_attachment(attachment)
62 .map_err(|reason| CodecError::Decode(reason.into()))?;
63 }
64 Ok(decoded)
65 }
66
67 fn should_push() -> bool {
68 true
69 }
70}
71
72#[cfg(test)]
73pub(crate) mod tests {
74 use xmtp_proto::xmtp::mls::message_contents::content_types::RemoteAttachmentInfo;
75
76 use super::*;
77
78 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
79 #[cfg_attr(not(target_arch = "wasm32"), test)]
80 fn test_encode_decode() {
82 let attachment_info_1 = RemoteAttachmentInfo {
83 content_digest: "ab".repeat(32),
84 secret: vec![0; 32],
85 nonce: vec![0; 12],
86 salt: vec![0; 32],
87 scheme: "https://".to_string(),
88 url: "https://example.com/attachment".to_string(),
89 content_length: Some(1000),
90 filename: Some("attachment_1.jpg".to_string()),
91 };
92 let attachment_info_2 = RemoteAttachmentInfo {
93 content_digest: "ab".repeat(32),
94 secret: vec![0; 32],
95 nonce: vec![0; 12],
96 salt: vec![0; 32],
97 scheme: "https://".to_string(),
98 url: "https://example.com/attachment".to_string(),
99 content_length: Some(1000),
100 filename: Some("attachment_2.jpg".to_string()),
101 };
102
103 let filename_1 = attachment_info_1.filename.clone();
105 let filename_2 = attachment_info_2.filename.clone();
106
107 let new_multi_remote_attachment_data: MultiRemoteAttachment = MultiRemoteAttachment {
108 attachments: vec![attachment_info_1.clone(), attachment_info_2.clone()],
109 };
110
111 let encoded = MultiRemoteAttachmentCodec::encode(new_multi_remote_attachment_data).unwrap();
112 assert_eq!(
113 encoded.clone().r#type.unwrap().type_id,
114 "multiRemoteStaticAttachment"
115 );
116 assert!(!encoded.content.is_empty());
117
118 let decoded = MultiRemoteAttachmentCodec::decode(encoded).unwrap();
119 assert_eq!(decoded.attachments[0].filename, filename_1);
120 assert_eq!(decoded.attachments[1].filename, filename_2);
121 assert_eq!(decoded.attachments[0].content_length, Some(1000));
122 assert_eq!(decoded.attachments[1].content_length, Some(1000));
123 }
124}