1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3use std::collections::HashMap;
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct Envelope {
8 pub manifest: Manifest,
9 pub metadata: Metadata,
10 pub audit: Audit,
11 #[serde(skip_serializing_if = "Option::is_none")]
12 pub manifest_jws: Option<String>, }
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct Manifest {
18 pub version: String,
19 pub id: String,
20 pub timestamp: String,
21 pub sender: Entity,
22 #[serde(skip_serializing_if = "Option::is_none")]
23 pub requester: Option<Entity>,
24 pub receiver: Vec<Entity>,
25 pub security: Security,
26 #[serde(skip_serializing_if = "Option::is_none")]
27 pub extensions: Option<Extensions>,
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
32pub struct Entity {
33 pub name: Option<String>,
34 pub id: String,
35 pub contact: Contact,
36 #[serde(skip_serializing_if = "Option::is_none")]
37 pub assertion: Option<Assertion>,
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
42#[serde(untagged)]
43pub enum Contact {
44 Email(String),
45 Point(ContactPoint),
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct ContactPoint {
51 pub system: String,
52 pub value: String,
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct Assertion {
58 pub signing_key: SigningKey,
59 pub key_reference: Option<String>,
60 pub signed_fields: Vec<String>,
61 pub signature: String,
62 #[serde(skip_serializing_if = "Option::is_none")]
63 pub expires_at: Option<String>,
64 #[serde(skip_serializing_if = "Option::is_none")]
65 pub directory_attestation: Option<DirectoryAttestation>,
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct SigningKey {
71 pub alg: String, pub public_key: String,
73 pub fingerprint: String, }
75
76#[derive(Debug, Clone, Serialize, Deserialize)]
78pub struct DirectoryAttestation {
79 pub provider: String,
80 pub attestation_signature: String,
81 pub attestation_timestamp: String,
82 pub attestation_public_key: String,
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct Security {
88 #[serde(skip_serializing_if = "Option::is_none")]
89 pub classification: Option<String>,
90 pub payload_hash: String, #[serde(skip_serializing_if = "Option::is_none")]
92 pub jws: Option<JwsReference>,
93 #[serde(skip_serializing_if = "Option::is_none")]
94 pub signature: Option<LegacySignature>,
95 #[serde(skip_serializing_if = "Option::is_none")]
96 pub encryption: Option<EncryptionInfo>,
97}
98
99#[derive(Debug, Clone, Serialize, Deserialize)]
101pub struct JwsReference {
102 pub jws_file: String,
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct LegacySignature {
108 pub alg: String,
109 pub sig: String,
110 #[serde(skip_serializing_if = "Option::is_none")]
111 pub hash: Option<String>,
112}
113
114#[derive(Debug, Clone, Serialize, Deserialize)]
116pub struct EncryptionInfo {
117 pub algorithm: String, pub ephemeral_public_key: String,
119 pub iv: String,
120 pub auth_tag: String,
121}
122
123#[derive(Debug, Clone, Serialize, Deserialize, Default)]
125pub struct Extensions {
126 #[serde(skip_serializing_if = "Option::is_none")]
127 pub custom_tags: Option<Vec<String>>,
128 #[serde(skip_serializing_if = "Option::is_none")]
129 pub consent: Option<ConsentExtension>,
130 #[serde(skip_serializing_if = "Option::is_none")]
131 pub deid: Option<DeidExtension>,
132 #[serde(flatten)]
133 pub additional: HashMap<String, Value>,
134}
135
136#[derive(Debug, Clone, Serialize, Deserialize)]
138pub struct ConsentExtension {
139 pub status: String, #[serde(skip_serializing_if = "Option::is_none")]
141 pub scope: Option<Vec<String>>,
142 #[serde(skip_serializing_if = "Option::is_none")]
143 pub method: Option<String>,
144 #[serde(skip_serializing_if = "Option::is_none")]
145 pub signed_on: Option<String>,
146}
147
148#[derive(Debug, Clone, Serialize, Deserialize)]
150pub struct DeidExtension {
151 pub keys: Vec<String>,
152}
153
154#[derive(Debug, Clone, Serialize, Deserialize)]
156pub struct Metadata {
157 pub version: String,
158 pub id: String,
159 pub timestamp: String,
160 pub patient: Patient,
161 #[serde(skip_serializing_if = "Option::is_none")]
162 pub report: Option<Report>,
163 #[serde(skip_serializing_if = "Option::is_none")]
164 pub studies: Option<Studies>,
165 #[serde(skip_serializing_if = "Option::is_none")]
166 pub extensions: Option<Extensions>,
167}
168
169#[derive(Debug, Clone, Serialize, Deserialize)]
171pub struct Patient {
172 pub id: String,
173 #[serde(skip_serializing_if = "Option::is_none")]
174 pub name: Option<HumanName>,
175 #[serde(skip_serializing_if = "Option::is_none")]
176 pub dob: Option<String>, #[serde(skip_serializing_if = "Option::is_none")]
178 pub sex: Option<String>, #[serde(skip_serializing_if = "Option::is_none")]
180 pub identifiers: Option<Vec<Identifier>>,
181 #[serde(skip_serializing_if = "Option::is_none")]
182 pub verification: Option<Verification>,
183}
184
185#[derive(Debug, Clone, Serialize, Deserialize)]
187pub struct HumanName {
188 #[serde(skip_serializing_if = "Option::is_none")]
189 pub family: Option<String>,
190 #[serde(skip_serializing_if = "Option::is_none")]
191 pub given: Option<Vec<String>>,
192 #[serde(skip_serializing_if = "Option::is_none")]
193 pub prefix: Option<String>,
194 #[serde(skip_serializing_if = "Option::is_none")]
195 pub suffix: Option<String>,
196 #[serde(skip_serializing_if = "Option::is_none")]
197 pub text: Option<String>,
198}
199
200#[derive(Debug, Clone, Serialize, Deserialize)]
202pub struct Identifier {
203 pub system: String,
204 pub value: String,
205}
206
207#[derive(Debug, Clone, Serialize, Deserialize)]
209pub struct Verification {
210 #[serde(skip_serializing_if = "Option::is_none")]
211 pub verified_by: Option<String>,
212 #[serde(skip_serializing_if = "Option::is_none")]
213 pub verified_on: Option<String>,
214}
215
216#[derive(Debug, Clone, Serialize, Deserialize)]
218pub struct Report {
219 #[serde(skip_serializing_if = "Option::is_none")]
220 pub file: Option<String>,
221}
222
223#[derive(Debug, Clone, Serialize, Deserialize, Default)]
225pub struct Studies {
226 #[serde(skip_serializing_if = "Option::is_none")]
227 pub study_description: Option<String>,
228 #[serde(skip_serializing_if = "Option::is_none")]
229 pub study_uid: Option<String>,
230 #[serde(skip_serializing_if = "Option::is_none")]
231 pub series: Option<Vec<Series>>,
232}
233
234#[derive(Debug, Clone, Serialize, Deserialize)]
236pub struct Series {
237 pub series_uid: String,
238 pub modality: String,
239 #[serde(skip_serializing_if = "Option::is_none")]
240 pub body_part: Option<String>,
241 #[serde(skip_serializing_if = "Option::is_none")]
242 pub instance_count: Option<i32>,
243}
244
245#[derive(Debug, Clone, Serialize, Deserialize)]
247pub struct Audit {
248 pub audit: Vec<AuditEntry>,
249}
250
251#[derive(Debug, Clone, Serialize, Deserialize)]
253pub struct AuditEntry {
254 pub event: String,
255 pub by: EntityRef,
256 #[serde(skip_serializing_if = "Option::is_none")]
257 pub to: Option<EntityRef>,
258 pub timestamp: String,
259 #[serde(skip_serializing_if = "Option::is_none")]
260 pub assertion: Option<Assertion>,
261}
262
263#[derive(Debug, Clone, Serialize, Deserialize)]
265pub struct EntityRef {
266 pub id: String,
267 #[serde(skip_serializing_if = "Option::is_none")]
268 pub name: Option<String>,
269}
270
271pub type Files = Vec<FileEntry>;
273
274#[derive(Debug, Clone, Serialize, Deserialize)]
276pub struct FileEntry {
277 pub file: String,
278 #[serde(skip_serializing_if = "Option::is_none")]
279 pub hash: Option<String>,
280 #[serde(skip_serializing_if = "Option::is_none")]
281 pub size_bytes: Option<i64>,
282}
283
284#[cfg(test)]
285mod tests {
286 use super::*;
287
288 #[test]
289 fn test_envelope_serialization_roundtrip() {
290 let envelope = Envelope {
291 manifest: Manifest {
292 version: "1.0".to_string(),
293 id: "test-id".to_string(),
294 timestamp: "2023-01-01T00:00:00Z".to_string(),
295 sender: Entity {
296 name: Some("Test Sender".to_string()),
297 id: "sender-1".to_string(),
298 contact: Contact::Email("sender@example.com".to_string()),
299 assertion: None,
300 },
301 requester: None,
302 receiver: vec![Entity {
303 name: Some("Test Receiver".to_string()),
304 id: "receiver-1".to_string(),
305 contact: Contact::Email("receiver@example.com".to_string()),
306 assertion: None,
307 }],
308 security: Security {
309 classification: Some("confidential".to_string()),
310 payload_hash: "sha256:abc123".to_string(),
311 jws: None,
312 signature: None,
313 encryption: None,
314 },
315 extensions: None,
316 },
317 metadata: Metadata {
318 version: "1.0".to_string(),
319 id: "test-id".to_string(),
320 timestamp: "2023-01-01T00:00:00Z".to_string(),
321 patient: Patient {
322 id: "patient-1".to_string(),
323 name: None,
324 dob: None,
325 sex: None,
326 identifiers: None,
327 verification: None,
328 },
329 report: None,
330 studies: None,
331 extensions: None,
332 },
333 audit: Audit {
334 audit: vec![AuditEntry {
335 event: "created".to_string(),
336 by: EntityRef {
337 id: "sender-1".to_string(),
338 name: Some("Test Sender".to_string()),
339 },
340 to: None,
341 timestamp: "2023-01-01T00:00:00Z".to_string(),
342 assertion: None,
343 }],
344 },
345 manifest_jws: None,
346 };
347
348 let json = serde_json::to_string_pretty(&envelope).expect("Failed to serialize");
350 assert!(!json.is_empty());
351
352 let deserialized: Envelope = serde_json::from_str(&json).expect("Failed to deserialize");
354
355 assert_eq!(envelope.manifest.id, deserialized.manifest.id);
357 assert_eq!(
358 envelope.metadata.patient.id,
359 deserialized.metadata.patient.id
360 );
361 assert_eq!(envelope.audit.audit.len(), deserialized.audit.audit.len());
362 }
363
364 #[test]
365 fn test_contact_variants() {
366 let email_contact = Contact::Email("test@example.com".to_string());
368 let json = serde_json::to_string(&email_contact).expect("Failed to serialize");
369 let deserialized: Contact = serde_json::from_str(&json).expect("Failed to deserialize");
370 matches!(deserialized, Contact::Email(_));
371
372 let point_contact = Contact::Point(ContactPoint {
374 system: "phone".to_string(),
375 value: "+1234567890".to_string(),
376 });
377 let json = serde_json::to_string(&point_contact).expect("Failed to serialize");
378 let deserialized: Contact = serde_json::from_str(&json).expect("Failed to deserialize");
379 matches!(deserialized, Contact::Point(_));
380 }
381}