use itertools::Itertools;
use smallvec::SmallVec;
use snafu::{OptionExt, ResultExt};
use std::fs::File;
use std::io::{BufReader, Read};
use std::path::Path;
use std::{collections::BTreeMap, io::Write};
use crate::{meta::FileMetaTable, FileMetaTableBuilder};
use crate::{
BuildMetaTable, CreateParser, CreatePrinter, DicomObject, FileDicomObject, MissingElementValue,
NoSuchAttributeName, NoSuchDataElementAlias, NoSuchDataElementTag, OpenFile, ParseMetaDataSet,
PrematureEnd, PrepareMetaTable, PrintDataSet, ReadFile, ReadToken, Result, UnexpectedToken,
UnsupportedTransferSyntax,
};
use dicom_core::dictionary::{DataDictionary, DictionaryEntry};
use dicom_core::header::{HasLength, Header};
use dicom_core::value::{Value, C};
use dicom_core::{DataElement, Length, Tag, VR};
use dicom_dictionary_std::StandardDataDictionary;
use dicom_encoding::transfer_syntax::TransferSyntaxIndex;
use dicom_encoding::{
encode::EncodeTo,
text::{SpecificCharacterSet, TextCodec},
TransferSyntax,
};
use dicom_parser::dataset::{DataSetReader, DataToken};
use dicom_parser::{
dataset::{read::Error as ParserError, DataSetWriter, IntoTokens},
StatefulDecode,
};
use dicom_transfer_syntax_registry::TransferSyntaxRegistry;
pub type InMemElement<D = StandardDataDictionary> = DataElement<InMemDicomObject<D>, InMemFragment>;
pub type InMemFragment = Vec<u8>;
type ParserResult<T> = std::result::Result<T, ParserError>;
#[derive(Debug, Clone)]
pub struct InMemDicomObject<D = StandardDataDictionary> {
entries: BTreeMap<Tag, InMemElement<D>>,
dict: D,
len: Length,
}
impl<'s, D> PartialEq for InMemDicomObject<D> {
fn eq(&self, other: &Self) -> bool {
self.entries == other.entries
}
}
impl<D> HasLength for InMemDicomObject<D> {
fn length(&self) -> Length {
self.len
}
}
impl<'s, D: 's> DicomObject for &'s InMemDicomObject<D>
where
D: DataDictionary,
D: Clone,
{
type Element = &'s InMemElement<D>;
fn element(&self, tag: Tag) -> Result<Self::Element> {
self.entries.get(&tag).context(NoSuchDataElementTag { tag })
}
fn element_by_name(&self, name: &str) -> Result<Self::Element> {
let tag = self.lookup_name(name)?;
self.element(tag)
}
}
impl FileDicomObject<InMemDicomObject<StandardDataDictionary>> {
pub fn open_file<P: AsRef<Path>>(path: P) -> Result<Self> {
Self::open_file_with_dict(path, StandardDataDictionary)
}
pub fn from_reader<S>(src: S) -> Result<Self>
where
S: Read,
{
Self::from_reader_with_dict(src, StandardDataDictionary)
}
}
impl InMemDicomObject<StandardDataDictionary> {
pub fn create_empty() -> Self {
InMemDicomObject {
entries: BTreeMap::new(),
dict: StandardDataDictionary,
len: Length::UNDEFINED,
}
}
#[inline]
pub fn from_element_source<I>(iter: I) -> Result<Self>
where
I: IntoIterator<Item = Result<InMemElement<StandardDataDictionary>>>,
{
Self::from_element_source_with_dict(iter, StandardDataDictionary)
}
#[inline]
pub fn from_element_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = InMemElement<StandardDataDictionary>>,
{
Self::from_iter_with_dict(iter, StandardDataDictionary)
}
#[inline]
pub fn read_dataset<S>(decoder: S) -> Result<Self>
where
S: StatefulDecode,
{
Self::read_dataset_with_dict(decoder, StandardDataDictionary)
}
#[inline]
pub fn read_dataset_with_ts_cs<S>(
from: S,
ts: &TransferSyntax,
cs: SpecificCharacterSet,
) -> Result<Self>
where
S: Read,
{
Self::read_dataset_with_dict_ts_cs(from, StandardDataDictionary, ts, cs)
}
#[inline]
pub fn read_dataset_with_ts<S>(from: S, ts: &TransferSyntax) -> Result<Self>
where
S: Read,
{
Self::read_dataset_with_dict_ts_cs(
from,
StandardDataDictionary,
ts,
SpecificCharacterSet::Default,
)
}
}
impl<D> FileDicomObject<InMemDicomObject<D>>
where
D: DataDictionary,
D: Clone,
{
pub fn new_empty_with_dict_and_meta(dict: D, meta: FileMetaTable) -> Self {
FileDicomObject {
meta,
obj: InMemDicomObject {
entries: BTreeMap::new(),
dict,
len: Length::UNDEFINED,
},
}
}
pub fn open_file_with_dict<P: AsRef<Path>>(path: P, dict: D) -> Result<Self> {
Self::open_file_with(path, dict, TransferSyntaxRegistry)
}
pub fn open_file_with<P: AsRef<Path>, R>(path: P, dict: D, ts_index: R) -> Result<Self>
where
P: AsRef<Path>,
R: TransferSyntaxIndex,
{
let path = path.as_ref();
let mut file =
BufReader::new(File::open(path).with_context(|| OpenFile { filename: path })?);
{
let mut buf = [0u8; 128];
file.read_exact(&mut buf)
.with_context(|| ReadFile { filename: path })?;
}
let meta = FileMetaTable::from_reader(&mut file).context(ParseMetaDataSet)?;
if let Some(ts) = ts_index.get(&meta.transfer_syntax) {
let cs = SpecificCharacterSet::Default;
let mut dataset =
DataSetReader::new_with_dictionary(file, dict.clone(), ts, cs, Default::default())
.context(CreateParser)?;
Ok(FileDicomObject {
meta,
obj: InMemDicomObject::build_object(&mut dataset, dict, false, Length::UNDEFINED)?,
})
} else {
UnsupportedTransferSyntax {
uid: meta.transfer_syntax,
}
.fail()
}
}
pub fn from_reader_with_dict<S>(src: S, dict: D) -> Result<Self>
where
S: Read,
{
Self::from_reader_with(src, dict, TransferSyntaxRegistry)
}
pub fn from_reader_with<'s, S: 's, R>(src: S, dict: D, ts_index: R) -> Result<Self>
where
S: Read,
R: TransferSyntaxIndex,
{
let mut file = BufReader::new(src);
let meta = FileMetaTable::from_reader(&mut file).context(ParseMetaDataSet)?;
if let Some(ts) = ts_index.get(&meta.transfer_syntax) {
let cs = SpecificCharacterSet::Default;
let mut dataset =
DataSetReader::new_with_dictionary(file, dict.clone(), ts, cs, Default::default())
.context(CreateParser)?;
let obj = InMemDicomObject::build_object(&mut dataset, dict, false, Length::UNDEFINED)?;
Ok(FileDicomObject { meta, obj })
} else {
UnsupportedTransferSyntax {
uid: meta.transfer_syntax,
}
.fail()
}
}
}
impl FileDicomObject<InMemDicomObject<StandardDataDictionary>> {
pub fn new_empty_with_meta(meta: FileMetaTable) -> Self {
FileDicomObject {
meta,
obj: InMemDicomObject {
entries: BTreeMap::new(),
dict: StandardDataDictionary,
len: Length::UNDEFINED,
},
}
}
}
impl<D> InMemDicomObject<D>
where
D: DataDictionary,
D: Clone,
{
pub fn new_empty_with_dict(dict: D) -> Self {
InMemDicomObject {
entries: BTreeMap::new(),
dict,
len: Length::UNDEFINED,
}
}
pub fn from_element_source_with_dict<I>(iter: I, dict: D) -> Result<Self>
where
I: IntoIterator<Item = Result<InMemElement<D>>>,
{
let entries: Result<_> = iter.into_iter().map_ok(|e| (e.tag(), e)).collect();
Ok(InMemDicomObject {
entries: entries?,
dict,
len: Length::UNDEFINED,
})
}
pub fn from_iter_with_dict<I>(iter: I, dict: D) -> Self
where
I: IntoIterator<Item = InMemElement<D>>,
{
let entries = iter.into_iter().map(|e| (e.tag(), e)).collect();
InMemDicomObject {
entries,
dict,
len: Length::UNDEFINED,
}
}
pub fn read_dataset_with_dict<S>(decoder: S, dict: D) -> Result<Self>
where
S: StatefulDecode,
D: DataDictionary,
{
let mut dataset = DataSetReader::new(decoder, Default::default());
InMemDicomObject::build_object(&mut dataset, dict, false, Length::UNDEFINED)
}
#[inline]
pub fn read_dataset_with_dict_ts<S>(from: S, dict: D, ts: &TransferSyntax) -> Result<Self>
where
S: Read,
D: DataDictionary,
{
Self::read_dataset_with_dict_ts_cs(from, dict, ts, SpecificCharacterSet::Default)
}
pub fn read_dataset_with_dict_ts_cs<S>(
from: S,
dict: D,
ts: &TransferSyntax,
cs: SpecificCharacterSet,
) -> Result<Self>
where
S: Read,
D: DataDictionary,
{
let from = BufReader::new(from);
let mut dataset =
DataSetReader::new_with_dictionary(from, dict.clone(), ts, cs, Default::default())
.context(CreateParser)?;
InMemDicomObject::build_object(&mut dataset, dict, false, Length::UNDEFINED)
}
pub fn meta(&self) -> Option<&FileMetaTable> {
None
}
pub fn element(&self, tag: Tag) -> Result<&InMemElement<D>> {
self.entries.get(&tag).context(NoSuchDataElementTag { tag })
}
pub fn element_by_name(&self, name: &str) -> Result<&InMemElement<D>> {
let tag = self.lookup_name(name)?;
self.entries
.get(&tag)
.with_context(|| NoSuchDataElementAlias {
tag,
alias: name.to_string(),
})
}
pub fn put(&mut self, elt: InMemElement<D>) -> Option<InMemElement<D>> {
self.entries.insert(elt.tag(), elt)
}
pub fn take_element(&mut self, tag: Tag) -> Result<InMemElement<D>> {
self.entries
.remove(&tag)
.context(NoSuchDataElementTag { tag })
}
pub fn take_element_by_name(&mut self, name: &str) -> Result<InMemElement<D>> {
let tag = self.lookup_name(name)?;
self.entries
.remove(&tag)
.with_context(|| NoSuchDataElementAlias {
tag,
alias: name.to_string(),
})
}
pub fn write_dataset<W, E, T>(&self, to: W, encoder: E, text_encoder: T) -> Result<()>
where
W: Write,
E: EncodeTo<W>,
T: TextCodec,
{
let mut dset_writer = DataSetWriter::new(to, encoder, text_encoder);
dset_writer
.write_sequence(self.into_tokens())
.context(PrintDataSet)?;
Ok(())
}
pub fn write_dataset_with_ts_cs<W>(
&self,
to: W,
ts: &TransferSyntax,
cs: SpecificCharacterSet,
) -> Result<()>
where
W: Write,
{
let mut dset_writer = DataSetWriter::with_ts_cs(to, ts, cs).context(CreatePrinter)?;
dset_writer
.write_sequence(self.into_tokens())
.context(PrintDataSet)?;
Ok(())
}
pub fn write_dataset_with_ts<W>(&self, to: W, ts: &TransferSyntax) -> Result<()>
where
W: Write,
{
self.write_dataset_with_ts_cs(to, ts, SpecificCharacterSet::Default)
}
pub fn with_exact_meta(self, meta: FileMetaTable) -> FileDicomObject<Self> {
FileDicomObject { meta, obj: self }
}
pub fn with_meta(self, mut meta: FileMetaTableBuilder) -> Result<FileDicomObject<Self>> {
match self.element(Tag(0x0008, 0x0008)) {
Ok(elem) => {
meta = meta.media_storage_sop_instance_uid(
elem.value().to_str().context(PrepareMetaTable)?,
);
}
Err(crate::Error::NoSuchDataElementTag { .. }) => {}
Err(err) => return Err(err),
}
Ok(FileDicomObject {
meta: meta.build().context(BuildMetaTable)?,
obj: self,
})
}
fn build_object<I: ?Sized>(dataset: &mut I, dict: D, in_item: bool, len: Length) -> Result<Self>
where
I: Iterator<Item = ParserResult<DataToken>>,
{
let mut entries: BTreeMap<Tag, InMemElement<D>> = BTreeMap::new();
while let Some(token) = dataset.next() {
let elem = match token.context(ReadToken)? {
DataToken::PixelSequenceStart => {
let value = InMemDicomObject::build_encapsulated_data(&mut *dataset)?;
DataElement::new(Tag(0x7fe0, 0x0010), VR::OB, value)
}
DataToken::ElementHeader(header) => {
let next_token = dataset.next().context(MissingElementValue)?;
match next_token.context(ReadToken)? {
DataToken::PrimitiveValue(v) => {
InMemElement::new(header.tag, header.vr, Value::Primitive(v))
}
token => {
return UnexpectedToken { token }.fail();
}
}
}
DataToken::SequenceStart { tag, len } => {
let items = Self::build_sequence(tag, len, &mut *dataset, &dict)?;
DataElement::new(tag, VR::SQ, Value::Sequence { items, size: len })
}
DataToken::ItemEnd if in_item => {
return Ok(InMemDicomObject { entries, dict, len });
}
token => return UnexpectedToken { token }.fail(),
};
entries.insert(elem.tag(), elem);
}
Ok(InMemDicomObject { entries, dict, len })
}
fn build_encapsulated_data<I>(
mut dataset: I,
) -> Result<Value<InMemDicomObject<D>, InMemFragment>>
where
I: Iterator<Item = ParserResult<DataToken>>,
{
let mut offset_table = None;
let mut fragments = C::new();
while let Some(token) = dataset.next() {
match token.context(ReadToken)? {
DataToken::ItemValue(data) => {
if offset_table.is_none() {
offset_table = Some(data.into());
} else {
fragments.push(data);
}
}
DataToken::ItemEnd => {
if offset_table.is_none() {
offset_table = Some(C::new())
}
}
DataToken::ItemStart { len: _ } => { }
DataToken::SequenceEnd => {
break;
}
token @ DataToken::ElementHeader(_)
| token @ DataToken::PixelSequenceStart
| token @ DataToken::SequenceStart { .. }
| token @ DataToken::PrimitiveValue(_) => {
return UnexpectedToken { token }.fail();
}
}
}
Ok(Value::PixelSequence {
fragments,
offset_table: offset_table.unwrap_or_default(),
})
}
fn build_sequence<I: ?Sized>(
_tag: Tag,
_len: Length,
dataset: &mut I,
dict: &D,
) -> Result<C<InMemDicomObject<D>>>
where
I: Iterator<Item = ParserResult<DataToken>>,
{
let mut items: C<_> = SmallVec::new();
while let Some(token) = dataset.next() {
match token.context(ReadToken)? {
DataToken::ItemStart { len } => {
items.push(Self::build_object(&mut *dataset, dict.clone(), true, len)?);
}
DataToken::SequenceEnd => {
return Ok(items);
}
token => return UnexpectedToken { token }.fail(),
};
}
PrematureEnd.fail()
}
fn lookup_name(&self, name: &str) -> Result<Tag> {
self.dict
.by_name(name)
.context(NoSuchAttributeName { name })
.map(|e| e.tag())
}
}
impl<'a, D> IntoIterator for &'a InMemDicomObject<D> {
type Item = &'a InMemElement<D>;
type IntoIter = ::std::collections::btree_map::Values<'a, Tag, InMemElement<D>>;
fn into_iter(self) -> Self::IntoIter {
self.entries.values()
}
}
impl<D> IntoIterator for InMemDicomObject<D> {
type Item = InMemElement<D>;
type IntoIter = Iter<D>;
fn into_iter(self) -> Self::IntoIter {
Iter {
inner: self.entries.into_iter(),
}
}
}
#[derive(Debug)]
pub struct Iter<D> {
inner: ::std::collections::btree_map::IntoIter<Tag, InMemElement<D>>,
}
impl<D> Iterator for Iter<D> {
type Item = InMemElement<D>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|x| x.1)
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
fn count(self) -> usize {
self.inner.count()
}
}
impl<D> Extend<InMemElement<D>> for InMemDicomObject<D> {
fn extend<I>(&mut self, iter: I)
where
I: IntoIterator<Item = InMemElement<D>>,
{
self.entries.extend(iter.into_iter().map(|e| (e.tag(), e)))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{meta::FileMetaTableBuilder, open_file, Error};
use byteordered::Endianness;
use dicom_core::value::PrimitiveValue;
use dicom_core::{
dicom_value,
header::{DataElementHeader, Length, VR},
};
use dicom_encoding::{
decode::{basic::BasicDecoder, implicit_le::ImplicitVRLittleEndianDecoder},
encode::EncoderFor,
text::DefaultCharacterSetCodec,
transfer_syntax::implicit_le::ImplicitVRLittleEndianEncoder,
};
use dicom_parser::{dataset::IntoTokens, StatefulDecoder};
use tempfile;
fn assert_obj_eq<D>(obj1: &InMemDicomObject<D>, obj2: &InMemDicomObject<D>)
where
D: std::fmt::Debug,
{
assert_eq!(format!("{:?}", obj1), format!("{:?}", obj2))
}
#[test]
fn inmem_object_compare() {
let mut obj1 = InMemDicomObject::create_empty();
let mut obj2 = InMemDicomObject::create_empty();
assert_eq!(obj1, obj2);
let empty_patient_name = DataElement::empty(Tag(0x0010, 0x0010), VR::PN);
obj1.put(empty_patient_name.clone());
assert_ne!(obj1, obj2);
obj2.put(empty_patient_name.clone());
assert_obj_eq(&obj1, &obj2);
}
#[test]
fn inmem_object_read_dataset() {
let data_in = [
0x10, 0x00, 0x10, 0x00,
0x08, 0x00, 0x00, 0x00,
b'D', b'o', b'e', b'^', b'J', b'o', b'h', b'n',
];
let decoder = ImplicitVRLittleEndianDecoder::default();
let text = Box::new(DefaultCharacterSetCodec) as Box<_>;
let mut cursor = &data_in[..];
let parser = StatefulDecoder::new(
&mut cursor,
decoder,
BasicDecoder::new(Endianness::Little),
text,
);
let obj = InMemDicomObject::read_dataset(parser).unwrap();
let mut gt = InMemDicomObject::create_empty();
let patient_name = DataElement::new(
Tag(0x0010, 0x0010),
VR::PN,
dicom_value!(Strs, ["Doe^John"]),
);
gt.put(patient_name);
assert_eq!(obj, gt);
}
#[test]
fn inmem_object_read_dataset_with_ts_cs() {
let data_in = [
0x10, 0x00, 0x10, 0x00,
0x08, 0x00, 0x00, 0x00,
b'D', b'o', b'e', b'^', b'J', b'o', b'h', b'n',
];
let ts = TransferSyntaxRegistry.get("1.2.840.10008.1.2").unwrap();
let cs = SpecificCharacterSet::Default;
let mut cursor = &data_in[..];
let obj = InMemDicomObject::read_dataset_with_dict_ts_cs(
&mut cursor,
StandardDataDictionary,
&ts,
cs,
)
.unwrap();
let mut gt = InMemDicomObject::create_empty();
let patient_name = DataElement::new(
Tag(0x0010, 0x0010),
VR::PN,
dicom_value!(Strs, ["Doe^John"]),
);
gt.put(patient_name);
assert_eq!(obj, gt);
}
#[test]
fn inmem_object_write_dataset() {
let mut obj = InMemDicomObject::create_empty();
let patient_name =
DataElement::new(Tag(0x0010, 0x0010), VR::PN, dicom_value!(Str, "Doe^John"));
obj.put(patient_name);
let mut out = Vec::new();
let printer = EncoderFor::new(ImplicitVRLittleEndianEncoder::default());
let text = DefaultCharacterSetCodec;
obj.write_dataset(&mut out, printer, text).unwrap();
assert_eq!(
out,
&[
0x10, 0x00, 0x10, 0x00,
0x08, 0x00, 0x00, 0x00,
b'D', b'o', b'e', b'^', b'J', b'o', b'h', b'n',
][..],
);
}
#[test]
fn inmem_object_write_dataset_with_ts() {
let mut obj = InMemDicomObject::create_empty();
let patient_name =
DataElement::new(Tag(0x0010, 0x0010), VR::PN, dicom_value!(Str, "Doe^John"));
obj.put(patient_name);
let mut out = Vec::new();
let ts = TransferSyntaxRegistry.get("1.2.840.10008.1.2.1").unwrap();
obj.write_dataset_with_ts(&mut out, &ts).unwrap();
assert_eq!(
out,
&[
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',
][..],
);
}
#[test]
fn inmem_object_write_dataset_with_ts_cs() {
let mut obj = InMemDicomObject::create_empty();
let patient_name =
DataElement::new(Tag(0x0010, 0x0010), VR::PN, dicom_value!(Str, "Doe^John"));
obj.put(patient_name);
let mut out = Vec::new();
let ts = TransferSyntaxRegistry.get("1.2.840.10008.1.2").unwrap();
let cs = SpecificCharacterSet::Default;
obj.write_dataset_with_ts_cs(&mut out, &ts, cs).unwrap();
assert_eq!(
out,
&[
0x10, 0x00, 0x10, 0x00,
0x08, 0x00, 0x00, 0x00,
b'D', b'o', b'e', b'^', b'J', b'o', b'h', b'n',
][..],
);
}
#[test]
fn inmem_write_to_file_with_meta() {
let sop_uid = "1.4.645.212121";
let mut obj = InMemDicomObject::create_empty();
obj.put(DataElement::new(
Tag(0x0010, 0x0010),
VR::PN,
dicom_value!(Strs, ["Doe^John"]),
));
obj.put(DataElement::new(
Tag(0x0008, 0x0060),
VR::CS,
dicom_value!(Strs, ["CR"]),
));
obj.put(DataElement::new(
Tag(0x0008, 0x0018),
VR::UI,
dicom_value!(Strs, [sop_uid]),
));
let file_object = obj
.with_meta(
FileMetaTableBuilder::default()
.transfer_syntax("1.2.840.10008.1.2.1")
.media_storage_sop_class_uid("1.2.840.10008.5.1.4.1.1.1")
.media_storage_sop_instance_uid(sop_uid),
)
.unwrap();
let dir = tempfile::tempdir().unwrap();
let mut file_path = dir.into_path();
file_path.push(format!("{}.dcm", sop_uid));
file_object.write_to_file(&file_path).unwrap();
let saved_object = open_file(file_path).unwrap();
assert_eq!(file_object, saved_object);
}
#[test]
fn inmem_write_to_file_with_exact_meta() {
let sop_uid = "1.4.645.212121";
let mut obj = InMemDicomObject::create_empty();
obj.put(DataElement::new(
Tag(0x0010, 0x0010),
VR::PN,
dicom_value!(Strs, ["Doe^John"]),
));
obj.put(DataElement::new(
Tag(0x0008, 0x0060),
VR::CS,
dicom_value!(Strs, ["CR"]),
));
obj.put(DataElement::new(
Tag(0x0008, 0x0018),
VR::UI,
dicom_value!(Strs, [sop_uid]),
));
let file_object = obj.with_exact_meta(
FileMetaTableBuilder::default()
.transfer_syntax("1.2.840.10008.1.2.1")
.media_storage_sop_class_uid("1.2.840.10008.5.1.4.1.1.1")
.media_storage_sop_instance_uid(sop_uid)
.build()
.unwrap(),
);
let dir = tempfile::tempdir().unwrap();
let mut file_path = dir.into_path();
file_path.push(format!("{}.dcm", sop_uid));
file_object.write_to_file(&file_path).unwrap();
let saved_object = open_file(file_path).unwrap();
assert_eq!(file_object, saved_object);
}
#[test]
fn inmem_object_get() {
let another_patient_name = DataElement::new(
Tag(0x0010, 0x0010),
VR::PN,
PrimitiveValue::Str("Doe^John".to_string()),
);
let mut obj = InMemDicomObject::create_empty();
obj.put(another_patient_name.clone());
let elem1 = (&obj).element(Tag(0x0010, 0x0010)).unwrap();
assert_eq!(elem1, &another_patient_name);
}
#[test]
fn inmem_object_get_by_name() {
let another_patient_name = DataElement::new(
Tag(0x0010, 0x0010),
VR::PN,
PrimitiveValue::Str("Doe^John".to_string()),
);
let mut obj = InMemDicomObject::create_empty();
obj.put(another_patient_name.clone());
let elem1 = (&obj).element_by_name("PatientName").unwrap();
assert_eq!(elem1, &another_patient_name);
}
#[test]
fn inmem_object_take_element() {
let another_patient_name = DataElement::new(
Tag(0x0010, 0x0010),
VR::PN,
PrimitiveValue::Str("Doe^John".to_string()),
);
let mut obj = InMemDicomObject::create_empty();
obj.put(another_patient_name.clone());
let elem1 = obj.take_element(Tag(0x0010, 0x0010)).unwrap();
assert_eq!(elem1, another_patient_name);
assert!(matches!(
obj.take_element(Tag(0x0010, 0x0010)),
Err(Error::NoSuchDataElementTag {
tag: Tag(0x0010, 0x0010),
..
})
));
}
#[test]
fn inmem_object_take_element_by_name() {
let another_patient_name = DataElement::new(
Tag(0x0010, 0x0010),
VR::PN,
PrimitiveValue::Str("Doe^John".to_string()),
);
let mut obj = InMemDicomObject::create_empty();
obj.put(another_patient_name.clone());
let elem1 = obj.take_element_by_name("PatientName").unwrap();
assert_eq!(elem1, another_patient_name);
assert!(matches!(
obj.take_element_by_name("PatientName"),
Err(Error::NoSuchDataElementAlias {
tag: Tag(0x0010, 0x0010),
alias,
..
}) if alias == "PatientName"));
}
#[test]
fn inmem_empty_object_into_tokens() {
let obj = InMemDicomObject::create_empty();
let tokens = obj.into_tokens();
assert_eq!(tokens.count(), 0);
}
#[test]
fn inmem_shallow_object_from_tokens() {
let tokens = vec![
DataToken::ElementHeader(DataElementHeader {
tag: Tag(0x0008, 0x0060),
vr: VR::CS,
len: Length(2),
}),
DataToken::PrimitiveValue(PrimitiveValue::Str("MG".to_owned())),
DataToken::ElementHeader(DataElementHeader {
tag: Tag(0x0010, 0x0010),
vr: VR::PN,
len: Length(8),
}),
DataToken::PrimitiveValue(PrimitiveValue::Str("Doe^John".to_owned())),
];
let gt_obj = InMemDicomObject::from_element_iter(vec![
DataElement::new(
Tag(0x0010, 0x0010),
VR::PN,
PrimitiveValue::Str("Doe^John".to_string()),
),
DataElement::new(
Tag(0x0008, 0x0060),
VR::CS,
PrimitiveValue::Str("MG".to_string()),
),
]);
let obj = InMemDicomObject::build_object(
&mut tokens.into_iter().map(Result::Ok),
StandardDataDictionary,
false,
Length::UNDEFINED,
)
.unwrap();
assert_obj_eq(&obj, >_obj);
}
#[test]
fn inmem_shallow_object_into_tokens() {
let patient_name = DataElement::new(
Tag(0x0010, 0x0010),
VR::PN,
PrimitiveValue::Str("Doe^John".to_string()),
);
let modality = DataElement::new(
Tag(0x0008, 0x0060),
VR::CS,
PrimitiveValue::Str("MG".to_string()),
);
let mut obj = InMemDicomObject::create_empty();
obj.put(patient_name);
obj.put(modality);
let tokens: Vec<_> = obj.into_tokens().collect();
assert_eq!(
tokens,
vec![
DataToken::ElementHeader(DataElementHeader {
tag: Tag(0x0008, 0x0060),
vr: VR::CS,
len: Length(2),
}),
DataToken::PrimitiveValue(PrimitiveValue::Str("MG".to_owned())),
DataToken::ElementHeader(DataElementHeader {
tag: Tag(0x0010, 0x0010),
vr: VR::PN,
len: Length(8),
}),
DataToken::PrimitiveValue(PrimitiveValue::Str("Doe^John".to_owned())),
]
);
}
#[test]
fn inmem_deep_object_from_tokens() {
use smallvec::smallvec;
let obj_1 = InMemDicomObject::from_element_iter(vec![
DataElement::new(Tag(0x0018, 0x6012), VR::US, Value::Primitive(1_u16.into())),
DataElement::new(Tag(0x0018, 0x6014), VR::US, Value::Primitive(2_u16.into())),
]);
let obj_2 = InMemDicomObject::from_element_iter(vec![DataElement::new(
Tag(0x0018, 0x6012),
VR::US,
Value::Primitive(4_u16.into()),
)]);
let gt_obj = InMemDicomObject::from_element_iter(vec![
DataElement::new(
Tag(0x0018, 0x6011),
VR::SQ,
Value::Sequence {
items: smallvec![obj_1, obj_2],
size: Length::UNDEFINED,
},
),
DataElement::new(Tag(0x0020, 0x4000), VR::LT, Value::Primitive("TEST".into())),
]);
let tokens: Vec<_> = vec![
DataToken::SequenceStart {
tag: Tag(0x0018, 0x6011),
len: Length::UNDEFINED,
},
DataToken::ItemStart {
len: Length::UNDEFINED,
},
DataToken::ElementHeader(DataElementHeader {
tag: Tag(0x0018, 0x6012),
vr: VR::US,
len: Length(2),
}),
DataToken::PrimitiveValue(PrimitiveValue::U16([1].as_ref().into())),
DataToken::ElementHeader(DataElementHeader {
tag: Tag(0x0018, 0x6014),
vr: VR::US,
len: Length(2),
}),
DataToken::PrimitiveValue(PrimitiveValue::U16([2].as_ref().into())),
DataToken::ItemEnd,
DataToken::ItemStart {
len: Length::UNDEFINED,
},
DataToken::ElementHeader(DataElementHeader {
tag: Tag(0x0018, 0x6012),
vr: VR::US,
len: Length(2),
}),
DataToken::PrimitiveValue(PrimitiveValue::U16([4].as_ref().into())),
DataToken::ItemEnd,
DataToken::SequenceEnd,
DataToken::ElementHeader(DataElementHeader {
tag: Tag(0x0020, 0x4000),
vr: VR::LT,
len: Length(4),
}),
DataToken::PrimitiveValue(PrimitiveValue::Str("TEST".into())),
];
let obj = InMemDicomObject::build_object(
&mut tokens.into_iter().map(Result::Ok),
StandardDataDictionary,
false,
Length::UNDEFINED,
)
.unwrap();
assert_obj_eq(&obj, >_obj);
}
#[test]
fn inmem_deep_object_into_tokens() {
use smallvec::smallvec;
let obj_1 = InMemDicomObject::from_element_iter(vec![
DataElement::new(Tag(0x0018, 0x6012), VR::US, Value::Primitive(1_u16.into())),
DataElement::new(Tag(0x0018, 0x6014), VR::US, Value::Primitive(2_u16.into())),
]);
let obj_2 = InMemDicomObject::from_element_iter(vec![DataElement::new(
Tag(0x0018, 0x6012),
VR::US,
Value::Primitive(4_u16.into()),
)]);
let main_obj = InMemDicomObject::from_element_iter(vec![
DataElement::new(
Tag(0x0018, 0x6011),
VR::SQ,
Value::Sequence {
items: smallvec![obj_1, obj_2],
size: Length::UNDEFINED,
},
),
DataElement::new(Tag(0x0020, 0x4000), VR::LT, Value::Primitive("TEST".into())),
]);
let tokens: Vec<_> = main_obj.into_tokens().collect();
assert_eq!(
tokens,
vec![
DataToken::SequenceStart {
tag: Tag(0x0018, 0x6011),
len: Length::UNDEFINED,
},
DataToken::ItemStart {
len: Length::UNDEFINED,
},
DataToken::ElementHeader(DataElementHeader {
tag: Tag(0x0018, 0x6012),
vr: VR::US,
len: Length(2),
}),
DataToken::PrimitiveValue(PrimitiveValue::U16([1].as_ref().into())),
DataToken::ElementHeader(DataElementHeader {
tag: Tag(0x0018, 0x6014),
vr: VR::US,
len: Length(2),
}),
DataToken::PrimitiveValue(PrimitiveValue::U16([2].as_ref().into())),
DataToken::ItemEnd,
DataToken::ItemStart {
len: Length::UNDEFINED,
},
DataToken::ElementHeader(DataElementHeader {
tag: Tag(0x0018, 0x6012),
vr: VR::US,
len: Length(2),
}),
DataToken::PrimitiveValue(PrimitiveValue::U16([4].as_ref().into())),
DataToken::ItemEnd,
DataToken::SequenceEnd,
DataToken::ElementHeader(DataElementHeader {
tag: Tag(0x0020, 0x4000),
vr: VR::LT,
len: Length(4),
}),
DataToken::PrimitiveValue(PrimitiveValue::Str("TEST".into())),
]
);
}
#[test]
fn inmem_encapsulated_pixel_data_from_tokens() {
use smallvec::smallvec;
let gt_obj = InMemDicomObject::from_element_iter(vec![DataElement::new(
Tag(0x7fe0, 0x0010),
VR::OB,
Value::PixelSequence {
fragments: smallvec![vec![0x33; 32]],
offset_table: Default::default(),
},
)]);
let tokens: Vec<_> = vec![
DataToken::PixelSequenceStart,
DataToken::ItemStart { len: Length(0) },
DataToken::ItemEnd,
DataToken::ItemStart { len: Length(32) },
DataToken::ItemValue(vec![0x33; 32]),
DataToken::ItemEnd,
DataToken::SequenceEnd,
];
let obj = InMemDicomObject::build_object(
&mut tokens.into_iter().map(Result::Ok),
StandardDataDictionary,
false,
Length::UNDEFINED,
)
.unwrap();
assert_obj_eq(&obj, >_obj);
}
#[test]
fn inmem_encapsulated_pixel_data_into_tokens() {
use smallvec::smallvec;
let main_obj = InMemDicomObject::from_element_iter(vec![DataElement::new(
Tag(0x7fe0, 0x0010),
VR::OB,
Value::PixelSequence {
fragments: smallvec![vec![0x33; 32]],
offset_table: Default::default(),
},
)]);
let tokens: Vec<_> = main_obj.into_tokens().collect();
assert_eq!(
tokens,
vec![
DataToken::PixelSequenceStart,
DataToken::ItemStart { len: Length(0) },
DataToken::ItemEnd,
DataToken::ItemStart { len: Length(32) },
DataToken::ItemValue(vec![0x33; 32]),
DataToken::ItemEnd,
DataToken::SequenceEnd,
]
);
}
}