//! This modules contains an assortment of types required for interpreting DICOM data elements.
//! It comprises a variety of basic data types, such as the DICOM attribute tag, the
//! element header, and element composite types.
use crate::value::{
CastValueError, ConvertValueError, DicomDate, DicomDateTime, DicomTime, PrimitiveValue, Value,
};
use chrono::FixedOffset;
use num_traits::NumCast;
use snafu::{ensure, Backtrace, Snafu};
use std::borrow::Cow;
use std::cmp::Ordering;
use std::fmt;
use std::str::{from_utf8, FromStr};
/// Error type for issues constructing a sequence item header.
#[derive(Debug, Snafu)]
#[non_exhaustive]
pub enum SequenceItemHeaderError {
/// Unexpected header tag.
/// Only Item (0xFFFE, 0xE000),
/// Item Delimiter (0xFFFE, 0xE00D),
/// or Sequence Delimiter (0xFFFE, 0xE0DD)
/// are admitted.
#[snafu(display("Unexpected tag {}", tag))]
UnexpectedTag { tag: Tag, backtrace: Backtrace },
/// Unexpected delimiter value length.
/// Must be zero for item delimiters.
#[snafu(display("Unexpected delimiter length {}", len))]
UnexpectedDelimiterLength { len: Length, backtrace: Backtrace },
}
type Result<T, E = SequenceItemHeaderError> = std::result::Result<T, E>;
/// 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;
/// Check whether the value is empty (0 length).
fn is_empty(&self) -> bool {
self.length() == Length(0)
}
}
/// A trait for a data type containing a DICOM header.
#[allow(clippy::len_without_is_empty)]
pub trait Header: HasLength {
/// Retrieve the element's tag as a `(group, element)` tuple.
fn tag(&self) -> Tag;
/// Check whether this is the header of an item.
fn is_item(&self) -> bool {
self.tag() == Tag(0xFFFE, 0xE000)
}
/// Check whether this is the header of an item delimiter.
fn is_item_delimiter(&self) -> bool {
self.tag() == Tag(0xFFFE, 0xE00D)
}
/// Check whether this is the header of a sequence delimiter.
fn is_sequence_delimiter(&self) -> bool {
self.tag() == Tag(0xFFFE, 0xE0DD)
}
/// Check whether this is the header of an encapsulated pixel data.
fn is_encapsulated_pixeldata(&self) -> bool {
self.tag() == Tag(0x7FE0, 0x0010) && self.length().is_undefined()
}
}
/// 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, Copy, Clone, Eq, Hash, PartialEq, Ord, PartialOrd)]
pub enum EmptyObject {}
impl HasLength for EmptyObject {
fn length(&self) -> Length {
unreachable!()
}
}
/// A data type that represents and owns a DICOM data element.
///
/// This type is capable of representing any data element fully in memory,
/// whether it be a primitive value,
/// a nested data set (where each item contains an object of type `I`),
/// or an encapsulated pixel data sequence (each item of type `P`).
#[derive(Debug, PartialEq, Clone)]
pub struct DataElement<I = EmptyObject, P = [u8; 0]> {
header: DataElementHeader,
value: Value<I, P>,
}
/// A data type that represents and owns a DICOM data element
/// containing a primitive value.
#[derive(Debug, PartialEq, Clone)]
pub struct PrimitiveDataElement {
header: DataElementHeader,
value: PrimitiveValue,
}
impl PrimitiveDataElement {
/// Main constructor for a primitive data element.
pub fn new(header: DataElementHeader, value: PrimitiveValue) -> Self {
PrimitiveDataElement { header, value }
}
}
impl<I, P> From<PrimitiveDataElement> for DataElement<I, P> {
fn from(o: PrimitiveDataElement) -> Self {
DataElement {
header: o.header,
value: o.value.into(),
}
}
}
/// A data type that represents a DICOM data element with
/// a borrowed value.
#[derive(Debug, PartialEq, Clone)]
pub struct DataElementRef<'v, I: 'v, P: 'v> {
header: DataElementHeader,
value: &'v Value<I, P>,
}
/// A data type that represents a DICOM data element with
/// a borrowed primitive value.
#[derive(Debug, PartialEq, Clone)]
pub struct PrimitiveDataElementRef<'v> {
header: DataElementHeader,
value: &'v PrimitiveValue,
}
impl<'a> PrimitiveDataElementRef<'a> {
/// Main constructor for a primitive data element reference.
pub fn new(header: DataElementHeader, value: &'a PrimitiveValue) -> Self {
PrimitiveDataElementRef { header, value }
}
}
impl<I, P> HasLength for DataElement<I, P> {
#[inline]
fn length(&self) -> Length {
self.header.length()
}
}
impl<I, P> Header for DataElement<I, P> {
#[inline]
fn tag(&self) -> Tag {
self.header.tag()
}
}
impl<I, P> HasLength for &DataElement<I, P> {
#[inline]
fn length(&self) -> Length {
(**self).length()
}
}
impl<'a, I, P> Header for &'a DataElement<I, P> {
#[inline]
fn tag(&self) -> Tag {
(**self).tag()
}
}
impl<'v, I, P> HasLength for DataElementRef<'v, I, P> {
#[inline]
fn length(&self) -> Length {
self.header.length()
}
}
impl<'v, I, P> Header for DataElementRef<'v, I, P> {
#[inline]
fn tag(&self) -> Tag {
self.header.tag()
}
}
impl<I, P> DataElement<I, P> {
/// Create an empty data element.
pub fn empty(tag: Tag, vr: VR) -> Self {
DataElement {
header: DataElementHeader {
tag,
vr,
len: Length(0),
},
value: PrimitiveValue::Empty.into(),
}
}
/// 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, P> {
&self.value
}
/// Move the data value out of the element, discarding the rest. If the
/// value is a sequence, its lifetime may still be bound to its original
/// source.
pub fn into_value(self) -> Value<I, P> {
self.value
}
}
impl<I, P> DataElement<I, P>
where
I: HasLength,
{
/// Create a primitive data element from the given parts,
/// where the length is inferred from the value's byte length.
///
/// If the value is textual,
/// the byte length of that value encoded in UTF-8 is assumed.
/// If you already have a length in this context,
/// prefer calling `new_with_len` instead.
///
/// This method will not check whether the value representation is
/// compatible with the given value.
pub fn new<T>(tag: Tag, vr: VR, value: T) -> Self
where
T: Into<Value<I, P>>,
{
let value = value.into();
DataElement {
header: DataElementHeader {
tag,
vr,
len: value.length(),
},
value,
}
}
/// Create a primitive data element from the given parts.
///
/// This method will not check
/// whether the length accurately represents the given value's byte length,
/// nor whether the value representation is compatible with the value.
pub fn new_with_len<T>(tag: Tag, vr: VR, length: Length, value: T) -> Self
where
T: Into<Value<I, P>>,
{
let value = value.into();
DataElement {
header: DataElementHeader {
tag,
vr,
len: length,
},
value,
}
}
/// Retrieve the element's value as a single clean string,
/// with no trailing whitespace.
///
/// Returns an error if the value is not primitive.
pub fn to_str(&self) -> Result<Cow<str>, CastValueError> {
self.value.to_str()
}
/// Retrieve the element's value as a single raw string,
/// with trailing whitespace kept.
///
/// Returns an error if the value is not primitive.
pub fn to_raw_str(&self) -> Result<Cow<str>, CastValueError> {
self.value.to_raw_str()
}
/// Retrieves the element's value as a clean string
#[deprecated(
note = "`to_clean_str()` is now deprecated in favour of using `to_str()` directly.
`to_raw_str()` replaces the old functionality of `to_str()` and maintains all trailing whitespace."
)]
pub fn to_clean_str(&self) -> Result<Cow<str>, CastValueError> {
self.value.to_str()
}
/// Convert the full primitive value into raw bytes.
///
/// String values already encoded with the `Str` and `Strs` variants
/// are provided in UTF-8.
///
/// Returns an error if the value is not primitive.
pub fn to_bytes(&self) -> Result<Cow<[u8]>, CastValueError> {
self.value().to_bytes()
}
/// Convert the full value of the data element into a sequence of strings.
///
/// If the value is a primitive, it will be converted into
/// a vector of strings as described in [`PrimitiveValue::to_multi_str`].
///
/// Returns an error if the value is not primitive.
///
/// [`PrimitiveValue::to_multi_str`]: ../enum.PrimitiveValue.html#to_multi_str
pub fn to_multi_str(&self) -> Result<Cow<[String]>, CastValueError> {
self.value().to_multi_str()
}
/// Retrieve and convert the value of the data element into an integer.
///
/// If the value is a primitive,
/// it will be converted into an integer
/// as described in [`PrimitiveValue::to_int`].
///
/// Returns an error if the value is not primitive.
///
/// [`PrimitiveValue::to_int`]: ../enum.PrimitiveValue.html#to_int
pub fn to_int<T>(&self) -> Result<T, ConvertValueError>
where
T: Clone,
T: NumCast,
T: FromStr<Err = std::num::ParseIntError>,
{
self.value().to_int()
}
/// Retrieve and convert the value of the data element
/// into a sequence of integers.
///
/// If the value is a primitive, it will be converted into
/// a vector of integers as described in [PrimitiveValue::to_multi_int].
///
/// [PrimitiveValue::to_multi_int]: ../enum.PrimitiveValue.html#to_multi_int
pub fn to_multi_int<T>(&self) -> Result<Vec<T>, ConvertValueError>
where
T: Clone,
T: NumCast,
T: FromStr<Err = std::num::ParseIntError>,
{
self.value().to_multi_int()
}
/// Retrieve and convert the value of the data element
/// into a single-precision floating point number.
///
/// If the value is a primitive, it will be converted into
/// a number as described in [`PrimitiveValue::to_float32`].
///
/// Returns an error if the value is not primitive.
///
/// [`PrimitiveValue::to_float32`]: ../enum.PrimitiveValue.html#to_float32
pub fn to_float32(&self) -> Result<f32, ConvertValueError> {
self.value().to_float32()
}
/// Retrieve and convert the value of the data element
/// into a sequence of single-precision floating point numbers.
///
/// If the value is a primitive, it will be converted into
/// a vector of numbers as described in [`PrimitiveValue::to_multi_float32`].
///
/// Returns an error if the value is not primitive.
///
/// [`PrimitiveValue::to_multi_float32`]: ../enum.PrimitiveValue.html#to_multi_float32
pub fn to_multi_float32(&self) -> Result<Vec<f32>, ConvertValueError> {
self.value().to_multi_float32()
}
/// Retrieve and convert the value of the data element
/// into a double-precision floating point number.
///
/// If the value is a primitive, it will be converted into
/// a number as described in [`PrimitiveValue::to_float64`].
///
/// Returns an error if the value is not primitive.
///
/// [`PrimitiveValue::to_float64`]: ../enum.PrimitiveValue.html#to_float64
pub fn to_float64(&self) -> Result<f64, ConvertValueError> {
self.value().to_float64()
}
/// Retrieve and convert the value of the data element
/// into a sequence of double-precision floating point numbers.
///
/// If the value is a primitive, it will be converted into
/// a vector of numbers as described in [`PrimitiveValue::to_multi_float64`].
///
/// Returns an error if the value is not primitive.
///
/// [`PrimitiveValue::to_multi_float64`]: ../enum.PrimitiveValue.html#to_multi_float64
pub fn to_multi_float64(&self) -> Result<Vec<f64>, ConvertValueError> {
self.value().to_multi_float64()
}
/// Retrieve and convert the primitive value into a date.
///
/// If the value is a primitive, it will be converted into
/// a `DicomDate` as described in [`PrimitiveValue::to_date`].
///
/// Returns an error if the value is not primitive.
///
pub fn to_date(&self) -> Result<DicomDate, ConvertValueError> {
self.value().to_date()
}
/// Retrieve and convert the primitive value into a sequence of dates.
///
/// If the value is a primitive, it will be converted into
/// a vector of `DicomDate` as described in [`PrimitiveValue::to_multi_date`].
///
/// Returns an error if the value is not primitive.
///
pub fn to_multi_date(&self) -> Result<Vec<DicomDate>, ConvertValueError> {
self.value().to_multi_date()
}
/// Retrieve and convert the primitive value into a time.
///
/// If the value is a primitive, it will be converted into
/// a `DicomTime` as described in [`PrimitiveValue::to_time`].
///
/// Returns an error if the value is not primitive.
///
pub fn to_time(&self) -> Result<DicomTime, ConvertValueError> {
self.value().to_time()
}
/// Retrieve and convert the primitive value into a sequence of times.
///
/// If the value is a primitive, it will be converted into
/// a vector of `DicomTime` as described in [`PrimitiveValue::to_multi_time`].
///
/// Returns an error if the value is not primitive.
///
pub fn to_multi_time(&self) -> Result<Vec<DicomTime>, ConvertValueError> {
self.value().to_multi_time()
}
/// Retrieve and convert the primitive value into a date-time.
///
/// If the value is a primitive, it will be converted into
/// a `DicomDateTime` as described in [`PrimitiveValue::to_datetime`].
///
/// Returns an error if the value is not primitive.
///
pub fn to_datetime(
&self,
default_offset: FixedOffset,
) -> Result<DicomDateTime, ConvertValueError> {
self.value().to_datetime(default_offset)
}
/// Retrieve and convert the primitive value into a sequence of date-times.
///
/// If the value is a primitive, it will be converted into
/// a vector of `DicomDateTime` as described in [`PrimitiveValue::to_multi_datetime`].
///
/// Returns an error if the value is not primitive.
///
pub fn to_multi_datetime(
&self,
default_offset: FixedOffset,
) -> Result<Vec<DicomDateTime>, ConvertValueError> {
self.value().to_multi_datetime(default_offset)
}
/// Retrieve the items stored in a sequence value.
///
/// Returns `None` if the value is not a sequence.
pub fn items(&self) -> Option<&[I]> {
self.value().items()
}
}
impl<'v, I, P> DataElementRef<'v, I, P>
where
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
/// is advised.
pub fn new(tag: Tag, vr: VR, value: &'v Value<I, P>) -> Self {
DataElementRef {
header: DataElementHeader {
tag,
vr,
len: value.length(),
},
value,
}
}
/// Retrieves the element's value representation, which can be unknown.
pub fn vr(&self) -> VR {
self.header.vr()
}
/// Retrieves the DICOM value.
pub fn value(&self) -> &Value<I, P> {
self.value
}
}
/// Macro for implementing getters to single and multi-values,
/// by delegating to `Value`.
///
/// Should be placed inside `DataElement`'s impl block.
macro_rules! impl_primitive_getters {
($name_single: ident, $name_multi: ident, $variant: ident, $ret: ty) => {
/// Get a single value of the requested type.
///
/// If it contains multiple values,
/// only the first one is returned.
/// An error is returned if the variant is not compatible.
pub fn $name_single(&self) -> Result<$ret, CastValueError> {
self.value().$name_single()
}
/// Get a sequence of values of the requested type without copying.
///
/// An error is returned if the variant is not compatible.
pub fn $name_multi(&self) -> Result<&[$ret], CastValueError> {
self.value().$name_multi()
}
};
}
impl<I, P> DataElement<I, P> {
/// Get a single string value.
///
/// If it contains multiple strings,
/// only the first one is returned.
///
/// An error is returned if the variant is not compatible.
///
/// To enable conversions of other variants to a textual representation,
/// see [`to_str()`] instead.
///
/// [`to_str()`]: #method.to_str
pub fn string(&self) -> Result<&str, CastValueError> {
self.value().string()
}
/// Get the inner sequence of string values
/// if the variant is either `Str` or `Strs`.
///
/// An error is returned if the variant is not compatible.
///
/// To enable conversions of other variants to a textual representation,
/// see [`to_str()`] instead.
///
/// [`to_str()`]: #method.to_str
pub fn strings(&self) -> Result<&[String], CastValueError> {
self.value().strings()
}
impl_primitive_getters!(date, dates, Date, DicomDate);
impl_primitive_getters!(time, times, Time, DicomTime);
impl_primitive_getters!(datetime, datetimes, DateTime, DicomDateTime);
impl_primitive_getters!(uint8, uint8_slice, U8, u8);
impl_primitive_getters!(uint16, uint16_slice, U16, u16);
impl_primitive_getters!(int16, int16_slice, I16, i16);
impl_primitive_getters!(uint32, uint32_slice, U32, u32);
impl_primitive_getters!(int32, int32_slice, I32, i32);
impl_primitive_getters!(int64, int64_slice, I64, i64);
impl_primitive_getters!(uint64, uint64_slice, U64, u64);
impl_primitive_getters!(float32, float32_slice, F32, f32);
impl_primitive_getters!(float64, float64_slice, F64, f64);
}
/// A data structure for a data element header, containing
/// a tag, value representation and specified length.
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct DataElementHeader {
/// DICOM tag
pub tag: Tag,
/// Value Representation
pub vr: VR,
/// Element length
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
}
}
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(),
vr,
len,
}
}
/// Retrieve the element's value representation, which can be unknown.
#[inline]
pub fn vr(&self) -> VR {
self.vr
}
/// Check whether the header suggests the value to be a sequence value:
/// if the value representation is SQ or the length is undefined.
#[inline]
pub fn is_non_primitive(&self) -> bool {
self.vr == VR::SQ || self.length().is_undefined()
}
}
impl From<SequenceItemHeader> for DataElementHeader {
fn from(value: SequenceItemHeader) -> DataElementHeader {
DataElementHeader {
tag: value.tag(),
vr: VR::UN,
len: value.length(),
}
}
}
/// Data type for describing a sequence item data element.
/// If the element represents an item, it will also contain
/// the specified length.
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum SequenceItemHeader {
/// The cursor contains an item.
Item {
/// the length of the item in bytes (can be 0xFFFFFFFF if undefined)
len: Length,
},
/// The cursor read an item delimiter.
/// The element ends here and should not be read any further.
ItemDelimiter,
/// The cursor read a sequence delimiter.
/// The element ends here and should not be read any further.
SequenceDelimiter,
}
impl SequenceItemHeader {
/// Create a sequence item header using the element's raw properties.
/// An error can be raised if the given properties do not relate to a
/// sequence item, a sequence item delimiter or a sequence delimiter.
pub fn new<T: Into<Tag>>(tag: T, len: Length) -> Result<SequenceItemHeader> {
match tag.into() {
Tag(0xFFFE, 0xE000) => {
// item
Ok(SequenceItemHeader::Item { len })
}
Tag(0xFFFE, 0xE00D) => {
// item delimiter
// delimiters should not have a positive length
if len != Length(0) {
UnexpectedDelimiterLengthSnafu { len }.fail()
} else {
Ok(SequenceItemHeader::ItemDelimiter)
}
}
Tag(0xFFFE, 0xE0DD) => {
// sequence delimiter
Ok(SequenceItemHeader::SequenceDelimiter)
}
tag => UnexpectedTagSnafu { tag }.fail(),
}
}
}
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),
}
}
}
/// An enum type for a DICOM value representation.
#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone, Ord, PartialOrd)]
pub enum VR {
/// Application Entity
AE,
/// Age String
AS,
/// Attribute Tag
AT,
/// Code String
CS,
/// Date
DA,
/// Decimal String
DS,
/// Date Time
DT,
/// Floating Point Single
FL,
/// Floating Point Double
FD,
/// Integer String
IS,
/// Long String
LO,
/// Long Text
LT,
/// Other Byte
OB,
/// Other Double
OD,
/// Other Float
OF,
/// Other Long
OL,
/// Other Very Long
OV,
/// Other Word
OW,
/// Person Name
PN,
/// Short String
SH,
/// Signed Long
SL,
/// Sequence of Items
SQ,
/// Signed Short
SS,
/// Short Text
ST,
/// Signed Very Long
SV,
/// Time
TM,
/// Unlimited Characters
UC,
/// Unique Identifier (UID)
UI,
/// Unsigned Long
UL,
/// Unknown
UN,
/// Universal Resource Identifier or Universal Resource Locator (URI/URL)
UR,
/// Unsigned Short
US,
/// Unlimited Text
UT,
/// Unsigned Very Long
UV,
}
impl VR {
/// Obtain the value representation corresponding to the given two bytes.
/// Each byte should represent an alphabetic character in upper case.
pub fn from_binary(chars: [u8; 2]) -> Option<Self> {
from_utf8(chars.as_ref())
.ok()
.and_then(|s| VR::from_str(s).ok())
}
/// Retrieve a string representation of this VR.
pub fn to_string(self) -> &'static str {
use VR::*;
match self {
AE => "AE",
AS => "AS",
AT => "AT",
CS => "CS",
DA => "DA",
DS => "DS",
DT => "DT",
FL => "FL",
FD => "FD",
IS => "IS",
LO => "LO",
LT => "LT",
OB => "OB",
OD => "OD",
OF => "OF",
OL => "OL",
OV => "OV",
OW => "OW",
PN => "PN",
SH => "SH",
SL => "SL",
SQ => "SQ",
SS => "SS",
ST => "ST",
SV => "SV",
TM => "TM",
UC => "UC",
UI => "UI",
UL => "UL",
UN => "UN",
UR => "UR",
US => "US",
UT => "UT",
UV => "UV",
}
}
/// Retrieve a copy of this VR's byte representation.
/// The function returns two alphabetic characters in upper case.
pub fn to_bytes(self) -> [u8; 2] {
let bytes = self.to_string().as_bytes();
[bytes[0], bytes[1]]
}
}
/// Obtain the value representation corresponding to the given string.
/// The string should hold exactly two UTF-8 encoded alphabetic characters
/// in upper case, otherwise no match is made.
impl FromStr for VR {
type Err = &'static str;
fn from_str(string: &str) -> std::result::Result<Self, Self::Err> {
use VR::*;
match string {
"AE" => Ok(AE),
"AS" => Ok(AS),
"AT" => Ok(AT),
"CS" => Ok(CS),
"DA" => Ok(DA),
"DS" => Ok(DS),
"DT" => Ok(DT),
"FL" => Ok(FL),
"FD" => Ok(FD),
"IS" => Ok(IS),
"LO" => Ok(LO),
"LT" => Ok(LT),
"OB" => Ok(OB),
"OD" => Ok(OD),
"OF" => Ok(OF),
"OL" => Ok(OL),
"OV" => Ok(OV),
"OW" => Ok(OW),
"PN" => Ok(PN),
"SH" => Ok(SH),
"SL" => Ok(SL),
"SQ" => Ok(SQ),
"SS" => Ok(SS),
"ST" => Ok(ST),
"SV" => Ok(SV),
"TM" => Ok(TM),
"UC" => Ok(UC),
"UI" => Ok(UI),
"UL" => Ok(UL),
"UN" => Ok(UN),
"UR" => Ok(UR),
"US" => Ok(US),
"UT" => Ok(UT),
"UV" => Ok(UV),
_ => Err("no such value representation"),
}
}
}
impl fmt::Display for VR {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(VR::to_string(*self))
}
}
/// Idiomatic alias for a tag's group number.
pub type GroupNumber = u16;
/// Idiomatic alias for a tag's element number.
pub type ElementNumber = u16;
/// The data type for DICOM data element tags.
///
/// Since types will not have a monomorphized tag, and so will only support
/// a (group, element) pair. For this purpose, `Tag` also provides a method
/// for converting it to a tuple. Both `(u16, u16)` and `[u16; 2]` can be
/// efficiently converted to this type as well.
///
/// Moreover, strings following the conventional text format
/// found in the DICOM standard
/// (e.g. `(7FE0,0010)`)
/// can be parsed using its `FromStr` implementation.
///
/// # Example
///
/// ```
/// # use dicom_core::Tag;
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let tag: Tag = "(0010,1005)".parse()?;
/// assert_eq!(tag, Tag(0x0010, 0x1005));
/// Ok(())
/// # }
/// ```
#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Clone, Copy)]
pub struct Tag(pub GroupNumber, pub ElementNumber);
impl Tag {
/// Getter for the tag's group value.
#[inline]
pub fn group(self) -> GroupNumber {
self.0
}
/// Getter for the tag's element value.
#[inline]
pub fn element(self) -> ElementNumber {
self.1
}
}
impl fmt::Debug for Tag {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Tag({:#06X?}, {:#06X?})", self.0, self.1)
}
}
impl fmt::Display for Tag {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({:04X},{:04X})", self.0, self.1)
}
}
impl PartialEq<(u16, u16)> for Tag {
fn eq(&self, other: &(u16, u16)) -> bool {
self.0 == other.0 && self.1 == other.1
}
}
impl PartialEq<[u16; 2]> for Tag {
fn eq(&self, other: &[u16; 2]) -> bool {
self.0 == other[0] && self.1 == other[1]
}
}
impl From<(u16, u16)> for Tag {
#[inline]
fn from(value: (u16, u16)) -> Tag {
Tag(value.0, value.1)
}
}
impl From<[u16; 2]> for Tag {
#[inline]
fn from(value: [u16; 2]) -> Tag {
Tag(value[0], value[1])
}
}
/// Could not parse DICOM tag
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, Snafu)]
pub enum ParseTagError {
/// expected tag start '('
Start,
/// expected tag part separator ','
Separator,
/// expected tag end ')'
End,
/// unexpected length
Length,
/// Illegal character for hexadecimal number
Number,
}
/// This parser implementation for tags
/// accepts strictly one of the following formats:
/// - `(gggg,eeee)`
/// - or `gggg,eeee`
///
/// where `gggg` and `eeee` are the characters representing
/// the group part an the element part in hexadecimal,
/// with four characters each.
/// Lowercase and uppercase characters are allowed.
impl FromStr for Tag {
type Err = ParseTagError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.len() {
11 => {
// (gggg,eeee)
ensure!(s.starts_with('('), StartSnafu);
let (num_g, rest) = parse_tag_part(&s[1..])?;
ensure!(rest.starts_with(','), SeparatorSnafu);
let (num_e, rest) = parse_tag_part(&rest[1..])?;
ensure!(rest == ")", EndSnafu);
Ok(Tag(num_g, num_e))
}
9 => {
// gggg,eeee
let (num_g, rest) = parse_tag_part(s)?;
ensure!(rest.starts_with(','), SeparatorSnafu);
let (num_e, _) = parse_tag_part(&rest[1..])?;
Ok(Tag(num_g, num_e))
}
_ => Err(ParseTagError::Length),
}
}
}
fn parse_tag_part(s: &str) -> Result<(u16, &str), ParseTagError> {
ensure!(s.is_char_boundary(4), NumberSnafu);
let (num, rest) = s.split_at(4);
ensure!(
num.chars().all(|c| ('0'..='9').contains(&c)
|| ('A'..='F').contains(&c)
|| ('a'..='f').contains(&c)),
NumberSnafu
);
let num = u16::from_str_radix(num, 16).expect("failed to parse tag part");
Ok((num, rest))
}
/// A type for representing data set content length, in bytes.
/// An internal value of `0xFFFF_FFFF` represents an undefined
/// (unspecified) length, which would have to be determined
/// with a traversal based on the content's encoding.
///
/// This also means that numeric comparisons and arithmetic
/// do not function the same way as primitive number types:
///
/// Two length of undefined length are not equal.
///
/// ```
/// # use dicom_core::Length;
/// assert_ne!(Length::UNDEFINED, Length::UNDEFINED);
/// ```
///
/// Any addition or substraction with at least one undefined
/// length results in an undefined length.
///
/// ```
/// # use dicom_core::Length;
/// assert!((Length::defined(64) + Length::UNDEFINED).is_undefined());
/// assert!((Length::UNDEFINED + 8).is_undefined());
/// ```
///
/// Comparing between at least one undefined length is always `false`.
///
/// ```
/// # use dicom_core::Length;
/// assert!(Length::defined(16) < Length::defined(64));
/// assert!(!(Length::UNDEFINED < Length::defined(64)));
/// assert!(!(Length::UNDEFINED > Length::defined(64)));
///
/// assert!(!(Length::UNDEFINED < Length::UNDEFINED));
/// assert!(!(Length::UNDEFINED > Length::UNDEFINED));
/// assert!(!(Length::UNDEFINED <= Length::UNDEFINED));
/// assert!(!(Length::UNDEFINED >= Length::UNDEFINED));
/// ```
///
#[derive(Clone, Copy)]
pub struct Length(pub u32);
const UNDEFINED_LEN: u32 = 0xFFFF_FFFF;
impl Length {
/// A length that is undefined.
pub const UNDEFINED: Self = Length(UNDEFINED_LEN);
/// Create a new length value from its internal representation.
/// This is equivalent to `Length(len)`.
#[inline]
pub fn new(len: u32) -> Self {
Length(len)
}
/// Create a new length value with the given number of bytes.
///
/// # Panic
///
/// This function will panic if `len` represents an undefined length.
#[inline]
pub fn defined(len: u32) -> Self {
assert_ne!(len, UNDEFINED_LEN);
Length(len)
}
}
impl From<u32> for Length {
#[inline]
fn from(o: u32) -> Self {
Length(o)
}
}
impl PartialEq<Length> for Length {
fn eq(&self, rhs: &Length) -> bool {
match (self.0, rhs.0) {
(UNDEFINED_LEN, _) | (_, UNDEFINED_LEN) => false,
(l1, l2) => l1 == l2,
}
}
}
impl PartialOrd<Length> for Length {
fn partial_cmp(&self, rhs: &Length) -> Option<Ordering> {
match (self.0, rhs.0) {
(UNDEFINED_LEN, _) | (_, UNDEFINED_LEN) => None,
(l1, l2) => Some(l1.cmp(&l2)),
}
}
}
impl ::std::ops::Add<Length> for Length {
type Output = Self;
fn add(self, rhs: Length) -> Self::Output {
match (self.0, rhs.0) {
(UNDEFINED_LEN, _) | (_, UNDEFINED_LEN) => Length::UNDEFINED,
(l1, l2) => {
let o = l1 + l2;
debug_assert!(
o != UNDEFINED_LEN,
"integer overflow (0xFFFF_FFFF reserved for undefined length)"
);
Length(o)
}
}
}
}
impl ::std::ops::Add<i32> for Length {
type Output = Self;
fn add(self, rhs: i32) -> Self::Output {
match self.0 {
UNDEFINED_LEN => Length::UNDEFINED,
len => {
let o = (len as i32 + rhs) as u32;
debug_assert!(
o != UNDEFINED_LEN,
"integer overflow (0xFFFF_FFFF reserved for undefined length)"
);
Length(o)
}
}
}
}
impl std::ops::Sub<Length> for Length {
type Output = Self;
fn sub(self, rhs: Length) -> Self::Output {
let mut o = self;
o -= rhs;
o
}
}
impl std::ops::SubAssign<Length> for Length {
fn sub_assign(&mut self, rhs: Length) {
match (self.0, rhs.0) {
(UNDEFINED_LEN, _) | (_, UNDEFINED_LEN) => (), // no-op
(_, l2) => {
self.0 -= l2;
debug_assert!(
self.0 != UNDEFINED_LEN,
"integer overflow (0xFFFF_FFFF reserved for undefined length)"
);
}
}
}
}
impl std::ops::Sub<i32> for Length {
type Output = Self;
fn sub(self, rhs: i32) -> Self::Output {
let mut o = self;
o -= rhs;
o
}
}
impl std::ops::SubAssign<i32> for Length {
fn sub_assign(&mut self, rhs: i32) {
match self.0 {
UNDEFINED_LEN => (), // no-op
len => {
self.0 = (len as i32 - rhs) as u32;
debug_assert!(
self.0 != UNDEFINED_LEN,
"integer overflow (0xFFFF_FFFF reserved for undefined length)"
);
}
}
}
}
impl Length {
/// Check whether this length is undefined (unknown).
#[inline]
pub fn is_undefined(self) -> bool {
self.0 == UNDEFINED_LEN
}
/// Check whether this length is well defined (not undefined).
#[inline]
pub fn is_defined(self) -> bool {
!self.is_undefined()
}
/// Fetch the concrete length value, if available.
/// Returns `None` if it represents an undefined length.
#[inline]
pub fn get(self) -> Option<u32> {
match self.0 {
UNDEFINED_LEN => None,
v => Some(v),
}
}
/// Check whether the length is equally specified as another length.
/// Unlike the implemented `PartialEq`, two undefined lengths are
/// considered equivalent by this method.
#[inline]
pub fn inner_eq(self, other: Length) -> bool {
self.0 == other.0
}
}
impl fmt::Debug for Length {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {
UNDEFINED_LEN => f.write_str("Length(Undefined)"),
l => f.debug_tuple("Length").field(&l).finish(),
}
}
}
impl fmt::Display for Length {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {
UNDEFINED_LEN => f.write_str("U/L"),
l => write!(f, "{}", &l),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{dicom_value, DicomValue};
#[test]
fn to_clean_string() {
let p = dicom_value!(U16, [256, 0, 16]);
let val = DicomValue::new(p);
let element = DataElement::new(Tag(0x0028, 0x3002), VR::US, val);
assert_eq!(element.to_str().unwrap(), "256\\0\\16",);
}
#[test]
fn tag_from_u16_pair() {
let t = Tag::from((0x0010u16, 0x0020u16));
assert_eq!(0x0010u16, t.group());
assert_eq!(0x0020u16, t.element());
}
#[test]
fn tag_from_u16_array() {
let t = Tag::from([0x0010u16, 0x0020u16]);
assert_eq!(0x0010u16, t.group());
assert_eq!(0x0020u16, t.element());
}
#[test]
fn get_date_value() {
let data_element: DataElement<_, _> = DataElement::new(
Tag(0x0010, 0x0030),
VR::DA,
Value::new(PrimitiveValue::from("19941012")),
);
assert_eq!(
data_element.to_date().unwrap(),
DicomDate::from_ymd(1994, 10, 12).unwrap(),
);
}
#[test]
fn create_data_element_from_primitive() {
let data_element: DataElement<EmptyObject, [u8; 0]> = DataElement::new(
Tag(0x0028, 0x3002),
VR::US,
crate::dicom_value!(U16, [256, 0, 16]),
);
assert_eq!(data_element.uint16_slice().unwrap(), &[256, 0, 16]);
}
#[test]
fn parse_tag() {
// with parens, lowercase
let tag: Tag = "(7fe0,0010)".parse().unwrap();
assert_eq!(tag, Tag(0x7FE0, 0x0010));
let tag: Tag = "(003a,001a)".parse().unwrap();
assert_eq!(tag, Tag(0x003A, 0x001A));
// with parens, uppercase
let tag: Tag = "(7FE0,0010)".parse().unwrap();
assert_eq!(tag, Tag(0x7FE0, 0x0010));
let tag: Tag = "(003A,001A)".parse().unwrap();
assert_eq!(tag, Tag(0x003A, 0x001A));
// with parens, mixed case
let tag: Tag = "(003a,001A)".parse().unwrap();
assert_eq!(tag, Tag(0x003A, 0x001A));
// without parens
let tag: Tag = "7fe0,0010".parse().unwrap();
assert_eq!(tag, Tag(0x7FE0, 0x0010));
let tag: Tag = "003a,001a".parse().unwrap();
assert_eq!(tag, Tag(0x003A, 0x001A));
// error case: unsupported number forms
let r: Result<Tag, _> = "+03a,0001".parse();
assert_eq!(r, Err(ParseTagError::Number));
// error case: bad start
let r: Result<Tag, _> = "[baad,0123)".parse();
assert_eq!(r, Err(ParseTagError::Start));
// error case: bad end
let r: Result<Tag, _> = "(baad,0123]".parse();
assert_eq!(r, Err(ParseTagError::End));
// error case: not enough characters
let r: Result<Tag, _> = "(3a,1a)".parse();
assert_eq!(r, Err(ParseTagError::Length));
// error case: bad characters
let r: Result<Tag, _> = "g00d,baad".parse();
assert_eq!(r, Err(ParseTagError::Number));
// error case: missing comma
let r: Result<Tag, _> = "(baad&0123)".parse();
assert_eq!(r, Err(ParseTagError::Separator));
}
}