1use std::sync::Arc;
8
9use serde::de::{self, Deserializer, MapAccess, Visitor};
10use serde::ser::{SerializeStruct, Serializer};
11use serde::{Deserialize, Serialize};
12
13#[derive(Debug, Clone, PartialEq, Eq, Default)]
16pub struct Attachment {
17 pub id: String,
18 pub format: String,
20 pub bytes: u64,
22 pub hash: String,
24 pub source: Option<String>,
27 pub data: Option<Arc<[u8]>>,
29}
30
31impl Attachment {
32 pub fn new(data: Vec<u8>, format: &str, source: Option<String>) -> Self {
34 let hash = sha256_hex(&data);
35 Self {
36 id: format!("{}.{format}", &hash[..32]), format: format.to_string(),
38 bytes: data.len() as u64,
39 hash,
40 source,
41 data: Some(data.into()),
42 }
43 }
44
45 pub fn with_hash(hash: String, format: &str, data: Vec<u8>, source: Option<String>) -> Self {
47 Self {
48 id: format!("{}.{format}", &hash[..hash.len().min(32)]), format: format.to_string(),
50 bytes: data.len() as u64,
51 hash,
52 source,
53 data: Some(data.into()),
54 }
55 }
56
57 pub fn mime(&self) -> &'static str {
59 mime_of(&self.format)
60 }
61}
62
63pub fn mime_of(format: &str) -> &'static str {
65 match format {
66 "jpg" | "jpeg" => "image/jpeg",
67 "png" => "image/png",
68 "gif" => "image/gif",
69 "webp" => "image/webp",
70 "svg" => "image/svg+xml",
71 "bmp" => "image/bmp",
72 "avif" => "image/avif",
73 "ico" => "image/vnd.microsoft.icon",
74 "tif" | "tiff" => "image/tiff",
75 "jp2" => "image/jp2",
76 "j2k" => "image/j2c",
77 "jb2" => "image/x-jbig2",
78 "emf" => "image/emf",
79 "wmf" => "image/wmf",
80 _ => "application/octet-stream",
81 }
82}
83
84fn sha256_hex(data: &[u8]) -> String {
85 let digest = ring::digest::digest(&ring::digest::SHA256, data);
86 digest.as_ref().iter().map(|b| format!("{b:02x}")).collect()
87}
88
89struct Raw<'a>(&'a [u8]);
91
92impl Serialize for Raw<'_> {
93 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
94 s.serialize_bytes(self.0)
95 }
96}
97
98impl Serialize for Attachment {
99 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
100 let data = self.data.as_deref().filter(|_| !s.is_human_readable());
102 let fields = 4 + usize::from(self.source.is_some()) + usize::from(data.is_some());
104 let mut st = s.serialize_struct("Attachment", fields)?;
105 st.serialize_field("id", &self.id)?;
106 st.serialize_field("format", &self.format)?;
107 st.serialize_field("bytes", &self.bytes)?;
108 st.serialize_field("hash", &self.hash)?;
109 if let Some(src) = &self.source {
110 st.serialize_field("source", src)?;
111 }
112 if let Some(d) = data {
113 st.serialize_field("data", &Raw(d))?;
114 }
115 st.end()
116 }
117}
118
119impl<'de> Deserialize<'de> for Attachment {
120 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
121 d.deserialize_map(AttachmentVisitor)
122 }
123}
124
125struct AttachmentVisitor;
126
127impl<'de> Visitor<'de> for AttachmentVisitor {
128 type Value = Attachment;
129
130 fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
131 f.write_str("an attachment")
132 }
133
134 fn visit_map<A: MapAccess<'de>>(self, mut m: A) -> Result<Attachment, A::Error> {
135 let mut a = Attachment::default();
136 while let Some(key) = m.next_key::<String>()? {
137 match key.as_str() {
138 "id" => a.id = m.next_value()?,
139 "format" => a.format = m.next_value()?,
140 "bytes" => a.bytes = m.next_value()?,
141 "hash" => a.hash = m.next_value()?,
142 "source" => a.source = m.next_value()?,
143 "data" => a.data = Some(m.next_value::<Bytes>()?.0.into()),
144 _ => {
145 m.next_value::<de::IgnoredAny>()?;
146 }
147 }
148 }
149 if a.id.is_empty() {
150 return Err(de::Error::missing_field("id"));
151 }
152 Ok(a)
153 }
154}
155
156struct Bytes(Vec<u8>);
158
159impl<'de> Deserialize<'de> for Bytes {
160 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
161 struct V;
162 impl<'de> Visitor<'de> for V {
163 type Value = Bytes;
164 fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
165 f.write_str("bytes")
166 }
167 fn visit_bytes<E: de::Error>(self, v: &[u8]) -> Result<Bytes, E> {
168 Ok(Bytes(v.to_vec()))
169 }
170 fn visit_byte_buf<E: de::Error>(self, v: Vec<u8>) -> Result<Bytes, E> {
171 Ok(Bytes(v))
172 }
173 fn visit_seq<A: de::SeqAccess<'de>>(self, mut s: A) -> Result<Bytes, A::Error> {
174 let mut out = Vec::new();
175 while let Some(b) = s.next_element::<u8>()? {
176 out.push(b);
177 }
178 Ok(Bytes(out))
179 }
180 }
181 d.deserialize_bytes(V)
182 }
183}
184
185#[cfg(test)]
186mod tests {
187 use super::*;
188
189 #[test]
190 fn json_lists_metadata_only() {
191 let a = Attachment::new(
192 b"\x89PNG\r\n\x1a\nxyz".to_vec(),
193 "png",
194 Some("https://e.x/a.png".into()),
195 );
196 assert_eq!(a.id.len(), 32 + 4);
197 assert_eq!(a.bytes, 11);
198 let v = serde_json::to_value(&a).unwrap();
199 assert_eq!(v["format"], "png");
200 assert_eq!(v["hash"].as_str().unwrap().len(), 64);
201 assert!(v.get("data").is_none(), "内容不进 JSON");
202 let back: Attachment = serde_json::from_value(v).unwrap();
203 assert_eq!(back.data, None);
204 assert_eq!(back.id, a.id);
205 }
206}