dicom_gen_uid/
lib.rs

1//! # DICOM UID generator
2//! 
3//! Designed for the simple need of wanting a DICOM unique identifier.
4//! 
5//! All UIDs are produced according to [DICOM PS3.5 2023d Annex B.2][1],
6//! creating UUID derived UIDs.
7//! 
8//! [1]: https://dicom.nema.org/medical/dicom/2023c/output/chtml/part05/sect_B.2.html
9use std::io::Write;
10
11pub use uuid;
12
13use uuid::Uuid;
14
15/// Generate a UUID (v4) and derive a DICOM UID from it.
16#[inline]
17pub fn gen_uid() -> String {
18    new_uid(Uuid::new_v4())
19}
20
21/// Generate a UUID (v4),
22/// derive a DICOM UID from it,
23/// and print it to the given writer.
24#[inline]
25pub fn gen_uid_to(to: impl Write) -> Result<(), std::io::Error> {
26    new_uid_to(Uuid::new_v4(), to)
27}
28
29/// Create a DICOM UID derived from the given UUID
30#[inline]
31pub fn new_uid(uuid: Uuid) -> String {
32    format!("2.25.{}", uuid.to_u128_le())
33}
34
35/// Generate a UUID derived DICOM
36/// and print it to the given writer.
37#[inline]
38pub fn new_uid_to(uuid: Uuid, mut to: impl Write) -> Result<(), std::io::Error> {
39    write!(to, "2.25.{}", uuid.to_u128_le())
40}
41
42#[cfg(test)]
43mod tests {
44    use crate::*;
45    use uuid::Uuid;
46
47    #[test]
48    fn base_test() {
49        for _ in 0..16 {
50            let uid = gen_uid();
51            assert!(uid.starts_with("2.25."));
52            assert!(
53                uid.len() > 8,
54                "expected more than 8 characters, but UID {} is {} characters long",
55                uid,
56                uid.len()
57            );
58            assert!(
59                uid.len() <= 44,
60                "expected less than 40 characters, but UID {} is {} characters long",
61                uid,
62                uid.len()
63            );
64
65            for c in uid.chars() {
66                assert!(c == '.' || ('0'..='9').contains(&c));
67            }
68        }
69    }
70
71    #[test]
72    fn new_uid_consistency() {
73        let uuid = Uuid::new_v4();
74
75        let uid = new_uid(uuid);
76        assert!(uid.starts_with("2.25."));
77        assert!(
78            uid.len() > 8,
79            "expected more than 8 characters, but UID {} is {} characters long",
80            uid,
81            uid.len()
82        );
83        assert!(
84            uid.len() <= 44,
85            "expected less than 44 characters, but UID {} is {} characters long",
86            uid,
87            uid.len()
88        );
89
90        for c in uid.chars() {
91            assert!(c == '.' || ('0'..='9').contains(&c));
92        }
93
94        let uid2 = new_uid(uuid);
95
96        assert_eq!(
97            uid, uid2,
98            "UIDs obtained from the same UUID should be equal"
99        );
100    }
101
102    #[test]
103    fn test_write_to() {
104        let mut out = vec![];
105
106        gen_uid_to(&mut out).unwrap();
107
108        let uid = std::str::from_utf8(&out).expect("output should be valid UTF-8");
109
110        let len = out.len();
111        assert_eq!(
112            len,
113            uid.len(),
114            "byte length should be equivalent to string length"
115        );
116
117        assert!(
118            len > 8,
119            "expected more than 8 characters, but UID {} is {} characters long",
120            uid,
121            len
122        );
123        assert!(
124            uid.len() <= 44,
125            "expected less than 44 characters, but UID {} is {} characters long",
126            uid,
127            uid.len()
128        );
129
130        for c in &out {
131            assert!(*c == b'.' || (b'0'..=b'9').contains(c));
132        }
133
134        gen_uid_to(&mut out).unwrap();
135        assert!(out.len() > len);
136    }
137}