wsi-dicom-annotations 0.2.1

DICOM WSI ANN, SEG, SR, and Parametric Map interoperability
Documentation
use serde::{Deserialize, Serialize};
use uuid::Uuid;

use crate::{Error, Result};

use super::code::{is_valid_dicom_uid, validate_text};

#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct TrackingIdentity {
    id: String,
    uid: String,
}

impl TrackingIdentity {
    pub fn new(id: impl Into<String>, uid: impl Into<String>) -> Result<Self> {
        let id = id.into();
        let uid = uid.into();
        validate_text("tracking ID", &id, 10_240)?;
        if !is_valid_dicom_uid(&uid) {
            return Err(Error::InvalidInput(
                "tracking UID is not a valid DICOM UID".into(),
            ));
        }
        Ok(Self { id, uid })
    }

    pub fn generated(prefix: &str, ordinal: u64) -> Result<Self> {
        if ordinal == 0
            || prefix.is_empty()
            || prefix.len() > 12
            || !prefix.bytes().all(|byte| byte.is_ascii_alphanumeric())
        {
            return Err(Error::InvalidInput(
                "tracking prefix must be 1..=12 ASCII letters or digits and ordinal must be positive"
                    .into(),
            ));
        }
        let id = format!("{prefix}-{ordinal:06}");
        let uid = format!("2.25.{}", Uuid::new_v4().as_u128());
        Self::new(id, uid)
    }

    #[must_use]
    pub fn id(&self) -> &str {
        &self.id
    }

    #[must_use]
    pub fn uid(&self) -> &str {
        &self.uid
    }
}