use byteordered::byteorder::{ByteOrder, LittleEndian};
use dicom_core::dicom_value;
use dicom_core::header::{DataElement, EmptyObject, HasLength, Header};
use dicom_core::value::{PrimitiveValue, Value};
use dicom_core::{Length, Tag, VR};
use dicom_encoding::decode::{self, DecodeFrom};
use dicom_encoding::encode::EncoderFor;
use dicom_encoding::text::{self, DefaultCharacterSetCodec, TextCodec};
use dicom_encoding::transfer_syntax::explicit_le::ExplicitVRLittleEndianEncoder;
use dicom_parser::dataset::{DataSetWriter, IntoTokens};
use snafu::{ensure, Backtrace, OptionExt, ResultExt, Snafu};
use std::io::{Read, Write};
use crate::{IMPLEMENTATION_CLASS_UID, IMPLEMENTATION_VERSION_NAME};
const DICM_MAGIC_CODE: [u8; 4] = [b'D', b'I', b'C', b'M'];
#[derive(Debug, Snafu)]
#[non_exhaustive]
pub enum Error {
#[snafu(display("Could not start reading DICOM data"))]
ReadMagicCode {
backtrace: Backtrace,
source: std::io::Error,
},
#[snafu(display("Could not read data value"))]
ReadValueData {
backtrace: Backtrace,
source: std::io::Error,
},
#[snafu(display("Could not decode text in {}", name))]
DecodeText {
name: &'static str,
#[snafu(backtrace)]
source: dicom_encoding::text::DecodeTextError,
},
#[snafu(display("Invalid DICOM data"))]
NotDicom { backtrace: Backtrace },
#[snafu(display("Could not decode data element"))]
DecodeElement {
#[snafu(backtrace)]
source: dicom_encoding::decode::Error,
},
#[snafu(display("Unexpected data element tagged {}", tag))]
UnexpectedTag { tag: Tag, backtrace: Backtrace },
#[snafu(display("Missing data element `{}`", alias))]
MissingElement {
alias: &'static str,
backtrace: Backtrace,
},
#[snafu(display("Unexpected length {} for data element tagged {}", length, tag))]
UnexpectedDataValueLength {
tag: Tag,
length: Length,
backtrace: Backtrace,
},
#[snafu(display("Undefined value length for data element tagged {}", tag))]
UndefinedValueLength { tag: Tag, backtrace: Backtrace },
#[snafu(display("Could not write file meta group data set"))]
WriteSet {
#[snafu(backtrace)]
source: dicom_parser::dataset::write::Error,
},
}
type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Clone, PartialEq)]
pub struct FileMetaTable {
pub information_group_length: u32,
pub information_version: [u8; 2],
pub media_storage_sop_class_uid: String,
pub media_storage_sop_instance_uid: String,
pub transfer_syntax: String,
pub implementation_class_uid: String,
pub implementation_version_name: Option<String>,
pub source_application_entity_title: Option<String>,
pub sending_application_entity_title: Option<String>,
pub receiving_application_entity_title: Option<String>,
pub private_information_creator_uid: Option<String>,
pub private_information: Option<Vec<u8>>,
}
fn read_str_body<'s, S: 's, T>(
source: &'s mut S,
text: &T,
group_length_remaining: &mut u32,
len: u32,
) -> Result<String>
where
S: Read,
T: TextCodec,
{
let mut v = vec![0; len as usize];
source.read_exact(&mut v).context(ReadValueData)?;
*group_length_remaining -= 8 + len;
text.decode(&v).context(DecodeText { name: text.name() })
}
impl FileMetaTable {
pub fn from_reader<R: Read>(file: R) -> Result<Self> {
FileMetaTable::read_from(file)
}
fn read_from<S: Read>(mut file: S) -> Result<Self> {
let mut buff: [u8; 4] = [0; 4];
{
file.read_exact(&mut buff).context(ReadMagicCode)?;
ensure!(buff == DICM_MAGIC_CODE, NotDicom);
}
let decoder = decode::file_header_decoder();
let text = text::DefaultCharacterSetCodec;
let builder = FileMetaTableBuilder::new();
let group_length: u32 = {
let (elem, _bytes_read) = decoder.decode_header(&mut file).context(DecodeElement)?;
if elem.tag() != (0x0002, 0x0000) {
return UnexpectedTag { tag: elem.tag() }.fail();
}
if elem.length() != Length(4) {
return UnexpectedDataValueLength {
tag: elem.tag(),
length: elem.length(),
}
.fail();
}
let mut buff: [u8; 4] = [0; 4];
file.read_exact(&mut buff).context(ReadValueData)?;
LittleEndian::read_u32(&buff)
};
let mut group_length_remaining = group_length;
let mut builder = builder.group_length(group_length);
while group_length_remaining > 0 {
let (elem, _bytes_read) = decoder.decode_header(&mut file).context(DecodeElement)?;
let elem_len = match elem.length().get() {
None => {
return UndefinedValueLength { tag: elem.tag() }.fail();
}
Some(len) => len,
};
builder = match elem.tag() {
Tag(0x0002, 0x0001) => {
if elem.length() != Length(2) {
return UnexpectedDataValueLength {
tag: elem.tag(),
length: elem.length(),
}
.fail();
}
let mut hbuf = [0u8; 2];
file.read_exact(&mut hbuf[..]).context(ReadValueData)?;
group_length_remaining -= 14;
builder.information_version(hbuf)
}
Tag(0x0002, 0x0002) => builder.media_storage_sop_class_uid(read_str_body(
&mut file,
&text,
&mut group_length_remaining,
elem_len,
)?),
Tag(0x0002, 0x0003) => builder.media_storage_sop_instance_uid(read_str_body(
&mut file,
&text,
&mut group_length_remaining,
elem_len,
)?),
Tag(0x0002, 0x0010) => builder.transfer_syntax(read_str_body(
&mut file,
&text,
&mut group_length_remaining,
elem_len,
)?),
Tag(0x0002, 0x0012) => builder.implementation_class_uid(read_str_body(
&mut file,
&text,
&mut group_length_remaining,
elem_len,
)?),
Tag(0x0002, 0x0013) => {
let mut v = vec![0; elem_len as usize];
file.read_exact(&mut v).context(ReadValueData)?;
group_length_remaining -= 8 + elem_len;
builder.implementation_version_name(
text.decode(&v).context(DecodeText { name: text.name() })?,
)
}
Tag(0x0002, 0x0016) => {
let mut v = vec![0; elem_len as usize];
file.read_exact(&mut v).context(ReadValueData)?;
group_length_remaining -= 8 + elem_len;
builder.source_application_entity_title(
text.decode(&v).context(DecodeText { name: text.name() })?,
)
}
Tag(0x0002, 0x0017) => {
let mut v = vec![0; elem_len as usize];
file.read_exact(&mut v).context(ReadValueData)?;
group_length_remaining -= 8 + elem_len;
builder.sending_application_entity_title(
text.decode(&v).context(DecodeText { name: text.name() })?,
)
}
Tag(0x0002, 0x0018) => {
let mut v = vec![0; elem_len as usize];
file.read_exact(&mut v).context(ReadValueData)?;
group_length_remaining -= 8 + elem_len;
builder.receiving_application_entity_title(
text.decode(&v).context(DecodeText { name: text.name() })?,
)
}
Tag(0x0002, 0x0100) => {
let mut v = vec![0; elem_len as usize];
file.read_exact(&mut v).context(ReadValueData)?;
group_length_remaining -= 8 + elem_len;
builder.private_information_creator_uid(
text.decode(&v).context(DecodeText { name: text.name() })?,
)
}
Tag(0x0002, 0x0102) => {
let mut v = vec![0; elem_len as usize];
file.read_exact(&mut v).context(ReadValueData)?;
group_length_remaining -= 12 + elem_len;
builder.private_information(v)
}
Tag(0x0002, _) => {
builder
}
_ => {
builder
}
}
}
builder.build()
}
pub fn into_element_iter(self) -> impl Iterator<Item = DataElement<EmptyObject, [u8; 0]>> {
let mut elems = vec![
DataElement::new(
Tag(0x0002, 0x0000),
VR::UL,
Value::Primitive(self.information_group_length.into()),
),
DataElement::new(
Tag(0x0002, 0x0001),
VR::OB,
Value::Primitive(dicom_value!(
U8,
[self.information_version[0], self.information_version[1]]
)),
),
DataElement::new(
Tag(0x0002, 0x0002),
VR::UI,
Value::Primitive(self.media_storage_sop_class_uid.into()),
),
DataElement::new(
Tag(0x0002, 0x0003),
VR::UI,
Value::Primitive(self.media_storage_sop_instance_uid.into()),
),
DataElement::new(
Tag(0x0002, 0x0010),
VR::UI,
Value::Primitive(self.transfer_syntax.into()),
),
DataElement::new(
Tag(0x0002, 0x0012),
VR::UI,
Value::Primitive(self.implementation_class_uid.into()),
),
];
if let Some(v) = self.implementation_version_name {
elems.push(DataElement::new(
Tag(0x0002, 0x0013),
VR::SH,
Value::Primitive(v.into()),
));
}
if let Some(v) = self.source_application_entity_title {
elems.push(DataElement::new(
Tag(0x0002, 0x0016),
VR::AE,
Value::Primitive(v.into()),
));
}
if let Some(v) = self.sending_application_entity_title {
elems.push(DataElement::new(
Tag(0x0002, 0x0017),
VR::AE,
Value::Primitive(v.into()),
));
}
if let Some(v) = self.receiving_application_entity_title {
elems.push(DataElement::new(
Tag(0x0002, 0x0018),
VR::AE,
Value::Primitive(v.into()),
));
}
if let Some(v) = self.private_information_creator_uid {
elems.push(DataElement::new(
Tag(0x0002, 0x0100),
VR::UI,
Value::Primitive(v.into()),
));
}
if let Some(v) = self.private_information {
elems.push(DataElement::new(
Tag(0x0002, 0x0102),
VR::OB,
Value::Primitive(PrimitiveValue::U8(v.into())),
));
}
elems.into_iter()
}
pub fn write<W: Write>(&self, writer: W) -> Result<()> {
let mut dset = DataSetWriter::new(
writer,
EncoderFor::new(ExplicitVRLittleEndianEncoder::default()),
DefaultCharacterSetCodec,
);
dset.write_sequence(
self.clone()
.into_element_iter()
.flat_map(IntoTokens::into_tokens),
)
.context(WriteSet)
}
}
#[derive(Debug, Clone)]
pub struct FileMetaTableBuilder {
information_group_length: Option<u32>,
information_version: Option<[u8; 2]>,
media_storage_sop_class_uid: Option<String>,
media_storage_sop_instance_uid: Option<String>,
transfer_syntax: Option<String>,
implementation_class_uid: Option<String>,
implementation_version_name: Option<String>,
source_application_entity_title: Option<String>,
sending_application_entity_title: Option<String>,
receiving_application_entity_title: Option<String>,
private_information_creator_uid: Option<String>,
private_information: Option<Vec<u8>>,
}
impl Default for FileMetaTableBuilder {
fn default() -> FileMetaTableBuilder {
FileMetaTableBuilder {
information_group_length: None,
information_version: None,
media_storage_sop_class_uid: None,
media_storage_sop_instance_uid: None,
transfer_syntax: None,
implementation_class_uid: None,
implementation_version_name: None,
source_application_entity_title: None,
sending_application_entity_title: None,
receiving_application_entity_title: None,
private_information_creator_uid: None,
private_information: None,
}
}
}
#[inline]
fn padded<T>(s: T, pad: char) -> String
where
T: Into<String>,
{
let mut s = s.into();
if s.len() % 2 == 1 {
s.push(pad);
}
s
}
fn ui_padded<T>(s: T) -> String
where
T: Into<String>,
{
padded(s, '\0')
}
fn txt_padded<T>(s: T) -> String
where
T: Into<String>,
{
padded(s, ' ')
}
impl FileMetaTableBuilder {
pub fn new() -> FileMetaTableBuilder {
FileMetaTableBuilder::default()
}
pub fn group_length(mut self, value: u32) -> FileMetaTableBuilder {
self.information_group_length = Some(value);
self
}
pub fn information_version(mut self, value: [u8; 2]) -> FileMetaTableBuilder {
self.information_version = Some(value);
self
}
pub fn media_storage_sop_class_uid<T>(mut self, value: T) -> FileMetaTableBuilder
where
T: Into<String>,
{
self.media_storage_sop_class_uid = Some(ui_padded(value));
self
}
pub fn media_storage_sop_instance_uid<T>(mut self, value: T) -> FileMetaTableBuilder
where
T: Into<String>,
{
self.media_storage_sop_instance_uid = Some(ui_padded(value));
self
}
pub fn transfer_syntax<T>(mut self, value: T) -> FileMetaTableBuilder
where
T: Into<String>,
{
self.transfer_syntax = Some(ui_padded(value));
self
}
pub fn implementation_class_uid<T>(mut self, value: T) -> FileMetaTableBuilder
where
T: Into<String>,
{
self.implementation_class_uid = Some(ui_padded(value));
self
}
pub fn implementation_version_name<T>(mut self, value: T) -> FileMetaTableBuilder
where
T: Into<String>,
{
self.implementation_version_name = Some(txt_padded(value));
self
}
pub fn source_application_entity_title<T>(mut self, value: T) -> FileMetaTableBuilder
where
T: Into<String>,
{
self.source_application_entity_title = Some(txt_padded(value));
self
}
pub fn sending_application_entity_title<T>(mut self, value: T) -> FileMetaTableBuilder
where
T: Into<String>,
{
self.sending_application_entity_title = Some(txt_padded(value));
self
}
pub fn receiving_application_entity_title<T>(mut self, value: T) -> FileMetaTableBuilder
where
T: Into<String>,
{
self.receiving_application_entity_title = Some(txt_padded(value));
self
}
pub fn private_information_creator_uid<T>(mut self, value: T) -> FileMetaTableBuilder
where
T: Into<String>,
{
self.private_information_creator_uid = Some(ui_padded(value));
self
}
pub fn private_information<T>(mut self, value: T) -> FileMetaTableBuilder
where
T: Into<Vec<u8>>,
{
self.private_information = Some(value.into());
self
}
pub fn build(self) -> Result<FileMetaTable> {
let information_version = self.information_version.unwrap_or_else(|| {
[0, 1]
});
let media_storage_sop_class_uid =
self.media_storage_sop_class_uid.context(MissingElement {
alias: "MediaStorageSOPClassUID",
})?;
let media_storage_sop_instance_uid =
self.media_storage_sop_instance_uid
.context(MissingElement {
alias: "MediaStorageSOPInstanceUID",
})?;
let transfer_syntax = self.transfer_syntax.context(MissingElement {
alias: "TransferSyntax",
})?;
let mut implementation_version_name = self.implementation_version_name;
let implementation_class_uid = self.implementation_class_uid.unwrap_or_else(|| {
implementation_version_name = Some(IMPLEMENTATION_VERSION_NAME.to_string());
IMPLEMENTATION_CLASS_UID.to_string()
});
fn dicom_len<T: AsRef<str>>(x: T) -> u32 {
let o = x.as_ref().len() as u32;
if o % 2 == 1 {
o + 1
} else {
o
}
}
let information_group_length = match self.information_group_length {
Some(e) => e,
None => {
14 + 8
+ dicom_len(&media_storage_sop_class_uid)
+ 8
+ dicom_len(&media_storage_sop_instance_uid)
+ 8
+ dicom_len(&transfer_syntax)
+ 8
+ dicom_len(&implementation_class_uid)
+ implementation_version_name
.as_ref()
.map(|s| 8 + s.len() as u32)
.unwrap_or(0)
+ self
.source_application_entity_title
.as_ref()
.map(|s| 8 + s.len() as u32)
.unwrap_or(0)
+ self
.sending_application_entity_title
.as_ref()
.map(|s| 8 + s.len() as u32)
.unwrap_or(0)
+ self
.receiving_application_entity_title
.as_ref()
.map(|s| 8 + s.len() as u32)
.unwrap_or(0)
+ self
.private_information_creator_uid
.as_ref()
.map(|s| 8 + s.len() as u32)
.unwrap_or(0)
+ self
.private_information
.as_ref()
.map(|x| 12 + x.len() as u32)
.unwrap_or(0)
}
};
Ok(FileMetaTable {
information_group_length,
information_version,
media_storage_sop_class_uid,
media_storage_sop_instance_uid,
transfer_syntax,
implementation_class_uid,
implementation_version_name,
source_application_entity_title: self.source_application_entity_title,
sending_application_entity_title: self.sending_application_entity_title,
receiving_application_entity_title: self.receiving_application_entity_title,
private_information_creator_uid: self.private_information_creator_uid,
private_information: self.private_information,
})
}
}
#[cfg(test)]
mod tests {
use crate::{IMPLEMENTATION_CLASS_UID, IMPLEMENTATION_VERSION_NAME};
use super::{FileMetaTable, FileMetaTableBuilder};
use dicom_core::value::Value;
use dicom_core::{dicom_value, DataElement, Tag, VR};
const TEST_META_1: &'static [u8] = &[
b'D', b'I', b'C', b'M',
0x02, 0x00, 0x00, 0x00, b'U', b'L', 0x04, 0x00, 0xc8, 0x00, 0x00, 0x00,
0x02, 0x00, 0x01, 0x00, b'O', b'B', 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01,
0x02, 0x00, 0x02, 0x00, b'U', b'I', 0x1a, 0x00, 0x31, 0x2e, 0x32, 0x2e, 0x38, 0x34, 0x30,
0x2e, 0x31, 0x30, 0x30, 0x30, 0x38, 0x2e, 0x35, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e,
0x31, 0x2e, 0x31, 0x00,
0x02, 0x00, 0x03, 0x00, b'U', b'I', 0x38, 0x00, 0x31, 0x2e, 0x32, 0x2e, 0x33, 0x2e, 0x34,
0x2e, 0x35, 0x2e, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x2e, 0x31, 0x32, 0x33,
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, 0x2e, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
0x2e, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2e, 0x31, 0x32, 0x33, 0x34,
0x35, 0x36, 0x37, 0x00,
0x02, 0x00, 0x10, 0x00, b'U', b'I', 0x14, 0x00, 0x31, 0x2e, 0x32, 0x2e, 0x38, 0x34, 0x30,
0x2e, 0x31, 0x30, 0x30, 0x30, 0x38, 0x2e, 0x31, 0x2e, 0x32, 0x2e, 0x31, 0x00,
0x02, 0x00, 0x12, 0x00, b'U', b'I', 0x14, 0x00, 0x31, 0x2e, 0x32, 0x2e, 0x33, 0x34, 0x35,
0x2e, 0x36, 0x2e, 0x37, 0x38, 0x39, 0x30, 0x2e, 0x31, 0x2e, 0x32, 0x33, 0x34,
0x02, 0x00, 0x13, 0x00, b'S', b'H', 0x10, 0x00, 0x52, 0x55, 0x53, 0x54, 0x59, 0x5f, 0x44,
0x49, 0x43, 0x4f, 0x4d, 0x5f, 0x32, 0x36, 0x39, 0x20,
0x02, 0x00, 0x16, 0x00, b'A', b'E', 0x00, 0x00,
];
#[test]
fn read_meta_table_from_reader() {
let mut source = TEST_META_1;
let table = FileMetaTable::from_reader(&mut source).unwrap();
let gt = FileMetaTable {
information_group_length: 200,
information_version: [0u8, 1u8],
media_storage_sop_class_uid: "1.2.840.10008.5.1.4.1.1.1\0".to_owned(),
media_storage_sop_instance_uid:
"1.2.3.4.5.12345678.1234567890.1234567.123456789.1234567\0".to_owned(),
transfer_syntax: "1.2.840.10008.1.2.1\0".to_owned(),
implementation_class_uid: "1.2.345.6.7890.1.234".to_owned(),
implementation_version_name: Some("RUSTY_DICOM_269 ".to_owned()),
source_application_entity_title: Some("".to_owned()),
sending_application_entity_title: None,
receiving_application_entity_title: None,
private_information_creator_uid: None,
private_information: None,
};
assert_eq!(table.information_group_length, 200);
assert_eq!(table.information_version, [0u8, 1u8]);
assert_eq!(
table.media_storage_sop_class_uid,
"1.2.840.10008.5.1.4.1.1.1\0"
);
assert_eq!(
table.media_storage_sop_instance_uid,
"1.2.3.4.5.12345678.1234567890.1234567.123456789.1234567\0"
);
assert_eq!(table.transfer_syntax, "1.2.840.10008.1.2.1\0");
assert_eq!(table.implementation_class_uid, "1.2.345.6.7890.1.234");
assert_eq!(
table.implementation_version_name,
Some("RUSTY_DICOM_269 ".to_owned())
);
assert_eq!(table.source_application_entity_title, Some("".into()));
assert_eq!(table.sending_application_entity_title, None);
assert_eq!(table.receiving_application_entity_title, None);
assert_eq!(table.private_information_creator_uid, None);
assert_eq!(table.private_information, None);
assert_eq!(table, gt);
}
#[test]
fn create_meta_table_with_builder() {
let table = FileMetaTableBuilder::new()
.information_version([0, 1])
.media_storage_sop_class_uid("1.2.840.10008.5.1.4.1.1.1")
.media_storage_sop_instance_uid(
"1.2.3.4.5.12345678.1234567890.1234567.123456789.1234567",
)
.transfer_syntax("1.2.840.10008.1.2.1")
.implementation_class_uid("1.2.345.6.7890.1.234")
.implementation_version_name("RUSTY_DICOM_269")
.source_application_entity_title("")
.build()
.unwrap();
let gt = FileMetaTable {
information_group_length: 200,
information_version: [0u8, 1u8],
media_storage_sop_class_uid: "1.2.840.10008.5.1.4.1.1.1\0".to_owned(),
media_storage_sop_instance_uid:
"1.2.3.4.5.12345678.1234567890.1234567.123456789.1234567\0".to_owned(),
transfer_syntax: "1.2.840.10008.1.2.1\0".to_owned(),
implementation_class_uid: "1.2.345.6.7890.1.234".to_owned(),
implementation_version_name: Some("RUSTY_DICOM_269 ".to_owned()),
source_application_entity_title: Some("".to_owned()),
sending_application_entity_title: None,
receiving_application_entity_title: None,
private_information_creator_uid: None,
private_information: None,
};
assert_eq!(table.information_group_length, gt.information_group_length);
assert_eq!(table, gt);
}
#[test]
fn create_meta_table_with_builder_minimal() {
let table = FileMetaTableBuilder::new()
.media_storage_sop_class_uid("1.2.840.10008.5.1.4.1.1.1")
.media_storage_sop_instance_uid(
"1.2.3.4.5.12345678.1234567890.1234567.123456789.1234567",
)
.transfer_syntax("1.2.840.10008.1.2")
.build()
.unwrap();
let gt = FileMetaTable {
information_group_length: 154
+ IMPLEMENTATION_CLASS_UID.len() as u32
+ IMPLEMENTATION_VERSION_NAME.len() as u32,
information_version: [0u8, 1u8],
media_storage_sop_class_uid: "1.2.840.10008.5.1.4.1.1.1\0".to_owned(),
media_storage_sop_instance_uid:
"1.2.3.4.5.12345678.1234567890.1234567.123456789.1234567\0".to_owned(),
transfer_syntax: "1.2.840.10008.1.2\0".to_owned(),
implementation_class_uid: IMPLEMENTATION_CLASS_UID.to_owned(),
implementation_version_name: Some(IMPLEMENTATION_VERSION_NAME.to_owned()),
source_application_entity_title: None,
sending_application_entity_title: None,
receiving_application_entity_title: None,
private_information_creator_uid: None,
private_information: None,
};
assert_eq!(table.information_group_length, gt.information_group_length);
assert_eq!(table, gt);
}
#[test]
fn read_meta_table_into_iter() {
let table = FileMetaTable {
information_group_length: 200,
information_version: [0u8, 1u8],
media_storage_sop_class_uid: "1.2.840.10008.5.1.4.1.1.1\0".to_owned(),
media_storage_sop_instance_uid:
"1.2.3.4.5.12345678.1234567890.1234567.123456789.1234567\0".to_owned(),
transfer_syntax: "1.2.840.10008.1.2.1\0".to_owned(),
implementation_class_uid: "1.2.345.6.7890.1.234".to_owned(),
implementation_version_name: Some("RUSTY_DICOM_269 ".to_owned()),
source_application_entity_title: Some("".to_owned()),
sending_application_entity_title: None,
receiving_application_entity_title: None,
private_information_creator_uid: None,
private_information: None,
};
let gt = vec![
DataElement::new(Tag(0x0002, 0x0000), VR::UL, dicom_value!(U32, 200)),
DataElement::new(Tag(0x0002, 0x0001), VR::OB, dicom_value!(U8, [0, 1])),
DataElement::new(
Tag(0x0002, 0x0002),
VR::UI,
Value::Primitive("1.2.840.10008.5.1.4.1.1.1\0".into()),
),
DataElement::new(
Tag(0x0002, 0x0003),
VR::UI,
Value::Primitive(
"1.2.3.4.5.12345678.1234567890.1234567.123456789.1234567\0".into(),
),
),
DataElement::new(
Tag(0x0002, 0x0010),
VR::UI,
Value::Primitive("1.2.840.10008.1.2.1\0".into()),
),
DataElement::new(
Tag(0x0002, 0x0012),
VR::UI,
Value::Primitive("1.2.345.6.7890.1.234".into()),
),
DataElement::new(
Tag(0x0002, 0x0013),
VR::SH,
Value::Primitive("RUSTY_DICOM_269 ".into()),
),
DataElement::new(Tag(0x0002, 0x0016), VR::AE, Value::Primitive("".into())),
];
let elems: Vec<_> = table.into_element_iter().collect();
assert_eq!(elems, gt);
}
}