wsi_dicom_annotations/annotations/model/
tracking.rs1use serde::{Deserialize, Serialize};
2use uuid::Uuid;
3
4use crate::{Error, Result};
5
6use super::code::{is_valid_dicom_uid, validate_text};
7
8#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
9#[serde(deny_unknown_fields)]
10pub struct TrackingIdentity {
11 id: String,
12 uid: String,
13}
14
15impl TrackingIdentity {
16 pub fn new(id: impl Into<String>, uid: impl Into<String>) -> Result<Self> {
17 let id = id.into();
18 let uid = uid.into();
19 validate_text("tracking ID", &id, 10_240)?;
20 if !is_valid_dicom_uid(&uid) {
21 return Err(Error::InvalidInput(
22 "tracking UID is not a valid DICOM UID".into(),
23 ));
24 }
25 Ok(Self { id, uid })
26 }
27
28 pub fn generated(prefix: &str, ordinal: u64) -> Result<Self> {
29 if ordinal == 0
30 || prefix.is_empty()
31 || prefix.len() > 12
32 || !prefix.bytes().all(|byte| byte.is_ascii_alphanumeric())
33 {
34 return Err(Error::InvalidInput(
35 "tracking prefix must be 1..=12 ASCII letters or digits and ordinal must be positive"
36 .into(),
37 ));
38 }
39 let id = format!("{prefix}-{ordinal:06}");
40 let uid = format!("2.25.{}", Uuid::new_v4().as_u128());
41 Self::new(id, uid)
42 }
43
44 #[must_use]
45 pub fn id(&self) -> &str {
46 &self.id
47 }
48
49 #[must_use]
50 pub fn uid(&self) -> &str {
51 &self.uid
52 }
53}