1use dicom_core::ops::{
40 ApplyOp, AttributeAction, AttributeOp, AttributeSelector, AttributeSelectorStep,
41};
42use dicom_encoding::Codec;
43use dicom_parser::dataset::read::{DataSetReaderOptions, OddLengthStrategy};
44use dicom_parser::dataset::write::DataSetWriterOptions;
45use dicom_parser::stateful::decode::CharacterSetOverride;
46use itertools::Itertools;
47use smallvec::SmallVec;
48use snafu::{ensure, OptionExt, ResultExt};
49use std::borrow::Cow;
50use std::fs::File;
51use std::io::{BufRead, BufReader, Read};
52use std::path::Path;
53use std::{collections::BTreeMap, io::Write};
54
55use crate::file::ReadPreamble;
56use crate::ops::{
57 ApplyError, ApplyResult, IncompatibleTypesSnafu, ModifySnafu, UnsupportedActionSnafu,
58};
59use crate::{meta::FileMetaTable, FileMetaTableBuilder};
60use crate::{
61 AccessByNameError, AccessError, AtAccessError, BuildMetaTableSnafu, CreateParserSnafu,
62 CreatePrinterSnafu, DicomObject, ElementNotFoundSnafu, FileDicomObject, InvalidGroupSnafu,
63 MissingElementValueSnafu, MissingLeafElementSnafu, NoSpaceSnafu, NoSuchAttributeNameSnafu,
64 NoSuchDataElementAliasSnafu, NoSuchDataElementTagSnafu, NotASequenceSnafu, OpenFileSnafu,
65 ParseMetaDataSetSnafu, ParseSopAttributeSnafu, PrematureEndSnafu, PrepareMetaTableSnafu,
66 PrintDataSetSnafu, PrivateCreatorNotFoundSnafu, PrivateElementError, ReadError, ReadFileSnafu,
67 ReadPreambleBytesSnafu, ReadTokenSnafu, ReadUnrecognizedTransferSyntaxSnafu,
68 ReadUnsupportedTransferSyntaxSnafu, ReadUnsupportedTransferSyntaxWithSuggestionSnafu,
69 UnexpectedTokenSnafu, WithMetaError, WriteError,
70};
71use dicom_core::dictionary::{DataDictionary, DataDictionaryEntry};
72use dicom_core::header::{GroupNumber, HasLength, Header};
73use dicom_core::value::{DataSetSequence, PixelFragmentSequence, Value, ValueType, C};
74use dicom_core::{DataElement, Length, PrimitiveValue, Tag, VR};
75use dicom_dictionary_std::{tags, uids, StandardDataDictionary};
76use dicom_encoding::transfer_syntax::TransferSyntaxIndex;
77use dicom_encoding::{encode::EncodeTo, text::SpecificCharacterSet, TransferSyntax};
78use dicom_parser::dataset::{DataSetReader, DataToken, IntoTokensOptions};
79use dicom_parser::{
80 dataset::{read::Error as ParserError, DataSetWriter, IntoTokens},
81 StatefulDecode,
82};
83use dicom_transfer_syntax_registry::TransferSyntaxRegistry;
84
85pub type InMemElement<D = StandardDataDictionary> = DataElement<InMemDicomObject<D>, InMemFragment>;
87
88pub type InMemFragment = dicom_core::value::InMemFragment;
90
91type Result<T, E = AccessError> = std::result::Result<T, E>;
92
93type ParserResult<T> = std::result::Result<T, ParserError>;
94
95#[derive(Debug, Clone)]
100pub struct InMemDicomObject<D = StandardDataDictionary> {
101 entries: BTreeMap<Tag, InMemElement<D>>,
103 dict: D,
105 len: Length,
109 pub(crate) charset_changed: bool,
113}
114
115impl<D> PartialEq for InMemDicomObject<D> {
116 fn eq(&self, other: &Self) -> bool {
118 self.entries == other.entries
119 }
120}
121
122impl<D> HasLength for InMemDicomObject<D> {
123 fn length(&self) -> Length {
124 self.len
125 }
126}
127
128impl<D> HasLength for &InMemDicomObject<D> {
129 fn length(&self) -> Length {
130 self.len
131 }
132}
133
134impl<D> DicomObject for InMemDicomObject<D>
135where
136 D: DataDictionary,
137 D: Clone,
138{
139 type Attribute<'a>
140 = &'a Value<InMemDicomObject<D>, InMemFragment>
141 where
142 Self: 'a;
143
144 type LeafAttribute<'a>
145 = &'a Value<InMemDicomObject<D>, InMemFragment>
146 where
147 Self: 'a;
148
149 #[inline]
150 fn attr_opt(&self, tag: Tag) -> Result<Option<Self::Attribute<'_>>> {
151 let elem = InMemDicomObject::element_opt(self, tag)?;
152 Ok(elem.map(|e| e.value()))
153 }
154
155 #[inline]
156 fn attr_by_name_opt(
157 &self,
158 name: &str,
159 ) -> Result<Option<Self::Attribute<'_>>, AccessByNameError> {
160 let elem = InMemDicomObject::element_by_name_opt(self, name)?;
161 Ok(elem.map(|e| e.value()))
162 }
163
164 #[inline]
165 fn attr(&self, tag: Tag) -> Result<Self::Attribute<'_>> {
166 let elem = InMemDicomObject::element(self, tag)?;
167 Ok(elem.value())
168 }
169
170 #[inline]
171 fn attr_by_name(&self, name: &str) -> Result<Self::Attribute<'_>, AccessByNameError> {
172 let elem = InMemDicomObject::element_by_name(self, name)?;
173 Ok(elem.value())
174 }
175
176 #[inline]
177 fn at(
178 &self,
179 selector: impl Into<AttributeSelector>,
180 ) -> Result<Self::LeafAttribute<'_>, AtAccessError> {
181 self.value_at(selector)
182 }
183}
184
185impl<'s, D: 's> DicomObject for &'s InMemDicomObject<D>
186where
187 D: DataDictionary,
188 D: Clone,
189{
190 type Attribute<'a>
191 = &'a Value<InMemDicomObject<D>, InMemFragment>
192 where
193 Self: 'a,
194 's: 'a;
195
196 type LeafAttribute<'a>
197 = &'a Value<InMemDicomObject<D>, InMemFragment>
198 where
199 Self: 'a,
200 's: 'a;
201
202 #[inline]
203 fn attr_opt(&self, tag: Tag) -> Result<Option<Self::Attribute<'_>>> {
204 let elem = InMemDicomObject::element_opt(*self, tag)?;
205 Ok(elem.map(|e| e.value()))
206 }
207
208 #[inline]
209 fn attr_by_name_opt(
210 &self,
211 name: &str,
212 ) -> Result<Option<Self::Attribute<'_>>, AccessByNameError> {
213 let elem = InMemDicomObject::element_by_name_opt(*self, name)?;
214 Ok(elem.map(|e| e.value()))
215 }
216
217 #[inline]
218 fn attr(&self, tag: Tag) -> Result<Self::Attribute<'_>> {
219 let elem = InMemDicomObject::element(*self, tag)?;
220 Ok(elem.value())
221 }
222
223 #[inline]
224 fn attr_by_name(&self, name: &str) -> Result<Self::Attribute<'_>, AccessByNameError> {
225 let elem = InMemDicomObject::element_by_name(*self, name)?;
226 Ok(elem.value())
227 }
228
229 #[inline]
230 fn at(
231 &self,
232 selector: impl Into<AttributeSelector>,
233 ) -> Result<Self::LeafAttribute<'_>, AtAccessError> {
234 self.value_at(selector)
235 }
236}
237
238impl FileDicomObject<InMemDicomObject<StandardDataDictionary>> {
239 pub fn open_file<P: AsRef<Path>>(path: P) -> Result<Self, ReadError> {
247 Self::open_file_with_dict(path, StandardDataDictionary)
248 }
249
250 pub fn from_reader<S>(src: S) -> Result<Self, ReadError>
258 where
259 S: Read,
260 {
261 Self::from_reader_with_dict(src, StandardDataDictionary)
262 }
263}
264
265impl InMemDicomObject<StandardDataDictionary> {
266 pub fn new_empty() -> Self {
268 InMemDicomObject {
269 entries: BTreeMap::new(),
270 dict: StandardDataDictionary,
271 len: Length::UNDEFINED,
272 charset_changed: false,
273 }
274 }
275
276 #[inline]
278 pub fn from_element_source<I>(iter: I) -> Result<Self>
279 where
280 I: IntoIterator<Item = Result<InMemElement<StandardDataDictionary>>>,
281 {
282 Self::from_element_source_with_dict(iter, StandardDataDictionary)
283 }
284
285 #[inline]
287 pub fn from_element_iter<I>(iter: I) -> Self
288 where
289 I: IntoIterator<Item = InMemElement<StandardDataDictionary>>,
290 {
291 Self::from_iter_with_dict(iter, StandardDataDictionary)
292 }
293
294 #[inline]
301 pub fn command_from_element_iter<I>(iter: I) -> Self
302 where
303 I: IntoIterator<Item = InMemElement<StandardDataDictionary>>,
304 {
305 Self::command_from_iter_with_dict(iter, StandardDataDictionary)
306 }
307
308 #[inline]
316 pub fn read_dataset<S>(decoder: S) -> Result<Self, ReadError>
317 where
318 S: StatefulDecode,
319 {
320 Self::read_dataset_with_dict(decoder, StandardDataDictionary)
321 }
322
323 #[inline]
329 pub fn read_dataset_with_ts_cs<S>(
330 from: S,
331 ts: &TransferSyntax,
332 cs: SpecificCharacterSet,
333 ) -> Result<Self, ReadError>
334 where
335 S: Read,
336 {
337 Self::read_dataset_with_dict_ts_cs(from, StandardDataDictionary, ts, cs)
338 }
339
340 #[inline]
347 pub fn read_dataset_with_ts<S>(from: S, ts: &TransferSyntax) -> Result<Self, ReadError>
348 where
349 S: Read,
350 {
351 Self::read_dataset_with_dict_ts_cs(
352 from,
353 StandardDataDictionary,
354 ts,
355 SpecificCharacterSet::default(),
356 )
357 }
358}
359
360impl<D> FileDicomObject<InMemDicomObject<D>>
361where
362 D: DataDictionary,
363 D: Clone,
364{
365 pub fn new_empty_with_dict_and_meta(dict: D, meta: FileMetaTable) -> Self {
368 FileDicomObject {
369 meta,
370 obj: InMemDicomObject {
371 entries: BTreeMap::new(),
372 dict,
373 len: Length::UNDEFINED,
374 charset_changed: false,
375 },
376 }
377 }
378
379 pub fn open_file_with_dict<P: AsRef<Path>>(path: P, dict: D) -> Result<Self, ReadError> {
387 Self::open_file_with(path, dict, TransferSyntaxRegistry)
388 }
389
390 pub fn open_file_with<P, R>(path: P, dict: D, ts_index: R) -> Result<Self, ReadError>
404 where
405 P: AsRef<Path>,
406 R: TransferSyntaxIndex,
407 {
408 Self::open_file_with_all_options(
409 path,
410 dict,
411 ts_index,
412 None,
413 ReadPreamble::Auto,
414 Default::default(),
415 Default::default(),
416 )
417 }
418
419 pub(crate) fn open_file_with_all_options<P, R>(
420 path: P,
421 dict: D,
422 ts_index: R,
423 read_until: Option<Tag>,
424 mut read_preamble: ReadPreamble,
425 odd_length: OddLengthStrategy,
426 charset_override: CharacterSetOverride,
427 ) -> Result<Self, ReadError>
428 where
429 P: AsRef<Path>,
430 R: TransferSyntaxIndex,
431 {
432 let path = path.as_ref();
433 let mut file =
434 BufReader::new(File::open(path).with_context(|_| OpenFileSnafu { filename: path })?);
435
436 if read_preamble == ReadPreamble::Auto {
437 read_preamble = Self::detect_preamble(&mut file)
438 .with_context(|_| ReadFileSnafu { filename: path })?;
439 }
440
441 if read_preamble == ReadPreamble::Auto || read_preamble == ReadPreamble::Always {
442 let mut buf = [0u8; 128];
443 file.read_exact(&mut buf)
445 .with_context(|_| ReadFileSnafu { filename: path })?;
446 }
447
448 Self::read_parts_with_all_options_impl(
449 file,
450 dict,
451 ts_index,
452 read_until,
453 odd_length,
454 charset_override,
455 )
456 }
457
458 pub fn from_reader_with_dict<S>(src: S, dict: D) -> Result<Self, ReadError>
466 where
467 S: Read,
468 {
469 Self::from_reader_with(src, dict, TransferSyntaxRegistry)
470 }
471
472 pub fn from_reader_with<S, R>(src: S, dict: D, ts_index: R) -> Result<Self, ReadError>
486 where
487 S: Read,
488 R: TransferSyntaxIndex,
489 {
490 Self::from_reader_with_all_options(
491 src,
492 dict,
493 ts_index,
494 None,
495 ReadPreamble::Auto,
496 Default::default(),
497 Default::default(),
498 )
499 }
500
501 pub(crate) fn from_reader_with_all_options<S, R>(
502 src: S,
503 dict: D,
504 ts_index: R,
505 read_until: Option<Tag>,
506 mut read_preamble: ReadPreamble,
507 odd_length: OddLengthStrategy,
508 charset_override: CharacterSetOverride,
509 ) -> Result<Self, ReadError>
510 where
511 S: Read,
512 R: TransferSyntaxIndex,
513 {
514 let mut file = BufReader::new(src);
515
516 if read_preamble == ReadPreamble::Auto {
517 read_preamble = Self::detect_preamble(&mut file).context(ReadPreambleBytesSnafu)?;
518 }
519
520 if read_preamble == ReadPreamble::Always {
521 let mut buf = [0u8; 128];
523 file.read_exact(&mut buf).context(ReadPreambleBytesSnafu)?;
525 }
526
527 Self::read_parts_with_all_options_impl(
528 file,
529 dict,
530 ts_index,
531 read_until,
532 odd_length,
533 charset_override,
534 )
535 }
536
537 fn detect_preamble<S>(reader: &mut BufReader<S>) -> std::io::Result<ReadPreamble>
540 where
541 S: Read,
542 {
543 let buf = reader.fill_buf()?;
544 let buflen = buf.len();
545
546 if buflen < 4 {
547 return Err(std::io::ErrorKind::UnexpectedEof.into());
548 }
549
550 if buflen >= 132 && &buf[128..132] == b"DICM" {
551 return Ok(ReadPreamble::Always);
552 }
553
554 if &buf[0..4] == b"DICM" {
555 return Ok(ReadPreamble::Never);
556 }
557
558 Ok(ReadPreamble::Auto)
560 }
561
562 fn read_parts_with_all_options_impl<S, R>(
570 mut src: BufReader<S>,
571 dict: D,
572 ts_index: R,
573 read_until: Option<Tag>,
574 odd_length: OddLengthStrategy,
575 charset_override: CharacterSetOverride,
576 ) -> Result<Self, ReadError>
577 where
578 S: Read,
579 R: TransferSyntaxIndex,
580 {
581 let mut meta = FileMetaTable::from_reader(&mut src).context(ParseMetaDataSetSnafu)?;
583
584 let ts_uid = meta.transfer_syntax();
585 if let Some(ts) = ts_index.get(ts_uid) {
587 let mut options = DataSetReaderOptions::default();
588 options.odd_length = odd_length;
589 options.charset_override = charset_override;
590
591 let obj = match ts.codec() {
592 Codec::Dataset(Some(adapter)) => {
593 let adapter = adapter.adapt_reader(Box::new(src));
594 let mut dataset = DataSetReader::new_with_ts_options(adapter, ts, options)
595 .context(CreateParserSnafu)?;
596 InMemDicomObject::build_object(
597 &mut dataset,
598 dict,
599 false,
600 Length::UNDEFINED,
601 read_until,
602 )?
603 }
604 Codec::Dataset(None) => {
605 if ts_uid == uids::DEFLATED_EXPLICIT_VR_LITTLE_ENDIAN
606 || ts_uid == uids::JPIP_REFERENCED_DEFLATE
607 || ts_uid == uids::JPIPHTJ2K_REFERENCED_DEFLATE
608 {
609 return ReadUnsupportedTransferSyntaxWithSuggestionSnafu {
610 uid: ts.uid(),
611 name: ts.name(),
612 feature_name: "dicom-transfer-syntax-registry/deflate",
613 }
614 .fail();
615 }
616
617 return ReadUnsupportedTransferSyntaxSnafu {
618 uid: ts.uid(),
619 name: ts.name(),
620 }
621 .fail();
622 }
623 Codec::None | Codec::EncapsulatedPixelData(..) => {
624 let mut dataset = DataSetReader::new_with_ts_options(src, ts, options)
625 .context(CreateParserSnafu)?;
626 InMemDicomObject::build_object(
627 &mut dataset,
628 dict,
629 false,
630 Length::UNDEFINED,
631 read_until,
632 )?
633 }
634 };
635
636 if meta.media_storage_sop_class_uid().is_empty() {
638 if let Some(elem) = obj.get(tags::SOP_CLASS_UID) {
639 meta.media_storage_sop_class_uid = elem
640 .value()
641 .to_str()
642 .context(ParseSopAttributeSnafu)?
643 .to_string();
644 }
645 }
646
647 if meta.media_storage_sop_instance_uid().is_empty() {
649 if let Some(elem) = obj.get(tags::SOP_INSTANCE_UID) {
650 meta.media_storage_sop_instance_uid = elem
651 .value()
652 .to_str()
653 .context(ParseSopAttributeSnafu)?
654 .to_string();
655 }
656 }
657
658 Ok(FileDicomObject { meta, obj })
659 } else {
660 ReadUnrecognizedTransferSyntaxSnafu {
661 uid: ts_uid.to_string(),
662 }
663 .fail()
664 }
665 }
666}
667
668impl FileDicomObject<InMemDicomObject<StandardDataDictionary>> {
669 pub fn new_empty_with_meta(meta: FileMetaTable) -> Self {
671 FileDicomObject {
672 meta,
673 obj: InMemDicomObject {
674 entries: BTreeMap::new(),
675 dict: StandardDataDictionary,
676 len: Length::UNDEFINED,
677 charset_changed: false,
678 },
679 }
680 }
681}
682
683impl<D> InMemDicomObject<D>
684where
685 D: DataDictionary,
686 D: Clone,
687{
688 pub fn new_empty_with_dict(dict: D) -> Self {
690 InMemDicomObject {
691 entries: BTreeMap::new(),
692 dict,
693 len: Length::UNDEFINED,
694 charset_changed: false,
695 }
696 }
697
698 pub fn from_element_source_with_dict<I>(iter: I, dict: D) -> Result<Self>
700 where
701 I: IntoIterator<Item = Result<InMemElement<D>>>,
702 {
703 let entries: Result<_> = iter.into_iter().map_ok(|e| (e.tag(), e)).collect();
704 Ok(InMemDicomObject {
705 entries: entries?,
706 dict,
707 len: Length::UNDEFINED,
708 charset_changed: false,
709 })
710 }
711
712 pub fn from_iter_with_dict<I>(iter: I, dict: D) -> Self
714 where
715 I: IntoIterator<Item = InMemElement<D>>,
716 {
717 let entries = iter.into_iter().map(|e| (e.tag(), e)).collect();
718 InMemDicomObject {
719 entries,
720 dict,
721 len: Length::UNDEFINED,
722 charset_changed: false,
723 }
724 }
725
726 pub fn command_from_iter_with_dict<I>(iter: I, dict: D) -> Self
733 where
734 I: IntoIterator<Item = InMemElement<D>>,
735 {
736 let mut calculated_length: u32 = 0;
737 let mut entries: BTreeMap<_, _> = iter
738 .into_iter()
739 .map(|e| {
740 if e.tag().0 == 0x0000 && e.tag().1 != 0x0000 {
742 let l = e.value().length();
743 calculated_length += if l.is_defined() { even_len(l.0) } else { 0 } + 8;
744 }
745
746 (e.tag(), e)
747 })
748 .collect();
749
750 entries.insert(
751 Tag(0, 0),
752 InMemElement::new(Tag(0, 0), VR::UL, PrimitiveValue::from(calculated_length)),
753 );
754
755 InMemDicomObject {
756 entries,
757 dict,
758 len: Length::UNDEFINED,
759 charset_changed: false,
760 }
761 }
762
763 pub fn read_dataset_with_dict<S>(decoder: S, dict: D) -> Result<Self, ReadError>
767 where
768 S: StatefulDecode,
769 D: DataDictionary,
770 {
771 let mut dataset = DataSetReader::new(decoder, Default::default());
772 InMemDicomObject::build_object(&mut dataset, dict, false, Length::UNDEFINED, None)
773 }
774
775 #[inline]
778 pub fn read_dataset_with_dict_ts<S>(
779 from: S,
780 dict: D,
781 ts: &TransferSyntax,
782 ) -> Result<Self, ReadError>
783 where
784 S: Read,
785 D: DataDictionary,
786 {
787 Self::read_dataset_with_dict_ts_cs(from, dict, ts, SpecificCharacterSet::default())
788 }
789
790 pub fn read_dataset_with_dict_ts_cs<S>(
798 from: S,
799 dict: D,
800 ts: &TransferSyntax,
801 cs: SpecificCharacterSet,
802 ) -> Result<Self, ReadError>
803 where
804 S: Read,
805 D: DataDictionary,
806 {
807 let from = BufReader::new(from);
808
809 match ts.codec() {
810 Codec::Dataset(Some(adapter)) => {
811 let adapter = adapter.adapt_reader(Box::new(from));
812 let mut dataset =
813 DataSetReader::new_with_ts_cs(adapter, ts, cs).context(CreateParserSnafu)?;
814 InMemDicomObject::build_object(&mut dataset, dict, false, Length::UNDEFINED, None)
815 }
816 Codec::Dataset(None) => {
817 let uid = ts.uid();
818 if uid == uids::DEFLATED_EXPLICIT_VR_LITTLE_ENDIAN
819 || uid == uids::JPIP_REFERENCED_DEFLATE
820 || uid == uids::JPIPHTJ2K_REFERENCED_DEFLATE
821 {
822 return ReadUnsupportedTransferSyntaxWithSuggestionSnafu {
823 uid,
824 name: ts.name(),
825 feature_name: "dicom-transfer-syntax-registry/deflate",
826 }
827 .fail();
828 }
829
830 ReadUnsupportedTransferSyntaxSnafu {
831 uid,
832 name: ts.name(),
833 }
834 .fail()
835 }
836 Codec::None | Codec::EncapsulatedPixelData(..) => {
837 let mut dataset =
838 DataSetReader::new_with_ts_cs(from, ts, cs).context(CreateParserSnafu)?;
839 InMemDicomObject::build_object(&mut dataset, dict, false, Length::UNDEFINED, None)
840 }
841 }
842 }
843
844 pub fn element(&self, tag: Tag) -> Result<&InMemElement<D>> {
854 self.entries
855 .get(&tag)
856 .context(NoSuchDataElementTagSnafu { tag })
857 }
858
859 pub fn element_by_name(&self, name: &str) -> Result<&InMemElement<D>, AccessByNameError> {
871 let tag = self.lookup_name(name)?;
872 self.entries
873 .get(&tag)
874 .with_context(|| NoSuchDataElementAliasSnafu {
875 tag,
876 alias: name.to_string(),
877 })
878 }
879
880 pub fn element_opt(&self, tag: Tag) -> Result<Option<&InMemElement<D>>, AccessError> {
885 match self.element(tag) {
886 Ok(e) => Ok(Some(e)),
887 Err(super::AccessError::NoSuchDataElementTag { .. }) => Ok(None),
888 }
889 }
890
891 pub fn get(&self, tag: Tag) -> Option<&InMemElement<D>> {
896 self.entries.get(&tag)
897 }
898
899 fn get_mut(&mut self, tag: Tag) -> Option<&mut InMemElement<D>> {
904 self.entries.get_mut(&tag)
905 }
906
907 pub fn element_by_name_opt(
918 &self,
919 name: &str,
920 ) -> Result<Option<&InMemElement<D>>, AccessByNameError> {
921 match self.element_by_name(name) {
922 Ok(e) => Ok(Some(e)),
923 Err(AccessByNameError::NoSuchDataElementAlias { .. }) => Ok(None),
924 Err(e) => Err(e),
925 }
926 }
927
928 fn find_private_creator(&self, group: GroupNumber, creator: &str) -> Option<&Tag> {
929 let range = Tag(group, 0)..Tag(group, 0xFF);
930 for (tag, elem) in self.entries.range(range) {
931 if elem.header().vr() == VR::LO && elem.to_str().unwrap_or_default() == creator {
934 return Some(tag);
935 }
936 }
937 None
938 }
939
940 pub fn private_element(
973 &self,
974 group: GroupNumber,
975 creator: &str,
976 element: u8,
977 ) -> Result<&InMemElement<D>, PrivateElementError> {
978 let tag = self.find_private_creator(group, creator).ok_or_else(|| {
979 PrivateCreatorNotFoundSnafu {
980 group,
981 creator: creator.to_string(),
982 }
983 .build()
984 })?;
985
986 let element_num = (tag.element() << 8) | (element as u16);
987 self.get(Tag(group, element_num)).ok_or_else(|| {
988 ElementNotFoundSnafu {
989 group,
990 creator: creator.to_string(),
991 elem: element,
992 }
993 .build()
994 })
995 }
996
997 pub fn put(&mut self, elt: InMemElement<D>) -> Option<InMemElement<D>> {
1002 self.put_element(elt)
1003 }
1004
1005 pub fn put_element(&mut self, elt: InMemElement<D>) -> Option<InMemElement<D>> {
1010 self.len = Length::UNDEFINED;
1011 self.invalidate_if_charset_changed(elt.tag());
1012 self.entries.insert(elt.tag(), elt)
1013 }
1014
1015 pub fn put_private_element(
1056 &mut self,
1057 group: GroupNumber,
1058 creator: &str,
1059 element: u8,
1060 vr: VR,
1061 value: PrimitiveValue,
1062 ) -> Result<Option<InMemElement<D>>, PrivateElementError> {
1063 ensure!(group % 2 == 1, InvalidGroupSnafu { group });
1064 let private_creator = self.find_private_creator(group, creator);
1065 if let Some(tag) = private_creator {
1066 let tag = Tag(group, (tag.element() << 8) | element as u16);
1068 Ok(self.put_element(DataElement::new(tag, vr, value)))
1069 } else {
1070 let range = Tag(group, 0)..Tag(group, 0xFF);
1072 let last_entry = self.entries.range(range).next_back();
1073 let next_available = match last_entry {
1074 Some((tag, _)) => tag.element() + 1,
1075 None => 0x01,
1076 };
1077 if next_available < 0xFF {
1078 let tag = Tag(group, next_available);
1080 self.put_str(tag, VR::LO, creator);
1081
1082 let tag = Tag(group, (next_available << 8) | element as u16);
1084 Ok(self.put_element(DataElement::new(tag, vr, value)))
1085 } else {
1086 NoSpaceSnafu { group }.fail()
1087 }
1088 }
1089 }
1090
1091 pub fn put_str(
1094 &mut self,
1095 tag: Tag,
1096 vr: VR,
1097 string: impl Into<String>,
1098 ) -> Option<InMemElement<D>> {
1099 self.put_element(DataElement::new(tag, vr, string.into()))
1100 }
1101
1102 pub fn remove_element(&mut self, tag: Tag) -> bool {
1105 if self.entries.remove(&tag).is_some() {
1106 self.len = Length::UNDEFINED;
1107 true
1108 } else {
1109 false
1110 }
1111 }
1112
1113 pub fn remove_element_by_name(&mut self, name: &str) -> Result<bool, AccessByNameError> {
1116 let tag = self.lookup_name(name)?;
1117 Ok(self.entries.remove(&tag).is_some()).map(|removed| {
1118 if removed {
1119 self.len = Length::UNDEFINED;
1120 }
1121 removed
1122 })
1123 }
1124
1125 pub fn take_element(&mut self, tag: Tag) -> Result<InMemElement<D>> {
1127 self.entries
1128 .remove(&tag)
1129 .map(|e| {
1130 self.len = Length::UNDEFINED;
1131 e
1132 })
1133 .context(NoSuchDataElementTagSnafu { tag })
1134 }
1135
1136 pub fn take(&mut self, tag: Tag) -> Option<InMemElement<D>> {
1140 self.entries.remove(&tag).map(|e| {
1141 self.len = Length::UNDEFINED;
1142 e
1143 })
1144 }
1145
1146 pub fn take_element_by_name(
1148 &mut self,
1149 name: &str,
1150 ) -> Result<InMemElement<D>, AccessByNameError> {
1151 let tag = self.lookup_name(name)?;
1152 self.entries
1153 .remove(&tag)
1154 .map(|e| {
1155 self.len = Length::UNDEFINED;
1156 e
1157 })
1158 .with_context(|| NoSuchDataElementAliasSnafu {
1159 tag,
1160 alias: name.to_string(),
1161 })
1162 }
1163
1164 pub fn retain(&mut self, mut f: impl FnMut(&InMemElement<D>) -> bool) {
1170 self.entries.retain(|_, elem| f(elem));
1171 self.len = Length::UNDEFINED;
1172 }
1173
1174 pub fn update_value(
1202 &mut self,
1203 tag: Tag,
1204 f: impl FnMut(&mut Value<InMemDicomObject<D>, InMemFragment>),
1205 ) -> bool {
1206 self.invalidate_if_charset_changed(tag);
1207 if let Some(e) = self.entries.get_mut(&tag) {
1208 e.update_value(f);
1209 self.len = Length::UNDEFINED;
1210 true
1211 } else {
1212 false
1213 }
1214 }
1215
1216 pub fn update_value_at(
1266 &mut self,
1267 selector: impl Into<AttributeSelector>,
1268 f: impl FnMut(&mut Value<InMemDicomObject<D>, InMemFragment>),
1269 ) -> Result<(), AtAccessError> {
1270 self.entry_at_mut(selector)
1271 .map(|e| e.update_value(f))
1272 .map(|_| {
1273 self.len = Length::UNDEFINED;
1274 })
1275 }
1276
1277 pub fn value_at(
1304 &self,
1305 selector: impl Into<AttributeSelector>,
1306 ) -> Result<&Value<InMemDicomObject<D>, InMemFragment>, AtAccessError> {
1307 let selector: AttributeSelector = selector.into();
1308
1309 let mut obj = self;
1310 for (i, step) in selector.iter().enumerate() {
1311 match step {
1312 AttributeSelectorStep::Tag(tag) => {
1314 return obj.get(*tag).map(|e| e.value()).with_context(|| {
1315 MissingLeafElementSnafu {
1316 selector: selector.clone(),
1317 }
1318 });
1319 }
1320 AttributeSelectorStep::Nested { tag, item } => {
1322 let e = obj
1323 .entries
1324 .get(tag)
1325 .with_context(|| crate::MissingSequenceSnafu {
1326 selector: selector.clone(),
1327 step_index: i as u32,
1328 })?;
1329
1330 let items = e.items().with_context(|| NotASequenceSnafu {
1332 selector: selector.clone(),
1333 step_index: i as u32,
1334 })?;
1335
1336 obj =
1338 items
1339 .get(*item as usize)
1340 .with_context(|| crate::MissingSequenceSnafu {
1341 selector: selector.clone(),
1342 step_index: i as u32,
1343 })?;
1344 }
1345 }
1346 }
1347
1348 unreachable!()
1349 }
1350
1351 pub fn convert_to_utf8(&mut self) {
1353 self.put(DataElement::new(
1354 tags::SPECIFIC_CHARACTER_SET,
1355 VR::CS,
1356 "ISO_IR 192",
1357 ));
1358 }
1359
1360 pub fn entry_at(
1369 &self,
1370 selector: impl Into<AttributeSelector>,
1371 ) -> Result<&InMemElement<D>, AtAccessError> {
1372 let selector: AttributeSelector = selector.into();
1373
1374 let mut obj = self;
1375 for (i, step) in selector.iter().enumerate() {
1376 match step {
1377 AttributeSelectorStep::Tag(tag) => {
1379 return obj.get(*tag).with_context(|| MissingLeafElementSnafu {
1380 selector: selector.clone(),
1381 })
1382 }
1383 AttributeSelectorStep::Nested { tag, item } => {
1385 let e = obj
1386 .entries
1387 .get(tag)
1388 .with_context(|| crate::MissingSequenceSnafu {
1389 selector: selector.clone(),
1390 step_index: i as u32,
1391 })?;
1392
1393 let items = e.items().with_context(|| NotASequenceSnafu {
1395 selector: selector.clone(),
1396 step_index: i as u32,
1397 })?;
1398
1399 obj =
1401 items
1402 .get(*item as usize)
1403 .with_context(|| crate::MissingSequenceSnafu {
1404 selector: selector.clone(),
1405 step_index: i as u32,
1406 })?;
1407 }
1408 }
1409 }
1410
1411 unreachable!()
1412 }
1413
1414 fn entry_at_mut(
1418 &mut self,
1419 selector: impl Into<AttributeSelector>,
1420 ) -> Result<&mut InMemElement<D>, AtAccessError> {
1421 let selector: AttributeSelector = selector.into();
1422
1423 let mut obj = self;
1424 for (i, step) in selector.iter().enumerate() {
1425 match step {
1426 AttributeSelectorStep::Tag(tag) => {
1428 return obj.get_mut(*tag).with_context(|| MissingLeafElementSnafu {
1429 selector: selector.clone(),
1430 })
1431 }
1432 AttributeSelectorStep::Nested { tag, item } => {
1434 let e =
1435 obj.entries
1436 .get_mut(tag)
1437 .with_context(|| crate::MissingSequenceSnafu {
1438 selector: selector.clone(),
1439 step_index: i as u32,
1440 })?;
1441
1442 let items = e.items_mut().with_context(|| NotASequenceSnafu {
1444 selector: selector.clone(),
1445 step_index: i as u32,
1446 })?;
1447
1448 obj = items.get_mut(*item as usize).with_context(|| {
1450 crate::MissingSequenceSnafu {
1451 selector: selector.clone(),
1452 step_index: i as u32,
1453 }
1454 })?;
1455 }
1456 }
1457 }
1458
1459 unreachable!()
1460 }
1461
1462 fn apply(&mut self, op: AttributeOp) -> ApplyResult {
1502 let AttributeOp { selector, action } = op;
1503 let dict = self.dict.clone();
1504
1505 let mut obj = self;
1506 for (i, step) in selector.iter().enumerate() {
1507 match step {
1508 AttributeSelectorStep::Tag(tag) => return obj.apply_leaf(*tag, action),
1510 AttributeSelectorStep::Nested { tag, item } => {
1512 if !obj.entries.contains_key(tag) {
1513 if action.is_constructive() {
1515 let vr = dict
1516 .by_tag(*tag)
1517 .and_then(|entry| entry.vr().exact())
1518 .unwrap_or(VR::UN);
1519
1520 if vr != VR::SQ && vr != VR::UN {
1521 return Err(ApplyError::NotASequence {
1522 selector: selector.clone(),
1523 step_index: i as u32,
1524 });
1525 }
1526
1527 obj.put(DataElement::new(*tag, vr, DataSetSequence::empty()));
1528 } else {
1529 return Err(ApplyError::MissingSequence {
1530 selector: selector.clone(),
1531 step_index: i as u32,
1532 });
1533 }
1534 };
1535
1536 let items = obj
1538 .entries
1539 .get_mut(tag)
1540 .expect("sequence element should exist at this point")
1541 .items_mut()
1542 .ok_or_else(|| ApplyError::NotASequence {
1543 selector: selector.clone(),
1544 step_index: i as u32,
1545 })?;
1546
1547 obj = if items.len() == *item as usize && action.is_constructive() {
1549 items.push(InMemDicomObject::new_empty_with_dict(dict.clone()));
1550 items.last_mut().unwrap()
1551 } else {
1552 items.get_mut(*item as usize).ok_or_else(|| {
1553 ApplyError::MissingSequence {
1554 selector: selector.clone(),
1555 step_index: i as u32,
1556 }
1557 })?
1558 };
1559 }
1560 }
1561 }
1562 unreachable!()
1563 }
1564
1565 fn apply_leaf(&mut self, tag: Tag, action: AttributeAction) -> ApplyResult {
1566 self.invalidate_if_charset_changed(tag);
1567 match action {
1568 AttributeAction::Remove => {
1569 self.remove_element(tag);
1570 Ok(())
1571 }
1572 AttributeAction::Empty => {
1573 if let Some(e) = self.entries.get_mut(&tag) {
1574 let vr = e.vr();
1575 *e = DataElement::empty(tag, vr);
1577 self.len = Length::UNDEFINED;
1578 }
1579 Ok(())
1580 }
1581 AttributeAction::SetVr(new_vr) => {
1582 if let Some(e) = self.entries.remove(&tag) {
1583 let (header, value) = e.into_parts();
1584 let e = DataElement::new(header.tag, new_vr, value);
1585 self.put(e);
1586 } else {
1587 self.put(DataElement::empty(tag, new_vr));
1588 }
1589 Ok(())
1590 }
1591 AttributeAction::Set(new_value) => {
1592 self.apply_change_value_impl(tag, new_value);
1593 Ok(())
1594 }
1595 AttributeAction::SetStr(string) => {
1596 let new_value = PrimitiveValue::from(&*string);
1597 self.apply_change_value_impl(tag, new_value);
1598 Ok(())
1599 }
1600 AttributeAction::SetIfMissing(new_value) => {
1601 if self.get(tag).is_none() {
1602 self.apply_change_value_impl(tag, new_value);
1603 }
1604 Ok(())
1605 }
1606 AttributeAction::SetStrIfMissing(string) => {
1607 if self.get(tag).is_none() {
1608 let new_value = PrimitiveValue::from(&*string);
1609 self.apply_change_value_impl(tag, new_value);
1610 }
1611 Ok(())
1612 }
1613 AttributeAction::Replace(new_value) => {
1614 if self.get(tag).is_some() {
1615 self.apply_change_value_impl(tag, new_value);
1616 }
1617 Ok(())
1618 }
1619 AttributeAction::ReplaceStr(string) => {
1620 if self.get(tag).is_some() {
1621 let new_value = PrimitiveValue::from(&*string);
1622 self.apply_change_value_impl(tag, new_value);
1623 }
1624 Ok(())
1625 }
1626 AttributeAction::PushStr(string) => self.apply_push_str_impl(tag, string),
1627 AttributeAction::PushI32(integer) => self.apply_push_i32_impl(tag, integer),
1628 AttributeAction::PushU32(integer) => self.apply_push_u32_impl(tag, integer),
1629 AttributeAction::PushI16(integer) => self.apply_push_i16_impl(tag, integer),
1630 AttributeAction::PushU16(integer) => self.apply_push_u16_impl(tag, integer),
1631 AttributeAction::PushF32(number) => self.apply_push_f32_impl(tag, number),
1632 AttributeAction::PushF64(number) => self.apply_push_f64_impl(tag, number),
1633 AttributeAction::Truncate(limit) => {
1634 self.update_value(tag, |value| value.truncate(limit));
1635 Ok(())
1636 }
1637 _ => UnsupportedActionSnafu.fail(),
1638 }
1639 }
1640
1641 fn apply_change_value_impl(&mut self, tag: Tag, new_value: PrimitiveValue) {
1642 self.invalidate_if_charset_changed(tag);
1643
1644 if let Some(e) = self.entries.get_mut(&tag) {
1645 let vr = e.vr();
1646 let new_value = if vr == VR::SQ && new_value.is_empty() {
1649 DataSetSequence::empty().into()
1650 } else {
1651 Value::from(new_value)
1652 };
1653 *e = DataElement::new(tag, vr, new_value);
1654 self.len = Length::UNDEFINED;
1655 } else {
1656 let vr = dicom_dictionary_std::StandardDataDictionary
1658 .by_tag(tag)
1659 .and_then(|entry| entry.vr().exact())
1660 .unwrap_or(VR::UN);
1661 let new_value = if vr == VR::SQ && new_value.is_empty() {
1666 DataSetSequence::empty().into()
1667 } else {
1668 Value::from(new_value)
1669 };
1670
1671 self.put(DataElement::new(tag, vr, new_value));
1672 }
1673 }
1674
1675 fn invalidate_if_charset_changed(&mut self, tag: Tag) {
1676 if tag == tags::SPECIFIC_CHARACTER_SET {
1677 self.charset_changed = true;
1678 }
1679 }
1680
1681 fn apply_push_str_impl(&mut self, tag: Tag, string: Cow<'static, str>) -> ApplyResult {
1682 if let Some(e) = self.entries.remove(&tag) {
1683 let (header, value) = e.into_parts();
1684 match value {
1685 Value::Primitive(mut v) => {
1686 self.invalidate_if_charset_changed(tag);
1687 v.extend_str([string]).context(ModifySnafu)?;
1689 self.put(DataElement::new(tag, header.vr, v));
1691 Ok(())
1692 }
1693
1694 Value::PixelSequence(..) => IncompatibleTypesSnafu {
1695 kind: ValueType::PixelSequence,
1696 }
1697 .fail(),
1698 Value::Sequence(..) => IncompatibleTypesSnafu {
1699 kind: ValueType::DataSetSequence,
1700 }
1701 .fail(),
1702 }
1703 } else {
1704 let vr = dicom_dictionary_std::StandardDataDictionary
1706 .by_tag(tag)
1707 .and_then(|entry| entry.vr().exact())
1708 .unwrap_or(VR::UN);
1709 self.put(DataElement::new(tag, vr, PrimitiveValue::from(&*string)));
1711 Ok(())
1712 }
1713 }
1714
1715 fn apply_push_i32_impl(&mut self, tag: Tag, integer: i32) -> ApplyResult {
1716 if let Some(e) = self.entries.remove(&tag) {
1717 let (header, value) = e.into_parts();
1718 match value {
1719 Value::Primitive(mut v) => {
1720 v.extend_i32([integer]).context(ModifySnafu)?;
1722 self.put(DataElement::new(tag, header.vr, v));
1724 Ok(())
1725 }
1726
1727 Value::PixelSequence(..) => IncompatibleTypesSnafu {
1728 kind: ValueType::PixelSequence,
1729 }
1730 .fail(),
1731 Value::Sequence(..) => IncompatibleTypesSnafu {
1732 kind: ValueType::DataSetSequence,
1733 }
1734 .fail(),
1735 }
1736 } else {
1737 let vr = dicom_dictionary_std::StandardDataDictionary
1739 .by_tag(tag)
1740 .and_then(|entry| entry.vr().exact())
1741 .unwrap_or(VR::SL);
1742 self.put(DataElement::new(tag, vr, PrimitiveValue::from(integer)));
1744 Ok(())
1745 }
1746 }
1747
1748 fn apply_push_u32_impl(&mut self, tag: Tag, integer: u32) -> ApplyResult {
1749 if let Some(e) = self.entries.remove(&tag) {
1750 let (header, value) = e.into_parts();
1751 match value {
1752 Value::Primitive(mut v) => {
1753 v.extend_u32([integer]).context(ModifySnafu)?;
1755 self.put(DataElement::new(tag, header.vr, v));
1757 Ok(())
1758 }
1759
1760 Value::PixelSequence(..) => IncompatibleTypesSnafu {
1761 kind: ValueType::PixelSequence,
1762 }
1763 .fail(),
1764 Value::Sequence(..) => IncompatibleTypesSnafu {
1765 kind: ValueType::DataSetSequence,
1766 }
1767 .fail(),
1768 }
1769 } else {
1770 let vr = dicom_dictionary_std::StandardDataDictionary
1772 .by_tag(tag)
1773 .and_then(|entry| entry.vr().exact())
1774 .unwrap_or(VR::UL);
1775 self.put(DataElement::new(tag, vr, PrimitiveValue::from(integer)));
1777 Ok(())
1778 }
1779 }
1780
1781 fn apply_push_i16_impl(&mut self, tag: Tag, integer: i16) -> ApplyResult {
1782 if let Some(e) = self.entries.remove(&tag) {
1783 let (header, value) = e.into_parts();
1784 match value {
1785 Value::Primitive(mut v) => {
1786 v.extend_i16([integer]).context(ModifySnafu)?;
1788 self.put(DataElement::new(tag, header.vr, v));
1790 Ok(())
1791 }
1792
1793 Value::PixelSequence(..) => IncompatibleTypesSnafu {
1794 kind: ValueType::PixelSequence,
1795 }
1796 .fail(),
1797 Value::Sequence(..) => IncompatibleTypesSnafu {
1798 kind: ValueType::DataSetSequence,
1799 }
1800 .fail(),
1801 }
1802 } else {
1803 let vr = dicom_dictionary_std::StandardDataDictionary
1805 .by_tag(tag)
1806 .and_then(|entry| entry.vr().exact())
1807 .unwrap_or(VR::SS);
1808 self.put(DataElement::new(tag, vr, PrimitiveValue::from(integer)));
1810 Ok(())
1811 }
1812 }
1813
1814 fn apply_push_u16_impl(&mut self, tag: Tag, integer: u16) -> ApplyResult {
1815 if let Some(e) = self.entries.remove(&tag) {
1816 let (header, value) = e.into_parts();
1817 match value {
1818 Value::Primitive(mut v) => {
1819 v.extend_u16([integer]).context(ModifySnafu)?;
1821 self.put(DataElement::new(tag, header.vr, v));
1823 Ok(())
1824 }
1825
1826 Value::PixelSequence(..) => IncompatibleTypesSnafu {
1827 kind: ValueType::PixelSequence,
1828 }
1829 .fail(),
1830 Value::Sequence(..) => IncompatibleTypesSnafu {
1831 kind: ValueType::DataSetSequence,
1832 }
1833 .fail(),
1834 }
1835 } else {
1836 let vr = dicom_dictionary_std::StandardDataDictionary
1838 .by_tag(tag)
1839 .and_then(|entry| entry.vr().exact())
1840 .unwrap_or(VR::US);
1841 self.put(DataElement::new(tag, vr, PrimitiveValue::from(integer)));
1843 Ok(())
1844 }
1845 }
1846
1847 fn apply_push_f32_impl(&mut self, tag: Tag, number: f32) -> ApplyResult {
1848 if let Some(e) = self.entries.remove(&tag) {
1849 let (header, value) = e.into_parts();
1850 match value {
1851 Value::Primitive(mut v) => {
1852 v.extend_f32([number]).context(ModifySnafu)?;
1854 self.put(DataElement::new(tag, header.vr, v));
1856 Ok(())
1857 }
1858
1859 Value::PixelSequence(..) => IncompatibleTypesSnafu {
1860 kind: ValueType::PixelSequence,
1861 }
1862 .fail(),
1863 Value::Sequence(..) => IncompatibleTypesSnafu {
1864 kind: ValueType::DataSetSequence,
1865 }
1866 .fail(),
1867 }
1868 } else {
1869 let vr = dicom_dictionary_std::StandardDataDictionary
1871 .by_tag(tag)
1872 .and_then(|entry| entry.vr().exact())
1873 .unwrap_or(VR::FL);
1874 self.put(DataElement::new(tag, vr, PrimitiveValue::from(number)));
1876 Ok(())
1877 }
1878 }
1879
1880 fn apply_push_f64_impl(&mut self, tag: Tag, number: f64) -> ApplyResult {
1881 if let Some(e) = self.entries.remove(&tag) {
1882 let (header, value) = e.into_parts();
1883 match value {
1884 Value::Primitive(mut v) => {
1885 v.extend_f64([number]).context(ModifySnafu)?;
1887 self.put(DataElement::new(tag, header.vr, v));
1889 Ok(())
1890 }
1891
1892 Value::PixelSequence(..) => IncompatibleTypesSnafu {
1893 kind: ValueType::PixelSequence,
1894 }
1895 .fail(),
1896 Value::Sequence(..) => IncompatibleTypesSnafu {
1897 kind: ValueType::DataSetSequence,
1898 }
1899 .fail(),
1900 }
1901 } else {
1902 let vr = dicom_dictionary_std::StandardDataDictionary
1904 .by_tag(tag)
1905 .and_then(|entry| entry.vr().exact())
1906 .unwrap_or(VR::FD);
1907 self.put(DataElement::new(tag, vr, PrimitiveValue::from(number)));
1909 Ok(())
1910 }
1911 }
1912
1913 pub fn write_dataset<W, E>(&self, to: W, encoder: E) -> Result<(), WriteError>
1931 where
1932 W: Write,
1933 E: EncodeTo<W>,
1934 {
1935 let mut dset_writer = DataSetWriter::new(to, encoder);
1937 let required_options = IntoTokensOptions::new(self.charset_changed);
1938 dset_writer
1940 .write_sequence(self.into_tokens_with_options(required_options))
1941 .context(PrintDataSetSnafu)?;
1942
1943 Ok(())
1944 }
1945
1946 pub fn write_dataset_with_ts_cs<W>(
1957 &self,
1958 to: W,
1959 ts: &TransferSyntax,
1960 cs: SpecificCharacterSet,
1961 ) -> Result<(), WriteError>
1962 where
1963 W: Write,
1964 {
1965 if let Codec::Dataset(Some(adapter)) = ts.codec() {
1966 let adapter = adapter.adapt_writer(Box::new(to));
1967 let mut dset_writer =
1969 DataSetWriter::with_ts(adapter, ts).context(CreatePrinterSnafu)?;
1970
1971 dset_writer
1973 .write_sequence(self.into_tokens())
1974 .context(PrintDataSetSnafu)?;
1975
1976 Ok(())
1977 } else {
1978 let mut dset_writer =
1980 DataSetWriter::with_ts_cs(to, ts, cs).context(CreatePrinterSnafu)?;
1981
1982 dset_writer
1984 .write_sequence(self.into_tokens())
1985 .context(PrintDataSetSnafu)?;
1986
1987 Ok(())
1988 }
1989 }
1990
1991 pub fn write_dataset_with_ts_cs_options<W>(
1999 &self,
2000 to: W,
2001 ts: &TransferSyntax,
2002 cs: SpecificCharacterSet,
2003 options: DataSetWriterOptions,
2004 ) -> Result<(), WriteError>
2005 where
2006 W: Write,
2007 {
2008 let mut dset_writer =
2010 DataSetWriter::with_ts_cs_options(to, ts, cs, options).context(CreatePrinterSnafu)?;
2011 let required_options = IntoTokensOptions::new(self.charset_changed);
2012
2013 dset_writer
2015 .write_sequence(self.into_tokens_with_options(required_options))
2016 .context(PrintDataSetSnafu)?;
2017
2018 Ok(())
2019 }
2020
2021 pub fn write_dataset_with_ts<W>(&self, to: W, ts: &TransferSyntax) -> Result<(), WriteError>
2032 where
2033 W: Write,
2034 {
2035 self.write_dataset_with_ts_cs(to, ts, SpecificCharacterSet::default())
2036 }
2037
2038 pub fn write_dataset_with_ts_options<W>(
2046 &self,
2047 to: W,
2048 ts: &TransferSyntax,
2049 options: DataSetWriterOptions,
2050 ) -> Result<(), WriteError>
2051 where
2052 W: Write,
2053 {
2054 self.write_dataset_with_ts_cs_options(to, ts, SpecificCharacterSet::default(), options)
2055 }
2056
2057 pub fn with_exact_meta(self, meta: FileMetaTable) -> FileDicomObject<Self> {
2066 FileDicomObject { meta, obj: self }
2067 }
2068
2069 pub fn with_meta(
2102 self,
2103 mut meta: FileMetaTableBuilder,
2104 ) -> Result<FileDicomObject<Self>, WithMetaError> {
2105 if let Some(elem) = self.get(tags::SOP_INSTANCE_UID) {
2106 meta = meta.media_storage_sop_instance_uid(
2107 elem.value().to_str().context(PrepareMetaTableSnafu)?,
2108 );
2109 }
2110 if let Some(elem) = self.get(tags::SOP_CLASS_UID) {
2111 meta = meta
2112 .media_storage_sop_class_uid(elem.value().to_str().context(PrepareMetaTableSnafu)?);
2113 }
2114 Ok(FileDicomObject {
2115 meta: meta.build().context(BuildMetaTableSnafu)?,
2116 obj: self,
2117 })
2118 }
2119
2120 pub fn iter(&self) -> impl Iterator<Item = &InMemElement<D>> + '_ {
2122 self.into_iter()
2123 }
2124
2125 pub fn tags(&self) -> impl Iterator<Item = Tag> + '_ {
2127 self.entries.keys().copied()
2128 }
2129
2130 fn build_object<I>(
2134 dataset: &mut I,
2135 dict: D,
2136 in_item: bool,
2137 len: Length,
2138 read_until: Option<Tag>,
2139 ) -> Result<Self, ReadError>
2140 where
2141 I: ?Sized + Iterator<Item = ParserResult<DataToken>>,
2142 {
2143 let mut entries: BTreeMap<Tag, InMemElement<D>> = BTreeMap::new();
2144 while let Some(token) = dataset.next() {
2146 let elem = match token.context(ReadTokenSnafu)? {
2147 DataToken::PixelSequenceStart => {
2148 if read_until
2150 .map(|t| t <= Tag(0x7fe0, 0x0010))
2151 .unwrap_or(false)
2152 {
2153 break;
2154 }
2155 let value = InMemDicomObject::build_encapsulated_data(&mut *dataset)?;
2156 DataElement::new(Tag(0x7fe0, 0x0010), VR::OB, value)
2157 }
2158 DataToken::ElementHeader(header) => {
2159 if read_until.map(|t| t <= header.tag).unwrap_or(false) {
2161 break;
2162 }
2163
2164 let next_token = dataset.next().context(MissingElementValueSnafu)?;
2166 match next_token.context(ReadTokenSnafu)? {
2167 DataToken::PrimitiveValue(v) => InMemElement::new_with_len(
2168 header.tag,
2169 header.vr,
2170 header.len,
2171 Value::Primitive(v),
2172 ),
2173 token => {
2174 return UnexpectedTokenSnafu { token }.fail();
2175 }
2176 }
2177 }
2178 DataToken::SequenceStart { tag, len } => {
2179 if read_until.map(|t| t <= tag).unwrap_or(false) {
2181 break;
2182 }
2183
2184 let items = Self::build_sequence(tag, len, &mut *dataset, &dict)?;
2186 DataElement::new_with_len(
2187 tag,
2188 VR::SQ,
2189 len,
2190 Value::Sequence(DataSetSequence::new(items, len)),
2191 )
2192 }
2193 DataToken::ItemEnd if in_item => {
2194 return Ok(InMemDicomObject {
2196 entries,
2197 dict,
2198 len,
2199 charset_changed: false,
2200 });
2201 }
2202 token => return UnexpectedTokenSnafu { token }.fail(),
2203 };
2204 entries.insert(elem.tag(), elem);
2205 }
2206
2207 Ok(InMemDicomObject {
2208 entries,
2209 dict,
2210 len,
2211 charset_changed: false,
2212 })
2213 }
2214
2215 fn build_encapsulated_data<I>(
2218 dataset: I,
2219 ) -> Result<Value<InMemDicomObject<D>, InMemFragment>, ReadError>
2220 where
2221 I: Iterator<Item = ParserResult<DataToken>>,
2222 {
2223 let mut offset_table = None;
2228
2229 let mut fragments = C::new();
2230
2231 for token in dataset {
2232 match token.context(ReadTokenSnafu)? {
2233 DataToken::OffsetTable(table) => {
2234 offset_table = Some(table);
2235 }
2236 DataToken::ItemValue(data) => {
2237 fragments.push(data);
2238 }
2239 DataToken::ItemEnd => {
2240 if offset_table.is_none() {
2244 offset_table = Some(Vec::new())
2245 }
2246 }
2247 DataToken::ItemStart { len: _ } => { }
2248 DataToken::SequenceEnd => {
2249 break;
2251 }
2252 token @ DataToken::ElementHeader(_)
2254 | token @ DataToken::PixelSequenceStart
2255 | token @ DataToken::SequenceStart { .. }
2256 | token @ DataToken::PrimitiveValue(_) => {
2257 return UnexpectedTokenSnafu { token }.fail();
2258 }
2259 }
2260 }
2261
2262 Ok(Value::PixelSequence(PixelFragmentSequence::new(
2263 offset_table.unwrap_or_default(),
2264 fragments,
2265 )))
2266 }
2267
2268 fn build_sequence<I>(
2270 _tag: Tag,
2271 _len: Length,
2272 dataset: &mut I,
2273 dict: &D,
2274 ) -> Result<C<InMemDicomObject<D>>, ReadError>
2275 where
2276 I: ?Sized + Iterator<Item = ParserResult<DataToken>>,
2277 {
2278 let mut items: C<_> = SmallVec::new();
2279 while let Some(token) = dataset.next() {
2280 match token.context(ReadTokenSnafu)? {
2281 DataToken::ItemStart { len } => {
2282 items.push(Self::build_object(
2283 &mut *dataset,
2284 dict.clone(),
2285 true,
2286 len,
2287 None,
2288 )?);
2289 }
2290 DataToken::SequenceEnd => {
2291 return Ok(items);
2292 }
2293 token => return UnexpectedTokenSnafu { token }.fail(),
2294 };
2295 }
2296
2297 PrematureEndSnafu.fail()
2299 }
2300
2301 fn lookup_name(&self, name: &str) -> Result<Tag, AccessByNameError> {
2302 self.dict
2303 .by_name(name)
2304 .context(NoSuchAttributeNameSnafu { name })
2305 .map(|e| e.tag())
2306 }
2307}
2308
2309impl<D> ApplyOp for InMemDicomObject<D>
2310where
2311 D: DataDictionary,
2312 D: Clone,
2313{
2314 type Err = ApplyError;
2315
2316 #[inline]
2317 fn apply(&mut self, op: AttributeOp) -> ApplyResult {
2318 self.apply(op)
2319 }
2320}
2321
2322impl<'a, D> IntoIterator for &'a InMemDicomObject<D> {
2323 type Item = &'a InMemElement<D>;
2324 type IntoIter = ::std::collections::btree_map::Values<'a, Tag, InMemElement<D>>;
2325
2326 fn into_iter(self) -> Self::IntoIter {
2327 self.entries.values()
2328 }
2329}
2330
2331impl<D> IntoIterator for InMemDicomObject<D> {
2332 type Item = InMemElement<D>;
2333 type IntoIter = Iter<D>;
2334
2335 fn into_iter(self) -> Self::IntoIter {
2336 Iter {
2337 inner: self.entries.into_iter(),
2338 }
2339 }
2340}
2341
2342#[derive(Debug)]
2344pub struct Iter<D> {
2345 inner: ::std::collections::btree_map::IntoIter<Tag, InMemElement<D>>,
2346}
2347
2348impl<D> Iterator for Iter<D> {
2349 type Item = InMemElement<D>;
2350
2351 fn next(&mut self) -> Option<Self::Item> {
2352 self.inner.next().map(|x| x.1)
2353 }
2354
2355 fn size_hint(&self) -> (usize, Option<usize>) {
2356 self.inner.size_hint()
2357 }
2358
2359 fn count(self) -> usize {
2360 self.inner.count()
2361 }
2362}
2363
2364impl<D> Extend<InMemElement<D>> for InMemDicomObject<D> {
2365 fn extend<I>(&mut self, iter: I)
2366 where
2367 I: IntoIterator<Item = InMemElement<D>>,
2368 {
2369 self.len = Length::UNDEFINED;
2370 self.entries.extend(iter.into_iter().map(|e| (e.tag(), e)))
2371 }
2372}
2373
2374fn even_len(l: u32) -> u32 {
2375 (l + 1) & !1
2376}
2377
2378#[cfg(test)]
2379mod tests {
2380 use super::*;
2381 use crate::{open_file, DicomAttribute as _};
2382 use byteordered::Endianness;
2383 use dicom_core::chrono::FixedOffset;
2384 use dicom_core::value::{DicomDate, DicomDateTime, DicomTime};
2385 use dicom_core::{dicom_value, header::DataElementHeader};
2386 use dicom_encoding::{
2387 decode::{basic::BasicDecoder, implicit_le::ImplicitVRLittleEndianDecoder},
2388 encode::{implicit_le::ImplicitVRLittleEndianEncoder, EncoderFor},
2389 };
2390 use dicom_parser::StatefulDecoder;
2391
2392 fn assert_obj_eq<D>(obj1: &InMemDicomObject<D>, obj2: &InMemDicomObject<D>)
2393 where
2394 D: std::fmt::Debug,
2395 {
2396 assert_eq!(format!("{obj1:?}"), format!("{:?}", obj2))
2399 }
2400
2401 #[test]
2402 fn inmem_object_compare() {
2403 let mut obj1 = InMemDicomObject::new_empty();
2404 let mut obj2 = InMemDicomObject::new_empty();
2405 assert_eq!(obj1, obj2);
2406 let empty_patient_name = DataElement::empty(Tag(0x0010, 0x0010), VR::PN);
2407 obj1.put(empty_patient_name.clone());
2408 assert_ne!(obj1, obj2);
2409 obj2.put(empty_patient_name.clone());
2410 assert_obj_eq(&obj1, &obj2);
2411 }
2412
2413 #[test]
2414 fn inmem_object_read_dataset() {
2415 let data_in = [
2416 0x10, 0x00, 0x10, 0x00, 0x08, 0x00, 0x00, 0x00, b'D', b'o', b'e', b'^', b'J', b'o', b'h', b'n',
2419 ];
2420
2421 let decoder = ImplicitVRLittleEndianDecoder::default();
2422 let text = SpecificCharacterSet::default();
2423 let mut cursor = &data_in[..];
2424 let parser = StatefulDecoder::new(
2425 &mut cursor,
2426 decoder,
2427 BasicDecoder::new(Endianness::Little),
2428 text,
2429 );
2430
2431 let obj = InMemDicomObject::read_dataset(parser).unwrap();
2432
2433 let mut gt = InMemDicomObject::new_empty();
2434
2435 let patient_name = DataElement::new(
2436 Tag(0x0010, 0x0010),
2437 VR::PN,
2438 dicom_value!(Strs, ["Doe^John"]),
2439 );
2440 gt.put(patient_name);
2441
2442 assert_eq!(obj, gt);
2443 }
2444
2445 #[test]
2446 fn inmem_object_read_dataset_with_ts_cs() {
2447 let data_in = [
2448 0x10, 0x00, 0x10, 0x00, 0x08, 0x00, 0x00, 0x00, b'D', b'o', b'e', b'^', b'J', b'o', b'h', b'n',
2451 ];
2452
2453 let ts = TransferSyntaxRegistry.get("1.2.840.10008.1.2").unwrap();
2454 let cs = SpecificCharacterSet::default();
2455 let mut cursor = &data_in[..];
2456
2457 let obj = InMemDicomObject::read_dataset_with_dict_ts_cs(
2458 &mut cursor,
2459 StandardDataDictionary,
2460 ts,
2461 cs,
2462 )
2463 .unwrap();
2464
2465 let mut gt = InMemDicomObject::new_empty();
2466
2467 let patient_name = DataElement::new(
2468 Tag(0x0010, 0x0010),
2469 VR::PN,
2470 dicom_value!(Strs, ["Doe^John"]),
2471 );
2472 gt.put(patient_name);
2473
2474 assert_eq!(obj, gt);
2475 }
2476
2477 #[test]
2480 fn inmem_object_read_dataset_saves_len() {
2481 let data_in = [
2482 0x08, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x00, 0x00, b'I', b'S', b'O', b'_', b'I', b'R', b' ', b'1', b'0', b'0',
2487 0x08, 0x00, 0x90, 0x00, 0x0c, 0x00, 0x00, 0x00, b'S', b'i', b'm', 0xF5, b'e', b's', b'^', b'J', b'o', 0xE3,
2491 b'o', b' ',
2492 ];
2493
2494 let ts = TransferSyntaxRegistry.get("1.2.840.10008.1.2").unwrap();
2495 let mut cursor = &data_in[..];
2496
2497 let obj =
2498 InMemDicomObject::read_dataset_with_dict_ts(&mut cursor, StandardDataDictionary, ts)
2499 .unwrap();
2500
2501 let physician_name = obj.element(Tag(0x0008, 0x0090)).unwrap();
2502 assert_eq!(physician_name.header().len, Length(12));
2503 assert_eq!(physician_name.value().to_str().unwrap(), "Simões^João");
2504 }
2505
2506 #[test]
2507 fn inmem_object_write_dataset() {
2508 let mut obj = InMemDicomObject::new_empty();
2509
2510 let patient_name =
2511 DataElement::new(Tag(0x0010, 0x0010), VR::PN, dicom_value!(Str, "Doe^John"));
2512 obj.put(patient_name);
2513
2514 let mut out = Vec::new();
2515
2516 let printer = EncoderFor::new(ImplicitVRLittleEndianEncoder::default());
2517
2518 obj.write_dataset(&mut out, printer).unwrap();
2519
2520 assert_eq!(
2521 out,
2522 &[
2523 0x10, 0x00, 0x10, 0x00, 0x08, 0x00, 0x00, 0x00, b'D', b'o', b'e', b'^', b'J', b'o', b'h', b'n',
2526 ][..],
2527 );
2528 }
2529
2530 #[test]
2531 fn inmem_object_write_dataset_with_ts() {
2532 let mut obj = InMemDicomObject::new_empty();
2533
2534 let patient_name =
2535 DataElement::new(Tag(0x0010, 0x0010), VR::PN, dicom_value!(Str, "Doe^John"));
2536 obj.put(patient_name);
2537
2538 let mut out = Vec::new();
2539
2540 let ts = TransferSyntaxRegistry.get("1.2.840.10008.1.2.1").unwrap();
2541
2542 obj.write_dataset_with_ts(&mut out, ts).unwrap();
2543
2544 assert_eq!(
2545 out,
2546 &[
2547 0x10, 0x00, 0x10, 0x00, b'P', b'N', 0x08, 0x00, b'D', b'o', b'e', b'^', b'J', b'o', b'h', b'n',
2551 ][..],
2552 );
2553 }
2554
2555 #[test]
2556 fn inmem_object_write_dataset_encapsulated_pixel_data() {
2557 let mut obj = InMemDicomObject::new_empty();
2558
2559 let sop_instance_uid = DataElement::new(
2560 tags::SOP_INSTANCE_UID,
2561 VR::UI,
2562 "2.25.44399302050596340528032699331187776010",
2563 );
2564 obj.put(sop_instance_uid);
2565
2566 obj.put(DataElement::new(
2567 tags::PIXEL_DATA,
2568 VR::OB,
2569 PixelFragmentSequence::new_fragments([
2570 vec![0x01, 0x02, 0x03, 0x04],
2571 vec![0x05, 0x06, 0x07, 0x08],
2572 ]),
2573 ));
2574
2575 let mut out = Vec::new();
2576
2577 let ts = TransferSyntaxRegistry
2578 .get(uids::ENCAPSULATED_UNCOMPRESSED_EXPLICIT_VR_LITTLE_ENDIAN)
2579 .unwrap();
2580
2581 obj.write_dataset_with_ts(&mut out, ts).unwrap();
2582
2583 assert_eq!(
2584 out,
2585 &[
2586 0x08, 0x00, 0x18, 0x00, b'U', b'I', 0x2c, 0x00, b'2', b'.', b'2', b'5', b'.', b'4', b'4', b'3', b'9', b'9', b'3', b'0', b'2', b'0',
2591 b'5', b'0', b'5', b'9', b'6', b'3', b'4', b'0', b'5', b'2', b'8', b'0', b'3', b'2',
2592 b'6', b'9', b'9', b'3', b'3', b'1', b'1', b'8', b'7', b'7', b'7', b'6', b'0', b'1',
2593 b'0', b'\0', 0xe0, 0x7f, 0x10, 0x00, b'O', b'B', 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x00, 0xe0, 0x04, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0xfe, 0xff, 0x00, 0xe0, 0x04, 0x00, 0x00, 0x00, 0x05, 0x06, 0x07, 0x08, 0xfe, 0xff, 0xdd, 0xe0, 0x00, 0x00, 0x00, 0x00, ][..],
2607 );
2608 }
2609
2610 #[test]
2611 fn inmem_object_write_dataset_with_ts_cs() {
2612 let mut obj = InMemDicomObject::new_empty();
2613
2614 let patient_name =
2615 DataElement::new(Tag(0x0010, 0x0010), VR::PN, dicom_value!(Str, "Doe^John"));
2616 obj.put(patient_name);
2617
2618 let mut out = Vec::new();
2619
2620 let ts = TransferSyntaxRegistry.get("1.2.840.10008.1.2").unwrap();
2621 let cs = SpecificCharacterSet::default();
2622
2623 obj.write_dataset_with_ts_cs(&mut out, ts, cs).unwrap();
2624
2625 assert_eq!(
2626 out,
2627 &[
2628 0x10, 0x00, 0x10, 0x00, 0x08, 0x00, 0x00, 0x00, b'D', b'o', b'e', b'^', b'J', b'o', b'h', b'n',
2631 ][..],
2632 );
2633 }
2634
2635 #[test]
2638 fn inmem_object_write_datetime_odd() {
2639 let mut obj = InMemDicomObject::new_empty();
2640
2641 let instance_number =
2643 DataElement::new(Tag(0x0020, 0x0013), VR::IS, PrimitiveValue::from(1_i32));
2644 obj.put(instance_number);
2645
2646 let dt = DicomDateTime::from_date_and_time_with_time_zone(
2648 DicomDate::from_ymd(2022, 11, 22).unwrap(),
2649 DicomTime::from_hms(18, 9, 35).unwrap(),
2650 FixedOffset::east_opt(3600).unwrap(),
2651 )
2652 .unwrap();
2653 let instance_coercion_date_time =
2654 DataElement::new(Tag(0x0008, 0x0015), VR::DT, dicom_value!(DateTime, dt));
2655 obj.put(instance_coercion_date_time);
2656
2657 let ts = TransferSyntaxRegistry.get("1.2.840.10008.1.2.1").unwrap();
2659
2660 let mut out = Vec::new();
2661 obj.write_dataset_with_ts(&mut out, ts)
2662 .expect("should write DICOM data without errors");
2663
2664 assert_eq!(
2665 out,
2666 &[
2667 0x08, 0x00, 0x15, 0x00, b'D', b'T', 0x14, 0x00, b'2', b'0', b'2', b'2', b'1', b'1', b'2', b'2', b'1', b'8', b'0', b'9', b'3', b'5', b'+', b'0', b'1', b'0', b'0', b' ', 0x20, 0x00, 0x13, 0x00, b'I', b'S', 0x02, 0x00, b'1', b' ' ][..],
2681 );
2682 }
2683
2684 #[test]
2687 fn inmem_write_to_file_with_meta() {
2688 let sop_uid = "1.4.645.212121";
2689 let mut obj = InMemDicomObject::new_empty();
2690
2691 obj.put(DataElement::new(
2692 Tag(0x0010, 0x0010),
2693 VR::PN,
2694 dicom_value!(Strs, ["Doe^John"]),
2695 ));
2696 obj.put(DataElement::new(
2697 Tag(0x0008, 0x0060),
2698 VR::CS,
2699 dicom_value!(Strs, ["CR"]),
2700 ));
2701 obj.put(DataElement::new(
2702 Tag(0x0008, 0x0018),
2703 VR::UI,
2704 dicom_value!(Strs, [sop_uid]),
2705 ));
2706
2707 let file_object = obj
2708 .with_meta(
2709 FileMetaTableBuilder::default()
2710 .transfer_syntax("1.2.840.10008.1.2.1")
2712 .media_storage_sop_class_uid("1.2.840.10008.5.1.4.1.1.1")
2714 .media_storage_sop_instance_uid(sop_uid),
2715 )
2716 .unwrap();
2717
2718 let dir = tempfile::tempdir().unwrap();
2720 let mut file_path = dir.keep();
2721 file_path.push(format!("{sop_uid}.dcm"));
2722
2723 file_object.write_to_file(&file_path).unwrap();
2724
2725 let saved_object = open_file(file_path).unwrap();
2727 assert_eq!(file_object, saved_object);
2728 }
2729
2730 #[test]
2733 fn inmem_with_meta_infers_sop_instance_uid() {
2734 let sop_uid = "1.4.645.252521";
2735 let mut obj = InMemDicomObject::new_empty();
2736
2737 obj.put(DataElement::new(
2738 tags::SOP_INSTANCE_UID,
2739 VR::UI,
2740 PrimitiveValue::from(sop_uid),
2741 ));
2742
2743 let file_object = obj
2744 .with_meta(
2745 FileMetaTableBuilder::default()
2747 .transfer_syntax("1.2.840.10008.1.2.1")
2749 .media_storage_sop_class_uid("1.2.840.10008.5.1.4.1.1.1"),
2751 )
2752 .unwrap();
2753
2754 let meta = file_object.meta();
2755
2756 assert_eq!(
2757 meta.media_storage_sop_instance_uid.trim_end_matches('\0'),
2758 sop_uid.trim_end_matches('\0'),
2759 );
2760 }
2761
2762 #[test]
2764 fn inmem_write_to_file_with_exact_meta() {
2765 let sop_uid = "1.4.645.212121";
2766 let mut obj = InMemDicomObject::new_empty();
2767
2768 obj.put(DataElement::new(
2769 Tag(0x0010, 0x0010),
2770 VR::PN,
2771 dicom_value!(Strs, ["Doe^John"]),
2772 ));
2773 obj.put(DataElement::new(
2774 Tag(0x0008, 0x0060),
2775 VR::CS,
2776 dicom_value!(Strs, ["CR"]),
2777 ));
2778 obj.put(DataElement::new(
2779 Tag(0x0008, 0x0018),
2780 VR::UI,
2781 dicom_value!(Strs, [sop_uid]),
2782 ));
2783
2784 let file_object = obj.with_exact_meta(
2785 FileMetaTableBuilder::default()
2786 .transfer_syntax("1.2.840.10008.1.2.1")
2788 .media_storage_sop_class_uid("1.2.840.10008.5.1.4.1.1.1")
2790 .media_storage_sop_instance_uid(sop_uid)
2791 .build()
2792 .unwrap(),
2793 );
2794
2795 let dir = tempfile::tempdir().unwrap();
2797 let mut file_path = dir.keep();
2798 file_path.push(format!("{sop_uid}.dcm"));
2799
2800 file_object.write_to_file(&file_path).unwrap();
2801
2802 let saved_object = open_file(file_path).unwrap();
2804 assert_eq!(file_object, saved_object);
2805 }
2806
2807 #[test]
2808 fn inmem_object_get() {
2809 let another_patient_name = DataElement::new(
2810 Tag(0x0010, 0x0010),
2811 VR::PN,
2812 PrimitiveValue::Str("Doe^John".to_string()),
2813 );
2814 let mut obj = InMemDicomObject::new_empty();
2815 obj.put(another_patient_name.clone());
2816 let elem1 = obj.element(Tag(0x0010, 0x0010)).unwrap();
2817 assert_eq!(elem1, &another_patient_name);
2818 }
2819
2820 #[test]
2821 fn infer_media_sop_from_dataset_sop_elements() {
2822 let sop_instance_uid = "1.4.645.313131";
2823 let sop_class_uid = "1.2.840.10008.5.1.4.1.1.2";
2824 let mut obj = InMemDicomObject::new_empty();
2825
2826 obj.put(DataElement::new(
2827 Tag(0x0008, 0x0018),
2828 VR::UI,
2829 dicom_value!(Strs, [sop_instance_uid]),
2830 ));
2831 obj.put(DataElement::new(
2832 Tag(0x0008, 0x0016),
2833 VR::UI,
2834 dicom_value!(Strs, [sop_class_uid]),
2835 ));
2836
2837 let file_object = obj.with_exact_meta(
2838 FileMetaTableBuilder::default()
2839 .transfer_syntax("1.2.840.10008.1.2.1")
2840 .media_storage_sop_class_uid("")
2842 .media_storage_sop_instance_uid("")
2843 .build()
2844 .unwrap(),
2845 );
2846
2847 let dir = tempfile::tempdir().unwrap();
2849 let mut file_path = dir.keep();
2850 file_path.push(format!("{sop_instance_uid}.dcm"));
2851
2852 file_object.write_to_file(&file_path).unwrap();
2853
2854 let saved_object = open_file(file_path).unwrap();
2856
2857 assert_eq!(
2859 saved_object.meta().media_storage_sop_instance_uid(),
2860 sop_instance_uid
2861 );
2862 assert_eq!(
2863 saved_object.meta().media_storage_sop_class_uid(),
2864 sop_class_uid
2865 );
2866 }
2867
2868 #[test]
2869 fn inmem_object_get_opt() {
2870 let another_patient_name = DataElement::new(
2871 Tag(0x0010, 0x0010),
2872 VR::PN,
2873 PrimitiveValue::Str("Doe^John".to_string()),
2874 );
2875 let mut obj = InMemDicomObject::new_empty();
2876 obj.put(another_patient_name.clone());
2877 let elem1 = obj.element_opt(Tag(0x0010, 0x0010)).unwrap();
2878 assert_eq!(elem1, Some(&another_patient_name));
2879
2880 assert_eq!(obj.element_opt(Tag(0x0010, 0x0020)).unwrap(), None);
2882 }
2883
2884 #[test]
2885 fn inmem_object_get_by_name() {
2886 let another_patient_name = DataElement::new(
2887 Tag(0x0010, 0x0010),
2888 VR::PN,
2889 PrimitiveValue::Str("Doe^John".to_string()),
2890 );
2891 let mut obj = InMemDicomObject::new_empty();
2892 obj.put(another_patient_name.clone());
2893 let elem1 = obj.element_by_name("PatientName").unwrap();
2894 assert_eq!(elem1, &another_patient_name);
2895 }
2896
2897 #[test]
2898 fn inmem_object_get_by_name_opt() {
2899 let another_patient_name = DataElement::new(
2900 Tag(0x0010, 0x0010),
2901 VR::PN,
2902 PrimitiveValue::Str("Doe^John".to_string()),
2903 );
2904 let mut obj = InMemDicomObject::new_empty();
2905 obj.put(another_patient_name.clone());
2906 let elem1 = obj.element_by_name_opt("PatientName").unwrap();
2907 assert_eq!(elem1, Some(&another_patient_name));
2908
2909 assert_eq!(obj.element_by_name_opt("PatientID").unwrap(), None);
2911 }
2912
2913 #[test]
2914 fn inmem_object_take_element() {
2915 let another_patient_name = DataElement::new(
2916 Tag(0x0010, 0x0010),
2917 VR::PN,
2918 PrimitiveValue::Str("Doe^John".to_string()),
2919 );
2920 let mut obj = InMemDicomObject::new_empty();
2921 obj.put(another_patient_name.clone());
2922 let elem1 = obj.take_element(Tag(0x0010, 0x0010)).unwrap();
2923 assert_eq!(elem1, another_patient_name);
2924 assert!(matches!(
2925 obj.take_element(Tag(0x0010, 0x0010)),
2926 Err(AccessError::NoSuchDataElementTag {
2927 tag: Tag(0x0010, 0x0010),
2928 ..
2929 })
2930 ));
2931 }
2932
2933 #[test]
2934 fn inmem_object_take_element_by_name() {
2935 let another_patient_name = DataElement::new(
2936 Tag(0x0010, 0x0010),
2937 VR::PN,
2938 PrimitiveValue::Str("Doe^John".to_string()),
2939 );
2940 let mut obj = InMemDicomObject::new_empty();
2941 obj.put(another_patient_name.clone());
2942 let elem1 = obj.take_element_by_name("PatientName").unwrap();
2943 assert_eq!(elem1, another_patient_name);
2944 assert!(matches!(
2945 obj.take_element_by_name("PatientName"),
2946 Err(AccessByNameError::NoSuchDataElementAlias {
2947 tag: Tag(0x0010, 0x0010),
2948 alias,
2949 ..
2950 }) if alias == "PatientName"));
2951 }
2952
2953 #[test]
2954 fn inmem_object_remove_element() {
2955 let another_patient_name = DataElement::new(
2956 Tag(0x0010, 0x0010),
2957 VR::PN,
2958 PrimitiveValue::Str("Doe^John".to_string()),
2959 );
2960 let mut obj = InMemDicomObject::new_empty();
2961 obj.put(another_patient_name.clone());
2962 assert!(obj.remove_element(Tag(0x0010, 0x0010)));
2963 assert!(!obj.remove_element(Tag(0x0010, 0x0010)));
2964 }
2965
2966 #[test]
2967 fn inmem_object_remove_element_by_name() {
2968 let another_patient_name = DataElement::new(
2969 Tag(0x0010, 0x0010),
2970 VR::PN,
2971 PrimitiveValue::Str("Doe^John".to_string()),
2972 );
2973 let mut obj = InMemDicomObject::new_empty();
2974 obj.put(another_patient_name.clone());
2975 assert!(obj.remove_element_by_name("PatientName").unwrap());
2976 assert!(!obj.remove_element_by_name("PatientName").unwrap());
2977 }
2978
2979 #[test]
2981 fn inmem_traverse_elements() {
2982 let sop_uid = "1.4.645.212121";
2983 let mut obj = InMemDicomObject::new_empty();
2984
2985 obj.put(DataElement::new(
2986 Tag(0x0010, 0x0010),
2987 VR::PN,
2988 dicom_value!(Strs, ["Doe^John"]),
2989 ));
2990 obj.put(DataElement::new(
2991 Tag(0x0008, 0x0060),
2992 VR::CS,
2993 dicom_value!(Strs, ["CR"]),
2994 ));
2995 obj.put(DataElement::new(
2996 Tag(0x0008, 0x0018),
2997 VR::UI,
2998 dicom_value!(Strs, [sop_uid]),
2999 ));
3000
3001 {
3002 let mut iter = obj.iter();
3003 assert_eq!(
3004 *iter.next().unwrap().header(),
3005 DataElementHeader::new(Tag(0x0008, 0x0018), VR::UI, Length(sop_uid.len() as u32)),
3006 );
3007 assert_eq!(
3008 *iter.next().unwrap().header(),
3009 DataElementHeader::new(Tag(0x0008, 0x0060), VR::CS, Length(2)),
3010 );
3011 assert_eq!(
3012 *iter.next().unwrap().header(),
3013 DataElementHeader::new(Tag(0x0010, 0x0010), VR::PN, Length(8)),
3014 );
3015 }
3016
3017 let tags: Vec<_> = obj.tags().collect();
3019 assert_eq!(
3020 tags,
3021 vec![
3022 Tag(0x0008, 0x0018),
3023 Tag(0x0008, 0x0060),
3024 Tag(0x0010, 0x0010),
3025 ]
3026 );
3027
3028 let mut iter = obj.into_iter();
3030 assert_eq!(
3031 iter.next(),
3032 Some(DataElement::new(
3033 Tag(0x0008, 0x0018),
3034 VR::UI,
3035 dicom_value!(Strs, [sop_uid]),
3036 )),
3037 );
3038 assert_eq!(
3039 iter.next(),
3040 Some(DataElement::new(
3041 Tag(0x0008, 0x0060),
3042 VR::CS,
3043 dicom_value!(Strs, ["CR"]),
3044 )),
3045 );
3046 assert_eq!(
3047 iter.next(),
3048 Some(DataElement::new(
3049 Tag(0x0010, 0x0010),
3050 VR::PN,
3051 PrimitiveValue::from("Doe^John"),
3052 )),
3053 );
3054 }
3055
3056 #[test]
3057 fn inmem_empty_object_into_tokens() {
3058 let obj = InMemDicomObject::new_empty();
3059 let tokens = obj.into_tokens();
3060 assert_eq!(tokens.count(), 0);
3061 }
3062
3063 #[test]
3064 fn inmem_shallow_object_from_tokens() {
3065 let tokens = vec![
3066 DataToken::ElementHeader(DataElementHeader {
3067 tag: Tag(0x0008, 0x0060),
3068 vr: VR::CS,
3069 len: Length(2),
3070 }),
3071 DataToken::PrimitiveValue(PrimitiveValue::Str("MG".to_owned())),
3072 DataToken::ElementHeader(DataElementHeader {
3073 tag: Tag(0x0010, 0x0010),
3074 vr: VR::PN,
3075 len: Length(8),
3076 }),
3077 DataToken::PrimitiveValue(PrimitiveValue::Str("Doe^John".to_owned())),
3078 ];
3079
3080 let gt_obj = InMemDicomObject::from_element_iter(vec![
3081 DataElement::new(
3082 Tag(0x0010, 0x0010),
3083 VR::PN,
3084 PrimitiveValue::Str("Doe^John".to_string()),
3085 ),
3086 DataElement::new(
3087 Tag(0x0008, 0x0060),
3088 VR::CS,
3089 PrimitiveValue::Str("MG".to_string()),
3090 ),
3091 ]);
3092
3093 let obj = InMemDicomObject::build_object(
3094 &mut tokens.into_iter().map(Result::Ok),
3095 StandardDataDictionary,
3096 false,
3097 Length::UNDEFINED,
3098 None,
3099 )
3100 .unwrap();
3101
3102 assert_obj_eq(&obj, >_obj);
3103 }
3104
3105 #[test]
3106 fn inmem_shallow_object_into_tokens() {
3107 let patient_name = DataElement::new(
3108 Tag(0x0010, 0x0010),
3109 VR::PN,
3110 PrimitiveValue::Str("Doe^John".to_string()),
3111 );
3112 let modality = DataElement::new(
3113 Tag(0x0008, 0x0060),
3114 VR::CS,
3115 PrimitiveValue::Str("MG".to_string()),
3116 );
3117 let mut obj = InMemDicomObject::new_empty();
3118 obj.put(patient_name);
3119 obj.put(modality);
3120
3121 let tokens: Vec<_> = obj.into_tokens().collect();
3122
3123 assert_eq!(
3124 tokens,
3125 vec![
3126 DataToken::ElementHeader(DataElementHeader {
3127 tag: Tag(0x0008, 0x0060),
3128 vr: VR::CS,
3129 len: Length(2),
3130 }),
3131 DataToken::PrimitiveValue(PrimitiveValue::Str("MG".to_owned())),
3132 DataToken::ElementHeader(DataElementHeader {
3133 tag: Tag(0x0010, 0x0010),
3134 vr: VR::PN,
3135 len: Length(8),
3136 }),
3137 DataToken::PrimitiveValue(PrimitiveValue::Str("Doe^John".to_owned())),
3138 ]
3139 );
3140 }
3141
3142 #[test]
3143 fn inmem_deep_object_from_tokens() {
3144 use smallvec::smallvec;
3145
3146 let obj_1 = InMemDicomObject::from_element_iter(vec![
3147 DataElement::new(Tag(0x0018, 0x6012), VR::US, Value::Primitive(1_u16.into())),
3148 DataElement::new(Tag(0x0018, 0x6014), VR::US, Value::Primitive(2_u16.into())),
3149 ]);
3150
3151 let obj_2 = InMemDicomObject::from_element_iter(vec![DataElement::new(
3152 Tag(0x0018, 0x6012),
3153 VR::US,
3154 Value::Primitive(4_u16.into()),
3155 )]);
3156
3157 let gt_obj = InMemDicomObject::from_element_iter(vec![
3158 DataElement::new(
3159 Tag(0x0018, 0x6011),
3160 VR::SQ,
3161 Value::from(DataSetSequence::new(
3162 smallvec![obj_1, obj_2],
3163 Length::UNDEFINED,
3164 )),
3165 ),
3166 DataElement::new(Tag(0x0020, 0x4000), VR::LT, Value::Primitive("TEST".into())),
3167 ]);
3168
3169 let tokens: Vec<_> = vec![
3170 DataToken::SequenceStart {
3171 tag: Tag(0x0018, 0x6011),
3172 len: Length::UNDEFINED,
3173 },
3174 DataToken::ItemStart {
3175 len: Length::UNDEFINED,
3176 },
3177 DataToken::ElementHeader(DataElementHeader {
3178 tag: Tag(0x0018, 0x6012),
3179 vr: VR::US,
3180 len: Length(2),
3181 }),
3182 DataToken::PrimitiveValue(PrimitiveValue::U16([1].as_ref().into())),
3183 DataToken::ElementHeader(DataElementHeader {
3184 tag: Tag(0x0018, 0x6014),
3185 vr: VR::US,
3186 len: Length(2),
3187 }),
3188 DataToken::PrimitiveValue(PrimitiveValue::U16([2].as_ref().into())),
3189 DataToken::ItemEnd,
3190 DataToken::ItemStart {
3191 len: Length::UNDEFINED,
3192 },
3193 DataToken::ElementHeader(DataElementHeader {
3194 tag: Tag(0x0018, 0x6012),
3195 vr: VR::US,
3196 len: Length(2),
3197 }),
3198 DataToken::PrimitiveValue(PrimitiveValue::U16([4].as_ref().into())),
3199 DataToken::ItemEnd,
3200 DataToken::SequenceEnd,
3201 DataToken::ElementHeader(DataElementHeader {
3202 tag: Tag(0x0020, 0x4000),
3203 vr: VR::LT,
3204 len: Length(4),
3205 }),
3206 DataToken::PrimitiveValue(PrimitiveValue::Str("TEST".into())),
3207 ];
3208
3209 let obj = InMemDicomObject::build_object(
3210 &mut tokens.into_iter().map(Result::Ok),
3211 StandardDataDictionary,
3212 false,
3213 Length::UNDEFINED,
3214 None,
3215 )
3216 .unwrap();
3217
3218 assert_obj_eq(&obj, >_obj);
3219 }
3220
3221 #[test]
3222 fn inmem_deep_object_into_tokens() {
3223 use smallvec::smallvec;
3224
3225 let obj_1 = InMemDicomObject::from_element_iter(vec![
3226 DataElement::new(Tag(0x0018, 0x6012), VR::US, Value::Primitive(1_u16.into())),
3227 DataElement::new(Tag(0x0018, 0x6014), VR::US, Value::Primitive(2_u16.into())),
3228 ]);
3229
3230 let obj_2 = InMemDicomObject::from_element_iter(vec![DataElement::new(
3231 Tag(0x0018, 0x6012),
3232 VR::US,
3233 Value::Primitive(4_u16.into()),
3234 )]);
3235
3236 let main_obj = InMemDicomObject::from_element_iter(vec![
3237 DataElement::new(
3238 Tag(0x0018, 0x6011),
3239 VR::SQ,
3240 Value::from(DataSetSequence::new(
3241 smallvec![obj_1, obj_2],
3242 Length::UNDEFINED,
3243 )),
3244 ),
3245 DataElement::new(Tag(0x0020, 0x4000), VR::LT, Value::Primitive("TEST".into())),
3246 ]);
3247
3248 let tokens: Vec<_> = main_obj.into_tokens().collect();
3249
3250 assert_eq!(
3251 tokens,
3252 vec![
3253 DataToken::SequenceStart {
3254 tag: Tag(0x0018, 0x6011),
3255 len: Length::UNDEFINED,
3256 },
3257 DataToken::ItemStart {
3258 len: Length::UNDEFINED,
3259 },
3260 DataToken::ElementHeader(DataElementHeader {
3261 tag: Tag(0x0018, 0x6012),
3262 vr: VR::US,
3263 len: Length(2),
3264 }),
3265 DataToken::PrimitiveValue(PrimitiveValue::U16([1].as_ref().into())),
3266 DataToken::ElementHeader(DataElementHeader {
3267 tag: Tag(0x0018, 0x6014),
3268 vr: VR::US,
3269 len: Length(2),
3270 }),
3271 DataToken::PrimitiveValue(PrimitiveValue::U16([2].as_ref().into())),
3272 DataToken::ItemEnd,
3273 DataToken::ItemStart {
3274 len: Length::UNDEFINED,
3275 },
3276 DataToken::ElementHeader(DataElementHeader {
3277 tag: Tag(0x0018, 0x6012),
3278 vr: VR::US,
3279 len: Length(2),
3280 }),
3281 DataToken::PrimitiveValue(PrimitiveValue::U16([4].as_ref().into())),
3282 DataToken::ItemEnd,
3283 DataToken::SequenceEnd,
3284 DataToken::ElementHeader(DataElementHeader {
3285 tag: Tag(0x0020, 0x4000),
3286 vr: VR::LT,
3287 len: Length(4),
3288 }),
3289 DataToken::PrimitiveValue(PrimitiveValue::Str("TEST".into())),
3290 ]
3291 );
3292 }
3293
3294 #[test]
3295 fn inmem_encapsulated_pixel_data_from_tokens() {
3296 use smallvec::smallvec;
3297
3298 let gt_obj = InMemDicomObject::from_element_iter(vec![DataElement::new(
3299 Tag(0x7fe0, 0x0010),
3300 VR::OB,
3301 Value::from(PixelFragmentSequence::new_fragments(smallvec![vec![
3302 0x33;
3303 32
3304 ]])),
3305 )]);
3306
3307 let tokens: Vec<_> = vec![
3308 DataToken::PixelSequenceStart,
3309 DataToken::ItemStart { len: Length(0) },
3310 DataToken::ItemEnd,
3311 DataToken::ItemStart { len: Length(32) },
3312 DataToken::ItemValue(vec![0x33; 32]),
3313 DataToken::ItemEnd,
3314 DataToken::SequenceEnd,
3315 ];
3316
3317 let obj = InMemDicomObject::build_object(
3318 &mut tokens.into_iter().map(Result::Ok),
3319 StandardDataDictionary,
3320 false,
3321 Length::UNDEFINED,
3322 None,
3323 )
3324 .unwrap();
3325
3326 assert_obj_eq(&obj, >_obj);
3327 }
3328
3329 #[test]
3330 fn inmem_encapsulated_pixel_data_into_tokens() {
3331 use smallvec::smallvec;
3332
3333 let main_obj = InMemDicomObject::from_element_iter(vec![DataElement::new(
3334 Tag(0x7fe0, 0x0010),
3335 VR::OB,
3336 Value::from(PixelFragmentSequence::new_fragments(smallvec![vec![
3337 0x33;
3338 32
3339 ]])),
3340 )]);
3341
3342 let tokens: Vec<_> = main_obj.into_tokens().collect();
3343
3344 assert_eq!(
3345 tokens,
3346 vec![
3347 DataToken::PixelSequenceStart,
3348 DataToken::ItemStart { len: Length(0) },
3349 DataToken::ItemEnd,
3350 DataToken::ItemStart { len: Length(32) },
3351 DataToken::ItemValue(vec![0x33; 32]),
3352 DataToken::ItemEnd,
3353 DataToken::SequenceEnd,
3354 ]
3355 );
3356 }
3357
3358 #[test]
3361 fn can_use_behind_trait() {
3362 fn dicom_dataset() -> impl DicomObject {
3363 InMemDicomObject::from_element_iter([DataElement::new(
3364 tags::PATIENT_NAME,
3365 VR::PN,
3366 PrimitiveValue::Str("Doe^John".to_string()),
3367 )])
3368 }
3369
3370 let obj = dicom_dataset();
3371 let elem1 = obj
3372 .attr_by_name_opt("PatientName")
3373 .unwrap()
3374 .expect("PatientName should be present");
3375 assert_eq!(
3376 &elem1
3377 .to_str()
3378 .expect("should be able to retrieve patient name as string"),
3379 "Doe^John"
3380 );
3381
3382 assert!(obj.attr_opt(tags::PATIENT_ID).unwrap().is_none());
3384 }
3385
3386 #[test]
3388 fn inmem_ops() {
3389 let base_obj = InMemDicomObject::from_element_iter([
3391 DataElement::new(
3392 tags::SERIES_INSTANCE_UID,
3393 VR::UI,
3394 PrimitiveValue::from("2.25.137041794342168732369025909031346220736.1"),
3395 ),
3396 DataElement::new(
3397 tags::SERIES_INSTANCE_UID,
3398 VR::UI,
3399 PrimitiveValue::from("2.25.137041794342168732369025909031346220736.1"),
3400 ),
3401 DataElement::new(
3402 tags::SOP_INSTANCE_UID,
3403 VR::UI,
3404 PrimitiveValue::from("2.25.137041794342168732369025909031346220736.1.1"),
3405 ),
3406 DataElement::new(
3407 tags::STUDY_DESCRIPTION,
3408 VR::LO,
3409 PrimitiveValue::from("Test study"),
3410 ),
3411 DataElement::new(
3412 tags::INSTITUTION_NAME,
3413 VR::LO,
3414 PrimitiveValue::from("Test Hospital"),
3415 ),
3416 DataElement::new(tags::ROWS, VR::US, PrimitiveValue::from(768_u16)),
3417 DataElement::new(tags::COLUMNS, VR::US, PrimitiveValue::from(1024_u16)),
3418 DataElement::new(
3419 tags::LOSSY_IMAGE_COMPRESSION,
3420 VR::CS,
3421 PrimitiveValue::from("01"),
3422 ),
3423 DataElement::new(
3424 tags::LOSSY_IMAGE_COMPRESSION_RATIO,
3425 VR::DS,
3426 PrimitiveValue::from("5"),
3427 ),
3428 DataElement::new(
3429 tags::LOSSY_IMAGE_COMPRESSION_METHOD,
3430 VR::DS,
3431 PrimitiveValue::from("ISO_10918_1"),
3432 ),
3433 ]);
3434
3435 {
3436 let mut obj = base_obj.clone();
3438 let op = AttributeOp {
3439 selector: AttributeSelector::from(tags::STUDY_DESCRIPTION),
3440 action: AttributeAction::Remove,
3441 };
3442
3443 obj.apply(op).unwrap();
3444
3445 assert_eq!(obj.get(tags::STUDY_DESCRIPTION), None);
3446 }
3447 {
3448 let mut obj = base_obj.clone();
3449
3450 let op = AttributeOp {
3453 selector: tags::INSTITUTION_NAME.into(),
3454 action: AttributeAction::SetIfMissing("Nope Hospital".into()),
3455 };
3456
3457 obj.apply(op).unwrap();
3458
3459 assert_eq!(
3460 obj.get(tags::INSTITUTION_NAME),
3461 Some(&DataElement::new(
3462 tags::INSTITUTION_NAME,
3463 VR::LO,
3464 PrimitiveValue::from("Test Hospital"),
3465 ))
3466 );
3467
3468 let op = AttributeOp::new(
3470 tags::INSTITUTION_NAME,
3471 AttributeAction::ReplaceStr("REMOVED".into()),
3472 );
3473
3474 obj.apply(op).unwrap();
3475
3476 assert_eq!(
3477 obj.get(tags::INSTITUTION_NAME),
3478 Some(&DataElement::new(
3479 tags::INSTITUTION_NAME,
3480 VR::LO,
3481 PrimitiveValue::from("REMOVED"),
3482 ))
3483 );
3484
3485 let op = AttributeOp::new(
3488 tags::REQUESTING_PHYSICIAN,
3489 AttributeAction::ReplaceStr("Doctor^Anonymous".into()),
3490 );
3491
3492 obj.apply(op).unwrap();
3493
3494 assert_eq!(obj.get(tags::REQUESTING_PHYSICIAN), None);
3495
3496 let op = AttributeOp::new(
3498 tags::REQUESTING_PHYSICIAN,
3499 AttributeAction::SetStrIfMissing("Doctor^Anonymous".into()),
3500 );
3501
3502 obj.apply(op).unwrap();
3503
3504 assert_eq!(
3505 obj.get(tags::REQUESTING_PHYSICIAN),
3506 Some(&DataElement::new(
3507 tags::REQUESTING_PHYSICIAN,
3508 VR::PN,
3509 PrimitiveValue::from("Doctor^Anonymous"),
3510 ))
3511 );
3512 }
3513 {
3514 let mut obj = base_obj.clone();
3516 let op = AttributeOp::new(
3517 tags::REQUESTING_PHYSICIAN,
3518 AttributeAction::SetStr("Doctor^Anonymous".into()),
3519 );
3520
3521 obj.apply(op).unwrap();
3522
3523 assert_eq!(
3524 obj.get(tags::REQUESTING_PHYSICIAN),
3525 Some(&DataElement::new(
3526 tags::REQUESTING_PHYSICIAN,
3527 VR::PN,
3528 PrimitiveValue::from("Doctor^Anonymous"),
3529 ))
3530 );
3531 }
3532
3533 {
3534 let mut obj = base_obj.clone();
3536 let op = AttributeOp::new(
3537 tags::LOSSY_IMAGE_COMPRESSION_RATIO,
3538 AttributeAction::PushF64(1.25),
3539 );
3540
3541 obj.apply(op).unwrap();
3542
3543 assert_eq!(
3544 obj.get(tags::LOSSY_IMAGE_COMPRESSION_RATIO),
3545 Some(&DataElement::new(
3546 tags::LOSSY_IMAGE_COMPRESSION_RATIO,
3547 VR::DS,
3548 dicom_value!(Strs, ["5", "1.25"]),
3549 ))
3550 );
3551 }
3552 }
3553
3554 #[test]
3556 fn nested_inmem_ops() {
3557 let obj_1 = InMemDicomObject::from_element_iter([
3558 DataElement::new(Tag(0x0018, 0x6012), VR::US, PrimitiveValue::from(1_u16)),
3559 DataElement::new(Tag(0x0018, 0x6014), VR::US, PrimitiveValue::from(2_u16)),
3560 ]);
3561
3562 let obj_2 = InMemDicomObject::from_element_iter([DataElement::new(
3563 Tag(0x0018, 0x6012),
3564 VR::US,
3565 PrimitiveValue::from(4_u16),
3566 )]);
3567
3568 let mut main_obj = InMemDicomObject::from_element_iter(vec![
3569 DataElement::new(
3570 tags::SEQUENCE_OF_ULTRASOUND_REGIONS,
3571 VR::SQ,
3572 DataSetSequence::from(vec![obj_1, obj_2]),
3573 ),
3574 DataElement::new(Tag(0x0020, 0x4000), VR::LT, Value::Primitive("TEST".into())),
3575 ]);
3576
3577 let selector: AttributeSelector =
3578 (tags::SEQUENCE_OF_ULTRASOUND_REGIONS, 0, Tag(0x0018, 0x6014)).into();
3579
3580 main_obj
3581 .apply(AttributeOp::new(selector, AttributeAction::Set(3.into())))
3582 .unwrap();
3583
3584 assert_eq!(
3585 main_obj
3586 .get(tags::SEQUENCE_OF_ULTRASOUND_REGIONS)
3587 .unwrap()
3588 .items()
3589 .unwrap()[0]
3590 .get(Tag(0x0018, 0x6014))
3591 .unwrap()
3592 .value(),
3593 &PrimitiveValue::from(3).into(),
3594 );
3595
3596 let selector: AttributeSelector =
3597 (tags::SEQUENCE_OF_ULTRASOUND_REGIONS, 1, Tag(0x0018, 0x6012)).into();
3598
3599 main_obj
3600 .apply(AttributeOp::new(selector, AttributeAction::Remove))
3601 .unwrap();
3602
3603 assert_eq!(
3605 main_obj
3606 .get(tags::SEQUENCE_OF_ULTRASOUND_REGIONS)
3607 .unwrap()
3608 .items()
3609 .unwrap()[1]
3610 .tags()
3611 .collect::<Vec<_>>(),
3612 Vec::<Tag>::new(),
3613 );
3614
3615 assert!(matches!(
3617 main_obj.value_at((tags::SEQUENCE_OF_ULTRASOUND_REGIONS, 1, Tag(0x0018, 0x6012),)),
3618 Err(AtAccessError::MissingLeafElement { .. })
3619 ))
3620 }
3621
3622 #[test]
3624 fn constructive_op() {
3625 let mut obj = InMemDicomObject::from_element_iter([DataElement::new(
3626 tags::SEQUENCE_OF_ULTRASOUND_REGIONS,
3627 VR::SQ,
3628 DataSetSequence::empty(),
3629 )]);
3630
3631 let op = AttributeOp::new(
3632 (
3633 tags::SEQUENCE_OF_ULTRASOUND_REGIONS,
3634 0,
3635 tags::REGION_SPATIAL_FORMAT,
3636 ),
3637 AttributeAction::Set(5_u16.into()),
3638 );
3639
3640 obj.apply(op).unwrap();
3641
3642 assert_eq!(
3644 obj.get(tags::SEQUENCE_OF_ULTRASOUND_REGIONS)
3645 .unwrap()
3646 .items()
3647 .unwrap()
3648 .len(),
3649 1,
3650 );
3651
3652 assert_eq!(
3654 &obj.get(tags::SEQUENCE_OF_ULTRASOUND_REGIONS)
3655 .unwrap()
3656 .items()
3657 .unwrap()[0],
3658 &InMemDicomObject::from_element_iter([DataElement::new(
3659 tags::REGION_SPATIAL_FORMAT,
3660 VR::US,
3661 PrimitiveValue::from(5_u16)
3662 )]),
3663 );
3664
3665 assert_eq!(
3667 obj.value_at((
3668 tags::SEQUENCE_OF_ULTRASOUND_REGIONS,
3669 0,
3670 tags::REGION_SPATIAL_FORMAT
3671 ))
3672 .unwrap(),
3673 &Value::from(PrimitiveValue::from(5_u16)),
3674 )
3675 }
3676
3677 #[test]
3680 fn inmem_ops_can_create_seq() {
3681 let mut obj = InMemDicomObject::new_empty();
3682
3683 obj.apply(AttributeOp::new(
3684 tags::SEQUENCE_OF_ULTRASOUND_REGIONS,
3685 AttributeAction::SetIfMissing(PrimitiveValue::Empty),
3686 ))
3687 .unwrap();
3688
3689 {
3690 let sequence_ultrasound = obj
3692 .get(tags::SEQUENCE_OF_ULTRASOUND_REGIONS)
3693 .expect("should have sequence element");
3694
3695 assert_eq!(sequence_ultrasound.vr(), VR::SQ);
3696
3697 assert_eq!(sequence_ultrasound.items(), Some(&[][..]),);
3698 }
3699
3700 obj.apply(AttributeOp::new(
3701 (
3702 tags::SEQUENCE_OF_ULTRASOUND_REGIONS,
3703 tags::REGION_SPATIAL_FORMAT,
3704 ),
3705 AttributeAction::Set(1_u16.into()),
3706 ))
3707 .unwrap();
3708
3709 {
3710 assert_eq!(
3712 obj.get(tags::SEQUENCE_OF_ULTRASOUND_REGIONS)
3713 .unwrap()
3714 .items()
3715 .map(|items| items.len()),
3716 Some(1),
3717 );
3718 }
3719 }
3720
3721 #[test]
3724 fn inmem_ops_can_create_nested_attribute() {
3725 let mut obj = InMemDicomObject::new_empty();
3726
3727 obj.apply(AttributeOp::new(
3728 (
3729 tags::SEQUENCE_OF_ULTRASOUND_REGIONS,
3730 tags::REGION_SPATIAL_FORMAT,
3731 ),
3732 AttributeAction::Set(1_u16.into()),
3733 ))
3734 .unwrap();
3735
3736 {
3737 assert_eq!(
3739 obj.get(tags::SEQUENCE_OF_ULTRASOUND_REGIONS)
3740 .unwrap()
3741 .items()
3742 .map(|items| items.len()),
3743 Some(1),
3744 );
3745
3746 assert_eq!(
3748 obj.value_at((
3749 tags::SEQUENCE_OF_ULTRASOUND_REGIONS,
3750 tags::REGION_SPATIAL_FORMAT
3751 ))
3752 .unwrap(),
3753 &PrimitiveValue::from(1_u16).into(),
3754 );
3755
3756 assert_eq!(
3758 DicomObject::at(
3759 &obj,
3760 (
3761 tags::SEQUENCE_OF_ULTRASOUND_REGIONS,
3762 tags::REGION_SPATIAL_FORMAT
3763 )
3764 )
3765 .unwrap(),
3766 &PrimitiveValue::from(1_u16).into(),
3767 );
3768 }
3769 }
3770
3771 #[test]
3774 fn inmem_ops_can_truncate_seq() {
3775 let mut obj = InMemDicomObject::from_element_iter([
3776 DataElement::new(
3777 tags::SEQUENCE_OF_ULTRASOUND_REGIONS,
3778 VR::SQ,
3779 DataSetSequence::from(vec![InMemDicomObject::new_empty()]),
3780 ),
3781 DataElement::new_with_len(
3782 tags::PIXEL_DATA,
3783 VR::OB,
3784 Length::UNDEFINED,
3785 PixelFragmentSequence::new(vec![], vec![vec![0xcc; 8192], vec![0x55; 1024]]),
3786 ),
3787 ]);
3788
3789 obj.apply(AttributeOp::new(
3791 tags::SEQUENCE_OF_ULTRASOUND_REGIONS,
3792 AttributeAction::Truncate(0),
3793 ))
3794 .unwrap();
3795
3796 {
3797 let sequence_ultrasound = obj
3798 .get(tags::SEQUENCE_OF_ULTRASOUND_REGIONS)
3799 .expect("should have sequence element");
3800 assert_eq!(sequence_ultrasound.items(), Some(&[][..]),);
3801 }
3802
3803 obj.apply(AttributeOp::new(
3805 tags::PIXEL_DATA,
3806 AttributeAction::Truncate(1),
3807 ))
3808 .unwrap();
3809
3810 {
3811 assert_eq!(
3813 obj.get(tags::PIXEL_DATA)
3814 .unwrap()
3815 .fragments()
3816 .map(|fragments| fragments.len()),
3817 Some(1),
3818 );
3819 }
3820 }
3821
3822 #[test]
3823 fn inmem_obj_reset_defined_length() {
3824 let mut entries: BTreeMap<Tag, InMemElement<StandardDataDictionary>> = BTreeMap::new();
3825
3826 let patient_name =
3827 DataElement::new(tags::PATIENT_NAME, VR::CS, PrimitiveValue::from("Doe^John"));
3828
3829 let study_description = DataElement::new(
3830 tags::STUDY_DESCRIPTION,
3831 VR::LO,
3832 PrimitiveValue::from("Test study"),
3833 );
3834
3835 entries.insert(tags::PATIENT_NAME, patient_name.clone());
3836
3837 let obj = InMemDicomObject::<StandardDataDictionary> {
3839 entries,
3840 dict: StandardDataDictionary,
3841 len: Length(1),
3842 charset_changed: false,
3843 };
3844
3845 assert!(obj.length().is_defined());
3846
3847 let mut o = obj.clone();
3848 o.put_element(study_description);
3849 assert!(o.length().is_undefined());
3850
3851 let mut o = obj.clone();
3852 o.remove_element(tags::PATIENT_NAME);
3853 assert!(o.length().is_undefined());
3854
3855 let mut o = obj.clone();
3856 o.remove_element_by_name("PatientName").unwrap();
3857 assert!(o.length().is_undefined());
3858
3859 let mut o = obj.clone();
3860 o.take_element(tags::PATIENT_NAME).unwrap();
3861 assert!(o.length().is_undefined());
3862
3863 let mut o = obj.clone();
3864 o.take_element_by_name("PatientName").unwrap();
3865 assert!(o.length().is_undefined());
3866
3867 let mut o = obj.clone();
3869 o.retain(|e| e.tag() == tags::PATIENT_NAME);
3870 assert!(o.length().is_undefined());
3871
3872 let mut o = obj.clone();
3873 o.apply(AttributeOp::new(
3874 tags::PATIENT_NAME,
3875 AttributeAction::Remove,
3876 ))
3877 .unwrap();
3878 assert!(o.length().is_undefined());
3879
3880 let mut o = obj.clone();
3881 o.apply(AttributeOp::new(tags::PATIENT_NAME, AttributeAction::Empty))
3882 .unwrap();
3883 assert!(o.length().is_undefined());
3884
3885 let mut o = obj.clone();
3886 o.apply(AttributeOp::new(
3887 tags::PATIENT_NAME,
3888 AttributeAction::SetVr(VR::IS),
3889 ))
3890 .unwrap();
3891 assert!(o.length().is_undefined());
3892
3893 let mut o = obj.clone();
3894 o.apply(AttributeOp::new(
3895 tags::PATIENT_NAME,
3896 AttributeAction::Set(dicom_value!(Str, "Unknown")),
3897 ))
3898 .unwrap();
3899 assert!(o.length().is_undefined());
3900
3901 let mut o = obj.clone();
3902 o.apply(AttributeOp::new(
3903 tags::PATIENT_NAME,
3904 AttributeAction::SetStr("Patient^Anonymous".into()),
3905 ))
3906 .unwrap();
3907 assert!(o.length().is_undefined());
3908
3909 let mut o = obj.clone();
3910 o.apply(AttributeOp::new(
3911 tags::PATIENT_AGE,
3912 AttributeAction::SetIfMissing(dicom_value!(75)),
3913 ))
3914 .unwrap();
3915 assert!(o.length().is_undefined());
3916
3917 let mut o = obj.clone();
3918 o.apply(AttributeOp::new(
3919 tags::PATIENT_ADDRESS,
3920 AttributeAction::SetStrIfMissing("Chicago".into()),
3921 ))
3922 .unwrap();
3923 assert!(o.length().is_undefined());
3924
3925 let mut o = obj.clone();
3926 o.apply(AttributeOp::new(
3927 tags::PATIENT_NAME,
3928 AttributeAction::Replace(dicom_value!(Str, "Unknown")),
3929 ))
3930 .unwrap();
3931 assert!(o.length().is_undefined());
3932
3933 let mut o = obj.clone();
3934 o.apply(AttributeOp::new(
3935 tags::PATIENT_NAME,
3936 AttributeAction::ReplaceStr("Unknown".into()),
3937 ))
3938 .unwrap();
3939 assert!(o.length().is_undefined());
3940
3941 let mut o = obj.clone();
3942 o.apply(AttributeOp::new(
3943 tags::PATIENT_NAME,
3944 AttributeAction::PushStr("^Prof".into()),
3945 ))
3946 .unwrap();
3947 assert!(o.length().is_undefined());
3948
3949 let mut o = obj.clone();
3950 o.apply(AttributeOp::new(
3951 tags::PATIENT_NAME,
3952 AttributeAction::PushI32(-16),
3953 ))
3954 .unwrap();
3955 assert!(o.length().is_undefined());
3956
3957 let mut o = obj.clone();
3958 o.apply(AttributeOp::new(
3959 tags::PATIENT_NAME,
3960 AttributeAction::PushU32(16),
3961 ))
3962 .unwrap();
3963 assert!(o.length().is_undefined());
3964
3965 let mut o = obj.clone();
3966 o.apply(AttributeOp::new(
3967 tags::PATIENT_NAME,
3968 AttributeAction::PushI16(-16),
3969 ))
3970 .unwrap();
3971 assert!(o.length().is_undefined());
3972
3973 let mut o = obj.clone();
3974 o.apply(AttributeOp::new(
3975 tags::PATIENT_NAME,
3976 AttributeAction::PushU16(16),
3977 ))
3978 .unwrap();
3979 assert!(o.length().is_undefined());
3980
3981 let mut o = obj.clone();
3982 o.apply(AttributeOp::new(
3983 tags::PATIENT_NAME,
3984 AttributeAction::PushF32(16.16),
3985 ))
3986 .unwrap();
3987 assert!(o.length().is_undefined());
3988
3989 let mut o = obj.clone();
3990 o.apply(AttributeOp::new(
3991 tags::PATIENT_NAME,
3992 AttributeAction::PushF64(16.1616),
3993 ))
3994 .unwrap();
3995 assert!(o.length().is_undefined());
3996 }
3997
3998 #[test]
3999 fn create_commands() {
4000 let obj = InMemDicomObject::command_from_element_iter([]);
4002 assert_eq!(
4003 obj.get(tags::COMMAND_GROUP_LENGTH)
4004 .map(|e| e.value().to_int::<u32>().unwrap()),
4005 Some(0)
4006 );
4007
4008 let obj = InMemDicomObject::command_from_element_iter([
4010 DataElement::new(
4012 tags::AFFECTED_SOP_CLASS_UID,
4013 VR::UI,
4014 PrimitiveValue::from("1.2.840.10008.5.1.4.1.2.1.1"),
4015 ),
4016 DataElement::new(
4018 tags::COMMAND_FIELD,
4019 VR::US,
4020 dicom_value!(U16, [0x0020]),
4022 ),
4023 DataElement::new(tags::MESSAGE_ID, VR::US, dicom_value!(U16, [0])),
4025 DataElement::new(
4027 tags::PRIORITY,
4028 VR::US,
4029 dicom_value!(U16, [0x0000]),
4031 ),
4032 DataElement::new(
4034 tags::COMMAND_DATA_SET_TYPE,
4035 VR::US,
4036 dicom_value!(U16, [0x0001]),
4037 ),
4038 ]);
4039 assert_eq!(
4040 obj.get(tags::COMMAND_GROUP_LENGTH)
4041 .map(|e| e.value().to_int::<u32>().unwrap()),
4042 Some(76)
4043 );
4044
4045 let storage_sop_class_uid = "1.2.840.10008.5.1.4.1.1.4";
4046 let storage_sop_instance_uid = "2.25.221314879990624101283043547144116927116";
4047
4048 let obj = InMemDicomObject::command_from_element_iter([
4050 DataElement::new(
4052 tags::COMMAND_GROUP_LENGTH,
4053 VR::UL,
4054 PrimitiveValue::from(9999_u32),
4055 ),
4056 DataElement::new(
4058 tags::AFFECTED_SOP_CLASS_UID,
4059 VR::UI,
4060 dicom_value!(Str, storage_sop_class_uid),
4061 ),
4062 DataElement::new(tags::COMMAND_FIELD, VR::US, dicom_value!(U16, [0x0001])),
4064 DataElement::new(tags::MESSAGE_ID, VR::US, dicom_value!(U16, [1])),
4066 DataElement::new(tags::PRIORITY, VR::US, dicom_value!(U16, [0x0000])),
4068 DataElement::new(
4070 tags::COMMAND_DATA_SET_TYPE,
4071 VR::US,
4072 dicom_value!(U16, [0x0000]),
4073 ),
4074 DataElement::new(
4076 tags::AFFECTED_SOP_INSTANCE_UID,
4077 VR::UI,
4078 dicom_value!(Str, storage_sop_instance_uid),
4079 ),
4080 ]);
4081
4082 assert_eq!(
4083 obj.get(tags::COMMAND_GROUP_LENGTH)
4084 .map(|e| e.value().to_int::<u32>().unwrap()),
4085 Some(126)
4086 );
4087 }
4088
4089 #[test]
4090 fn test_even_len() {
4091 assert_eq!(even_len(0), 0);
4092 assert_eq!(even_len(1), 2);
4093 assert_eq!(even_len(2), 2);
4094 assert_eq!(even_len(3), 4);
4095 assert_eq!(even_len(4), 4);
4096 assert_eq!(even_len(5), 6);
4097 }
4098
4099 #[test]
4100 fn can_update_value() {
4101 let mut obj = InMemDicomObject::from_element_iter([DataElement::new(
4102 tags::ANATOMIC_REGION_SEQUENCE,
4103 VR::SQ,
4104 DataSetSequence::empty(),
4105 )]);
4106 assert_eq!(
4107 obj.get(tags::ANATOMIC_REGION_SEQUENCE).map(|e| e.length()),
4108 Some(Length(0)),
4109 );
4110
4111 assert!(!obj.update_value(tags::BURNED_IN_ANNOTATION, |_value| {
4112 panic!("should not be called")
4113 }),);
4114
4115 let o = obj.update_value(tags::ANATOMIC_REGION_SEQUENCE, |value| {
4116 let items = value.items_mut().unwrap();
4118 items.push(InMemDicomObject::from_element_iter([DataElement::new(
4119 tags::INSTANCE_NUMBER,
4120 VR::IS,
4121 PrimitiveValue::from(1),
4122 )]));
4123 });
4124 assert!(o);
4125
4126 assert!(obj
4127 .get(tags::ANATOMIC_REGION_SEQUENCE)
4128 .unwrap()
4129 .length()
4130 .is_undefined());
4131 }
4132
4133 #[test]
4134 fn deep_sequence_change_encoding_writes_undefined_sequence_length() {
4135 use smallvec::smallvec;
4136
4137 let obj_1 = InMemDicomObject::from_element_iter(vec![
4138 DataElement::new(
4140 tags::STUDY_DESCRIPTION,
4141 VR::SL,
4142 Value::Primitive("MORFOLOGÍA Y FUNCIÓN".into()),
4143 ),
4144 DataElement::new(
4146 tags::SERIES_DESCRIPTION,
4147 VR::SL,
4148 Value::Primitive("0123456789".into()),
4149 ),
4150 ]);
4151
4152 let some_tag = Tag(0x0018, 0x6011);
4153
4154 let inner_sequence = InMemDicomObject::from_element_iter(vec![DataElement::new(
4155 some_tag,
4156 VR::SQ,
4157 Value::from(DataSetSequence::new(
4158 smallvec![obj_1],
4159 Length(30), )),
4161 )]);
4162 let outer_sequence = DataElement::new(
4163 some_tag,
4164 VR::SQ,
4165 Value::from(DataSetSequence::new(
4166 smallvec![inner_sequence.clone(), inner_sequence],
4167 Length(60), )),
4169 );
4170
4171 let original_object = InMemDicomObject::from_element_iter(vec![
4172 DataElement::new(tags::SPECIFIC_CHARACTER_SET, VR::CS, "ISO_IR 100"),
4173 outer_sequence,
4174 ]);
4175
4176 assert_eq!(
4177 original_object
4178 .get(some_tag)
4179 .expect("object should be present")
4180 .length(),
4181 Length(60)
4182 );
4183
4184 let mut changed_charset = original_object.clone();
4185 changed_charset.convert_to_utf8();
4186 assert!(changed_charset.charset_changed);
4187
4188 use dicom_parser::dataset::DataToken as token;
4189 let options = IntoTokensOptions::new(true);
4190 let converted_tokens: Vec<_> = changed_charset.into_tokens_with_options(options).collect();
4191
4192 assert_eq!(
4193 vec![
4194 token::ElementHeader(DataElementHeader {
4195 tag: Tag(0x0008, 0x0005),
4196 vr: VR::CS,
4197 len: Length(10),
4198 }),
4199 token::PrimitiveValue("ISO_IR 192".into()),
4200 token::SequenceStart {
4201 tag: Tag(0x0018, 0x6011),
4202 len: Length::UNDEFINED,
4203 },
4204 token::ItemStart {
4205 len: Length::UNDEFINED
4206 },
4207 token::SequenceStart {
4208 tag: Tag(0x0018, 0x6011),
4209 len: Length::UNDEFINED,
4210 },
4211 token::ItemStart {
4212 len: Length::UNDEFINED
4213 },
4214 token::ElementHeader(DataElementHeader {
4215 tag: Tag(0x0008, 0x1030),
4216 vr: VR::SL,
4217 len: Length(22),
4218 }),
4219 token::PrimitiveValue("MORFOLOGÍA Y FUNCIÓN".into()),
4220 token::ElementHeader(DataElementHeader {
4221 tag: Tag(0x0008, 0x103E),
4222 vr: VR::SL,
4223 len: Length(10),
4224 }),
4225 token::PrimitiveValue("0123456789".into()),
4226 token::ItemEnd,
4227 token::SequenceEnd,
4228 token::ItemEnd,
4229 token::ItemStart {
4230 len: Length::UNDEFINED
4231 },
4232 token::SequenceStart {
4233 tag: Tag(0x0018, 0x6011),
4234 len: Length::UNDEFINED,
4235 },
4236 token::ItemStart {
4237 len: Length::UNDEFINED
4238 },
4239 token::ElementHeader(DataElementHeader {
4240 tag: Tag(0x0008, 0x1030),
4241 vr: VR::SL,
4242 len: Length(22),
4243 }),
4244 token::PrimitiveValue("MORFOLOGÍA Y FUNCIÓN".into()),
4245 token::ElementHeader(DataElementHeader {
4246 tag: Tag(0x0008, 0x103E),
4247 vr: VR::SL,
4248 len: Length(10),
4249 }),
4250 token::PrimitiveValue("0123456789".into()),
4251 token::ItemEnd,
4252 token::SequenceEnd,
4253 token::ItemEnd,
4254 token::SequenceEnd,
4255 ],
4256 converted_tokens
4257 );
4258 }
4259
4260 #[test]
4261 fn private_elements() {
4262 let mut ds = InMemDicomObject::from_element_iter(vec![
4263 DataElement::new(
4264 Tag(0x0009, 0x0010),
4265 VR::LO,
4266 PrimitiveValue::from("CREATOR 1"),
4267 ),
4268 DataElement::new(
4269 Tag(0x0009, 0x0011),
4270 VR::LO,
4271 PrimitiveValue::from("CREATOR 2"),
4272 ),
4273 DataElement::new(
4274 Tag(0x0011, 0x0010),
4275 VR::LO,
4276 PrimitiveValue::from("CREATOR 3"),
4277 ),
4278 ]);
4279 ds.put_private_element(
4280 0x0009,
4281 "CREATOR 1",
4282 0x01,
4283 VR::DS,
4284 PrimitiveValue::Str("1.0".to_string()),
4285 )
4286 .unwrap();
4287 ds.put_private_element(
4288 0x0009,
4289 "CREATOR 4",
4290 0x02,
4291 VR::DS,
4292 PrimitiveValue::Str("1.0".to_string()),
4293 )
4294 .unwrap();
4295
4296 let res = ds.put_private_element(
4297 0x0012,
4298 "CREATOR 4",
4299 0x02,
4300 VR::DS,
4301 PrimitiveValue::Str("1.0".to_string()),
4302 );
4303 assert_eq!(
4304 &res.err().unwrap().to_string(),
4305 "Group number must be odd, found 0x0012"
4306 );
4307
4308 assert_eq!(
4309 ds.private_element(0x0009, "CREATOR 1", 0x01)
4310 .unwrap()
4311 .value()
4312 .to_str()
4313 .unwrap(),
4314 "1.0"
4315 );
4316 assert_eq!(
4317 ds.private_element(0x0009, "CREATOR 4", 0x02)
4318 .unwrap()
4319 .value()
4320 .to_str()
4321 .unwrap(),
4322 "1.0"
4323 );
4324 assert_eq!(
4325 ds.private_element(0x0009, "CREATOR 4", 0x02)
4326 .unwrap()
4327 .header()
4328 .tag(),
4329 Tag(0x0009, 0x1202)
4330 );
4331 }
4332
4333 #[test]
4334 fn private_element_group_full() {
4335 let mut ds = InMemDicomObject::from_element_iter(
4336 (0..=0x00FFu16)
4337 .map(|i| {
4338 DataElement::new(Tag(0x0009, i), VR::LO, PrimitiveValue::from("CREATOR 1"))
4339 })
4340 .collect::<Vec<DataElement<_>>>(),
4341 );
4342 let res = ds.put_private_element(0x0009, "TEST", 0x01, VR::DS, PrimitiveValue::from("1.0"));
4343 assert_eq!(
4344 res.err().unwrap().to_string(),
4345 "No space available in group 0x0009"
4346 );
4347 }
4348}