1use js_int::UInt;
6use ruma_common::OwnedMxcUri;
7use ruma_macros::EventContent;
8use serde::{Deserialize, Serialize};
9
10use super::{
11 message::TextContentBlock,
12 room::{EncryptedFile, EncryptedFileHashes, EncryptedFileInfo, message::Relation},
13};
14
15#[derive(Clone, Debug, Serialize, Deserialize, EventContent)]
23#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
24#[ruma_event(type = "org.matrix.msc1767.file", kind = MessageLike, without_relation)]
25pub struct FileEventContent {
26 #[serde(rename = "org.matrix.msc1767.text")]
28 pub text: TextContentBlock,
29
30 #[serde(rename = "org.matrix.msc1767.file")]
32 pub file: FileContentBlock,
33
34 #[serde(rename = "org.matrix.msc1767.caption", skip_serializing_if = "Option::is_none")]
36 pub caption: Option<CaptionContentBlock>,
37
38 #[cfg(feature = "unstable-msc3955")]
40 #[serde(
41 default,
42 skip_serializing_if = "ruma_common::serde::is_default",
43 rename = "org.matrix.msc1767.automated"
44 )]
45 pub automated: bool,
46
47 #[serde(
49 flatten,
50 skip_serializing_if = "Option::is_none",
51 deserialize_with = "crate::room::message::relation_serde::deserialize_relation"
52 )]
53 pub relates_to: Option<Relation<FileEventContentWithoutRelation>>,
54}
55
56impl FileEventContent {
57 pub fn plain(text: TextContentBlock, url: OwnedMxcUri, name: String) -> Self {
60 Self {
61 text,
62 file: FileContentBlock::plain(url, name),
63 caption: None,
64 #[cfg(feature = "unstable-msc3955")]
65 automated: false,
66 relates_to: None,
67 }
68 }
69
70 pub fn plain_with_plain_text(
73 plain_text: impl Into<String>,
74 url: OwnedMxcUri,
75 name: String,
76 ) -> Self {
77 Self {
78 text: TextContentBlock::plain(plain_text),
79 file: FileContentBlock::plain(url, name),
80 caption: None,
81 #[cfg(feature = "unstable-msc3955")]
82 automated: false,
83 relates_to: None,
84 }
85 }
86
87 pub fn encrypted(
90 text: TextContentBlock,
91 url: OwnedMxcUri,
92 name: String,
93 encryption_info: EncryptedContent,
94 ) -> Self {
95 Self {
96 text,
97 file: FileContentBlock::encrypted(url, name, encryption_info),
98 caption: None,
99 #[cfg(feature = "unstable-msc3955")]
100 automated: false,
101 relates_to: None,
102 }
103 }
104
105 pub fn encrypted_with_plain_text(
108 plain_text: impl Into<String>,
109 url: OwnedMxcUri,
110 name: String,
111 encryption_info: EncryptedContent,
112 ) -> Self {
113 Self {
114 text: TextContentBlock::plain(plain_text),
115 file: FileContentBlock::encrypted(url, name, encryption_info),
116 caption: None,
117 #[cfg(feature = "unstable-msc3955")]
118 automated: false,
119 relates_to: None,
120 }
121 }
122}
123
124#[derive(Clone, Debug, Serialize, Deserialize)]
126#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
127pub struct FileContentBlock {
128 pub url: OwnedMxcUri,
130
131 pub name: String,
133
134 #[serde(skip_serializing_if = "Option::is_none")]
136 pub mimetype: Option<String>,
137
138 #[serde(skip_serializing_if = "Option::is_none")]
140 pub size: Option<UInt>,
141
142 #[serde(flatten, skip_serializing_if = "Option::is_none")]
146 pub encryption_info: Option<Box<EncryptedContent>>,
147}
148
149impl FileContentBlock {
150 pub fn plain(url: OwnedMxcUri, name: String) -> Self {
152 Self { url, name, mimetype: None, size: None, encryption_info: None }
153 }
154
155 pub fn encrypted(url: OwnedMxcUri, name: String, encryption_info: EncryptedContent) -> Self {
157 Self {
158 url,
159 name,
160 mimetype: None,
161 size: None,
162 encryption_info: Some(Box::new(encryption_info)),
163 }
164 }
165
166 pub fn is_encrypted(&self) -> bool {
168 self.encryption_info.is_some()
169 }
170}
171
172#[derive(Clone, Debug, Deserialize, Serialize)]
174#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
175pub struct EncryptedContent {
176 #[serde(flatten)]
178 pub info: EncryptedFileInfo,
179
180 pub hashes: EncryptedFileHashes,
184}
185
186impl EncryptedContent {
187 pub fn new(info: EncryptedFileInfo, hashes: EncryptedFileHashes) -> Self {
189 Self { info, hashes }
190 }
191}
192
193impl From<&EncryptedFile> for EncryptedContent {
194 fn from(encrypted: &EncryptedFile) -> Self {
195 let EncryptedFile { info, hashes, .. } = encrypted;
196 Self { info: info.to_owned(), hashes: hashes.to_owned() }
197 }
198}
199
200#[derive(Clone, Debug, Serialize, Deserialize)]
207#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
208pub struct CaptionContentBlock {
209 #[serde(rename = "org.matrix.msc1767.text")]
211 pub text: TextContentBlock,
212}
213
214impl CaptionContentBlock {
215 pub fn plain(body: impl Into<String>) -> Self {
217 Self { text: TextContentBlock::plain(body) }
218 }
219
220 pub fn html(body: impl Into<String>, html_body: impl Into<String>) -> Self {
222 Self { text: TextContentBlock::html(body, html_body) }
223 }
224
225 #[cfg(feature = "markdown")]
230 pub fn markdown(body: impl AsRef<str> + Into<String>) -> Self {
231 Self { text: TextContentBlock::markdown(body) }
232 }
233}
234
235impl From<TextContentBlock> for CaptionContentBlock {
236 fn from(text: TextContentBlock) -> Self {
237 Self { text }
238 }
239}