Skip to main content

xmtp_content_types/
markdown.rs

1use std::collections::HashMap;
2
3use xmtp_proto::xmtp::mls::message_contents::{ContentTypeId, EncodedContent};
4
5use super::{CodecError, ContentCodec};
6
7pub struct MarkdownCodec {}
8
9impl MarkdownCodec {
10    const AUTHORITY_ID: &'static str = "xmtp.org";
11    pub const TYPE_ID: &'static str = "markdown";
12    const ENCODING_KEY: &'static str = "encoding";
13    const ENCODING_UTF8: &'static str = "UTF-8";
14    pub const MAJOR_VERSION: u32 = 1;
15    pub const MINOR_VERSION: u32 = 0;
16}
17
18impl ContentCodec<String> for MarkdownCodec {
19    fn content_type() -> ContentTypeId {
20        ContentTypeId {
21            authority_id: MarkdownCodec::AUTHORITY_ID.to_string(),
22            type_id: MarkdownCodec::TYPE_ID.to_string(),
23            version_major: MarkdownCodec::MAJOR_VERSION,
24            version_minor: MarkdownCodec::MINOR_VERSION,
25        }
26    }
27
28    fn encode(markdown: String) -> Result<EncodedContent, CodecError> {
29        Ok(EncodedContent {
30            r#type: Some(MarkdownCodec::content_type()),
31            parameters: HashMap::from([(
32                MarkdownCodec::ENCODING_KEY.to_string(),
33                MarkdownCodec::ENCODING_UTF8.to_string(),
34            )]),
35            fallback: None,
36            compression: None,
37            content: markdown.into_bytes(),
38        })
39    }
40
41    // implements: CTYPE-019
42    fn decode(content: EncodedContent) -> Result<String, CodecError> {
43        let encoding = content
44            .parameters
45            .get(MarkdownCodec::ENCODING_KEY)
46            .map_or(MarkdownCodec::ENCODING_UTF8, String::as_str);
47        if encoding != MarkdownCodec::ENCODING_UTF8 {
48            return Err(CodecError::Decode(format!(
49                "Unsupported text encoding {encoding}"
50            )));
51        }
52        let markdown = std::str::from_utf8(&content.content)
53            .map_err(|utf8_err| CodecError::Decode(utf8_err.to_string()))?;
54        Ok(markdown.to_string())
55    }
56
57    fn should_push() -> bool {
58        true
59    }
60}
61
62#[cfg(test)]
63pub(crate) mod tests {
64    use crate::{ContentCodec, markdown::MarkdownCodec};
65
66    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
67    #[cfg_attr(not(target_arch = "wasm32"), test)]
68    // verifies: CTYPE-007, CTYPE-021
69    fn can_encode_and_decode_markdown() {
70        let markdown = "# Hello, world!";
71        let encoded_content =
72            MarkdownCodec::encode(markdown.to_string()).expect("Should encode successfully");
73        let decoded_content =
74            MarkdownCodec::decode(encoded_content).expect("Should decode successfully");
75        assert!(decoded_content == markdown);
76    }
77}