Skip to main content

wsi_dicom_annotations/annotations/model/
tracking.rs

1use 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}