Skip to main content

dicom_toolkit_data/
dataset.rs

1//! DICOM dataset — an ordered map of `Tag → Element`.
2//!
3//! Ports DCMTK's `DcmDataset` / `DcmItem`. Elements are kept in ascending
4//! tag order, matching the DICOM requirement for encoded files.
5
6use crate::element::Element;
7use crate::value::Value;
8use dicom_toolkit_core::error::{DcmError, DcmResult};
9use dicom_toolkit_dict::{Tag, Vr};
10use indexmap::IndexMap;
11
12/// A DICOM dataset: an ordered collection of data elements.
13///
14/// Internally backed by an `IndexMap` that is kept sorted by tag.
15#[derive(Debug, Clone, Default, PartialEq)]
16pub struct DataSet {
17    elements: IndexMap<Tag, Element>,
18}
19
20impl DataSet {
21    pub fn new() -> Self {
22        Self {
23            elements: IndexMap::new(),
24        }
25    }
26
27    // ── Core map operations ───────────────────────────────────────────────────
28
29    /// Insert an element, maintaining ascending tag order.
30    pub fn insert(&mut self, element: Element) {
31        self.elements.insert(element.tag, element);
32        self.elements.sort_unstable_keys();
33    }
34
35    pub fn get(&self, tag: Tag) -> Option<&Element> {
36        self.elements.get(&tag)
37    }
38
39    pub fn get_mut(&mut self, tag: Tag) -> Option<&mut Element> {
40        self.elements.get_mut(&tag)
41    }
42
43    pub fn remove(&mut self, tag: Tag) -> Option<Element> {
44        self.elements.swap_remove(&tag)
45    }
46
47    pub fn contains(&self, tag: Tag) -> bool {
48        self.elements.contains_key(&tag)
49    }
50
51    pub fn len(&self) -> usize {
52        self.elements.len()
53    }
54
55    pub fn is_empty(&self) -> bool {
56        self.elements.is_empty()
57    }
58
59    pub fn iter(&self) -> impl Iterator<Item = (&Tag, &Element)> {
60        self.elements.iter()
61    }
62
63    pub fn tags(&self) -> impl Iterator<Item = Tag> + '_ {
64        self.elements.keys().copied()
65    }
66
67    /// Return the element for `tag`, or a [`DcmError::UnknownTag`] if absent.
68    pub fn find_element(&self, tag: Tag) -> DcmResult<&Element> {
69        self.elements.get(&tag).ok_or(DcmError::UnknownTag {
70            group: tag.group,
71            element: tag.element,
72        })
73    }
74
75    // ── Convenience getters ───────────────────────────────────────────────────
76
77    pub fn get_string(&self, tag: Tag) -> Option<&str> {
78        self.get(tag)?.string_value()
79    }
80
81    pub fn get_strings(&self, tag: Tag) -> Option<&[String]> {
82        self.get(tag)?.strings_value()
83    }
84
85    pub fn get_u16(&self, tag: Tag) -> Option<u16> {
86        self.get(tag)?.u16_value()
87    }
88
89    pub fn get_u32(&self, tag: Tag) -> Option<u32> {
90        self.get(tag)?.u32_value()
91    }
92
93    pub fn get_i32(&self, tag: Tag) -> Option<i32> {
94        self.get(tag)?.i32_value()
95    }
96
97    pub fn get_f64(&self, tag: Tag) -> Option<f64> {
98        self.get(tag)?.f64_value()
99    }
100
101    pub fn get_bytes(&self, tag: Tag) -> Option<&[u8]> {
102        self.get(tag)?.bytes_value()
103    }
104
105    pub fn get_items(&self, tag: Tag) -> Option<&[DataSet]> {
106        self.get(tag)?.items()
107    }
108
109    // ── Convenience setters ───────────────────────────────────────────────────
110
111    pub fn set_string(&mut self, tag: Tag, vr: Vr, value: &str) {
112        self.insert(Element::string(tag, vr, value));
113    }
114
115    pub fn set_strings(&mut self, tag: Tag, vr: Vr, values: Vec<String>) {
116        self.insert(Element::new(tag, vr, Value::Strings(values)));
117    }
118
119    pub fn set_u16(&mut self, tag: Tag, value: u16) {
120        self.insert(Element::u16(tag, value));
121    }
122
123    pub fn set_u32(&mut self, tag: Tag, value: u32) {
124        self.insert(Element::u32(tag, value));
125    }
126
127    pub fn set_i32(&mut self, tag: Tag, value: i32) {
128        self.insert(Element::i32(tag, value));
129    }
130
131    pub fn set_f64(&mut self, tag: Tag, value: f64) {
132        self.insert(Element::f64(tag, value));
133    }
134
135    pub fn set_bytes(&mut self, tag: Tag, vr: Vr, data: Vec<u8>) {
136        self.insert(Element::bytes(tag, vr, data));
137    }
138
139    pub fn set_sequence(&mut self, tag: Tag, items: Vec<DataSet>) {
140        self.insert(Element::sequence(tag, items));
141    }
142
143    pub fn set_uid(&mut self, tag: Tag, uid: &str) {
144        self.insert(Element::uid(tag, uid));
145    }
146}
147
148// ── Tests ─────────────────────────────────────────────────────────────────────
149
150#[cfg(test)]
151mod tests {
152    use super::*;
153    use dicom_toolkit_dict::tags;
154
155    #[test]
156    fn dataset_insert_and_get() {
157        let mut ds = DataSet::new();
158        ds.set_u16(tags::ROWS, 512);
159        assert_eq!(ds.get_u16(tags::ROWS), Some(512));
160    }
161
162    #[test]
163    fn dataset_contains_remove() {
164        let mut ds = DataSet::new();
165        ds.set_string(tags::PATIENT_NAME, Vr::PN, "Smith^John");
166        assert!(ds.contains(tags::PATIENT_NAME));
167        let removed = ds.remove(tags::PATIENT_NAME).unwrap();
168        assert_eq!(removed.string_value(), Some("Smith^John"));
169        assert!(!ds.contains(tags::PATIENT_NAME));
170    }
171
172    #[test]
173    fn dataset_len_is_empty() {
174        let mut ds = DataSet::new();
175        assert!(ds.is_empty());
176        assert_eq!(ds.len(), 0);
177        ds.set_u16(tags::ROWS, 1);
178        assert!(!ds.is_empty());
179        assert_eq!(ds.len(), 1);
180    }
181
182    #[test]
183    fn dataset_tag_order_ascending() {
184        // Insert in reverse order; tags() should return in ascending order.
185        let mut ds = DataSet::new();
186        ds.set_u16(tags::COLUMNS, 256); // (0028,0011)
187        ds.set_u16(tags::ROWS, 512); // (0028,0010)
188        ds.set_string(tags::PATIENT_NAME, Vr::PN, "Doe^Jane"); // (0010,0010)
189
190        let tags: Vec<Tag> = ds.tags().collect();
191        assert!(
192            tags.windows(2).all(|w| w[0] < w[1]),
193            "tags not in order: {:?}",
194            tags
195        );
196    }
197
198    #[test]
199    fn dataset_convenience_getters() {
200        let mut ds = DataSet::new();
201        ds.set_string(tags::PATIENT_ID, Vr::LO, "PID001");
202        ds.set_strings(
203            tags::IMAGE_TYPE,
204            Vr::CS,
205            vec!["ORIGINAL".into(), "PRIMARY".into()],
206        );
207        ds.set_u16(tags::ROWS, 512);
208        ds.set_u32(Tag::new(0x0028, 0x0000), 42);
209        ds.set_i32(Tag::new(0x0020, 0x0013), -1);
210        ds.set_f64(Tag::new(0x0028, 0x1050), 1024.0);
211        ds.set_uid(tags::SOP_CLASS_UID, "1.2.840.10008.1.1");
212
213        assert_eq!(ds.get_string(tags::PATIENT_ID), Some("PID001"));
214        assert_eq!(ds.get_strings(tags::IMAGE_TYPE).unwrap().len(), 2);
215        assert_eq!(ds.get_u16(tags::ROWS), Some(512));
216        assert_eq!(ds.get_u32(Tag::new(0x0028, 0x0000)), Some(42));
217        assert_eq!(ds.get_i32(Tag::new(0x0020, 0x0013)), Some(-1));
218        assert!((ds.get_f64(Tag::new(0x0028, 0x1050)).unwrap() - 1024.0).abs() < 1e-9);
219        assert_eq!(
220            ds.get_string(tags::SOP_CLASS_UID),
221            Some("1.2.840.10008.1.1")
222        );
223    }
224
225    #[test]
226    fn dataset_set_bytes() {
227        let mut ds = DataSet::new();
228        let data = vec![0u8, 1, 2, 3];
229        ds.set_bytes(Tag::new(0x0042, 0x0011), Vr::OB, data.clone());
230        assert_eq!(
231            ds.get_bytes(Tag::new(0x0042, 0x0011)),
232            Some(data.as_slice())
233        );
234    }
235
236    #[test]
237    fn dataset_nested_sequence() {
238        let mut item = DataSet::new();
239        item.set_string(tags::PATIENT_NAME, Vr::PN, "Jones^Bob");
240
241        let mut ds = DataSet::new();
242        ds.set_sequence(Tag::new(0x0008, 0x1115), vec![item]);
243
244        let items = ds.get_items(Tag::new(0x0008, 0x1115)).unwrap();
245        assert_eq!(items.len(), 1);
246        assert_eq!(items[0].get_string(tags::PATIENT_NAME), Some("Jones^Bob"));
247    }
248
249    #[test]
250    fn dataset_find_element_ok() {
251        let mut ds = DataSet::new();
252        ds.set_u16(tags::ROWS, 512);
253        assert!(ds.find_element(tags::ROWS).is_ok());
254    }
255
256    #[test]
257    fn dataset_find_element_not_found() {
258        let ds = DataSet::new();
259        let err = ds.find_element(tags::ROWS).unwrap_err();
260        // Should be UnknownTag
261        assert!(matches!(err, DcmError::UnknownTag { .. }));
262    }
263
264    #[test]
265    fn dataset_iter() {
266        let mut ds = DataSet::new();
267        ds.set_u16(tags::ROWS, 512);
268        ds.set_u16(tags::COLUMNS, 256);
269        let count = ds.iter().count();
270        assert_eq!(count, 2);
271    }
272
273    #[test]
274    fn dataset_overwrite() {
275        let mut ds = DataSet::new();
276        ds.set_u16(tags::ROWS, 512);
277        ds.set_u16(tags::ROWS, 1024);
278        assert_eq!(ds.len(), 1);
279        assert_eq!(ds.get_u16(tags::ROWS), Some(1024));
280    }
281}