Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DICOM object writing #35

Merged
merged 18 commits into from
May 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 96 additions & 52 deletions core/src/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,33 @@
//! element header, and element composite types.

use crate::error::{Error, Result};
use crate::value::{DicomValueType, PrimitiveValue, Value};
use crate::value::{PrimitiveValue, Value};
use std::borrow::Cow;
use std::cmp::Ordering;
use std::fmt;
use std::str::{from_utf8, FromStr};

/// Trait for any DICOM entity (element or item) which may have a length.
pub trait HasLength {
/// Retrieve the value data's length as specified by the data element or
/// item, in bytes.
///
/// It is named `length` to make it distinct from the conventional method
/// signature `len(&self) -> usize` for the number of elements of a
/// collection.
///
/// According to the standard, the concrete value size may be undefined,
/// which can be the case for sequence elements or specific primitive
/// values.
fn length(&self) -> Length;
}

/// A trait for a data type containing a DICOM header.
#[allow(clippy::len_without_is_empty)]
pub trait Header {
pub trait Header: HasLength {
/// Retrieve the element's tag as a `(group, element)` tuple.
fn tag(&self) -> Tag;

/// Retrieve the value data's length as specified by the data element, in bytes.
/// According to the standard, the concrete value size may be undefined,
/// which can be the case for sequence elements or specific primitive values.
fn len(&self) -> Length;

/// Check whether this is the header of an item.
fn is_item(&self) -> bool {
self.tag() == Tag(0xFFFE, 0xE000)
Expand All @@ -36,9 +46,23 @@ pub trait Header {
}
}

/// Stub type representing a non-existing DICOM object.
///
/// This type implements `HasLength`, but cannot be instantiated.
/// This makes it so that `Value<EmptyObject>` is sure to be either a primitive
/// value or a sequence with no items.
#[derive(Debug, PartialEq)]
pub enum EmptyObject {}

impl HasLength for EmptyObject {
fn length(&self) -> Length {
unreachable!()
}
}

/// A data type that represents and owns a DICOM data element. Unlike
/// [`PrimitiveDataElement`], this type may contain multiple data elements
/// through the item sequence VR (of type `I`).
/// through the item sequence VR (where each item contains an object of type `I`).
#[derive(Debug, PartialEq, Clone)]
pub struct DataElement<I> {
header: DataElementHeader,
Expand Down Expand Up @@ -91,15 +115,24 @@ impl<'a> PrimitiveDataElementRef<'a> {
PrimitiveDataElementRef { header, value }
}
}
impl<I> HasLength for DataElement<I> {
#[inline]
fn length(&self) -> Length {
self.header.length()
}
}

impl<I> Header for DataElement<I> {
#[inline]
fn tag(&self) -> Tag {
self.header.tag()
}
}

impl<I> HasLength for &DataElement<I> {
#[inline]
fn len(&self) -> Length {
self.header.len()
fn length(&self) -> Length {
(**self).length()
}
}

Expand All @@ -109,9 +142,12 @@ impl<'a, I> Header for &'a DataElement<I> {
(**self).tag()
}

}

impl<'v, I> HasLength for DataElementRef<'v, I> {
#[inline]
fn len(&self) -> Length {
(**self).len()
fn length(&self) -> Length {
self.header.length()
}
}

Expand All @@ -120,17 +156,9 @@ impl<'v, I> Header for DataElementRef<'v, I> {
fn tag(&self) -> Tag {
self.header.tag()
}

#[inline]
fn len(&self) -> Length {
self.header.len()
}
}

impl<I> DataElement<I>
where
I: DicomValueType,
{
impl<I> DataElement<I> {
/// Create an empty data element.
pub fn empty(tag: Tag, vr: VR) -> Self {
DataElement {
Expand All @@ -143,24 +171,17 @@ where
}
}

/// Create a primitive data element from the given parts. This method will not check
/// whether the value representation is compatible with the given value.
pub fn new(tag: Tag, vr: VR, value: Value<I>) -> Self {
DataElement {
header: DataElementHeader {
tag,
vr,
len: value.size(),
},
value,
}
}

/// Retrieve the element header.
pub fn header(&self) -> &DataElementHeader {
&self.header
}

/// Retrieve the value representation, which may be unknown or not
/// applicable.
pub fn vr(&self) -> VR {
self.header.vr()
}

/// Retrieve the data value.
pub fn value(&self) -> &Value<I> {
&self.value
Expand All @@ -172,11 +193,25 @@ where
pub fn into_value(self) -> Value<I> {
self.value
}
}

/// Retrieve the value representation, which may be unknown or not
/// applicable.
pub fn vr(&self) -> VR {
self.header.vr()
impl<I> DataElement<I>
where
I: HasLength,
{
/// Create a primitive data element from the given parts.
///
/// This method will not check whether the value representation is
/// compatible with the given value.
pub fn new(tag: Tag, vr: VR, value: Value<I>) -> Self {
DataElement {
header: DataElementHeader {
tag,
vr,
len: value.length(),
},
value,
}
}

/// Retrieve the element's value as a single string.
Expand All @@ -187,7 +222,7 @@ where

impl<'v, I> DataElementRef<'v, I>
where
I: DicomValueType,
I: HasLength,
{
/// Create a data element from the given parts. This method will not check
/// whether the value representation is compatible with the value. Caution
Expand All @@ -197,7 +232,7 @@ where
header: DataElementHeader {
tag,
vr,
len: value.size(),
len: value.length(),
},
value,
}
Expand Down Expand Up @@ -226,19 +261,24 @@ pub struct DataElementHeader {
pub len: Length,
}

impl HasLength for DataElementHeader {
#[inline]
fn length(&self) -> Length {
self.len
}
}

impl Header for DataElementHeader {
#[inline]
fn tag(&self) -> Tag {
self.tag
}

fn len(&self) -> Length {
self.len
}
}

impl DataElementHeader {
/// Create a new data element header with the given properties.
/// This is just a trivial constructor.
#[inline]
pub fn new<T: Into<Tag>>(tag: T, vr: VR, len: Length) -> DataElementHeader {
DataElementHeader {
tag: tag.into(),
Expand All @@ -248,6 +288,7 @@ impl DataElementHeader {
}

/// Retrieve the element's value representation, which can be unknown.
#[inline]
pub fn vr(&self) -> VR {
self.vr
}
Expand All @@ -258,7 +299,7 @@ impl From<SequenceItemHeader> for DataElementHeader {
DataElementHeader {
tag: value.tag(),
vr: VR::UN,
len: value.len(),
len: value.length(),
}
}
}
Expand Down Expand Up @@ -309,21 +350,24 @@ impl SequenceItemHeader {
}
}

impl HasLength for SequenceItemHeader {
#[inline]
fn length(&self) -> Length {
match *self {
SequenceItemHeader::Item { len } => len,
SequenceItemHeader::ItemDelimiter | SequenceItemHeader::SequenceDelimiter => Length(0),
}
}
}
impl Header for SequenceItemHeader {
#[inline]
fn tag(&self) -> Tag {
match *self {
SequenceItemHeader::Item { .. } => Tag(0xFFFE, 0xE000),
SequenceItemHeader::ItemDelimiter => Tag(0xFFFE, 0xE00D),
SequenceItemHeader::SequenceDelimiter => Tag(0xFFFE, 0xE0DD),
}
}

fn len(&self) -> Length {
match *self {
SequenceItemHeader::Item { len } => len,
SequenceItemHeader::ItemDelimiter | SequenceItemHeader::SequenceDelimiter => Length(0),
}
}
}

/// An enum type for a DICOM value representation.
Expand Down
Loading