1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
#![crate_type = "lib"]
#![deny(trivial_numeric_casts, unsafe_code, unstable_features)]
#![warn(
    missing_debug_implementations,
    unused_qualifications,
    unused_import_braces
)]
#![allow(clippy::derive_partial_eq_without_eq)]

//! This is the core library of DICOM-rs containing various concepts,
//! data structures and traits specific to DICOM content.
//!
//! The current structure of this crate is as follows:
//!
//! - [`header`] comprises various data types for DICOM element header,
//!   including common definitions for DICOM tags and value representations.
//! - [`dictionary`] describes common behavior of DICOM data dictionaries,
//!   which translate attribute names and/or tags to a dictionary entry
//!   containing relevant information about the attribute.
//! - [`ops`] provides constructs for defining
//!   operations on DICOM attributes,
//!   to be applied on types resembling DICOM objects or data sets.
//! - [`value`] holds definitions for values in standard DICOM elements,
//!   with the awareness of multiplicity, representation,
//!   and the possible presence of sequences.
//!

pub mod dictionary;
pub mod header;
pub mod ops;
pub mod prelude;
pub mod value;

pub use dictionary::DataDictionary;
pub use header::{DataElement, DataElementHeader, Length, Tag, VR};
pub use value::{PrimitiveValue, Value as DicomValue};

// re-export crates that are part of the public API
pub use chrono;
pub use smallvec;

/// Helper macro for constructing a DICOM primitive value,
/// of an arbitrary variant and multiplicity.
///
/// The base syntax is a value type identifier,
/// which is one of the variants of [`PrimitiveValue`],
/// followed by either an expression resolving to one standard Rust value,
/// or an explicitly laid out array of Rust values.
/// The type variant may be omitted in some cases.
///
/// Passing a single expression for multiple values is not supported.
/// Please use standard `From` conversions instead.
///
/// ```none
/// dicom_value!() // empty value
/// dicom_value!(«Type», «expression») // one value
/// dicom_value!(«Type», [«expression1», «expression2», ...]) // multiple values
/// dicom_value!(«expression») // a single value, inferred variant
/// ```
///
/// # Examples:
///
/// Strings are automatically converted to retain ownership.
///
/// ```
/// use dicom_core::value::PrimitiveValue;
/// use dicom_core::{DicomValue, dicom_value};
///
/// let value = dicom_value!(Str, "Smith^John");
/// assert_eq!(
///     value,
///     PrimitiveValue::Str("Smith^John".to_owned()),
/// );
/// ```
///
/// A DICOM value may also have multiple elements:
///
/// ```
/// # use dicom_core::value::PrimitiveValue;
/// # use dicom_core::dicom_value;
/// let value = dicom_value!(Strs, [
///     "Smith^John",
///     "Simões^João",
/// ]);
/// assert_eq!(
///     value,
///     PrimitiveValue::Strs([
///         "Smith^John".to_string(),
///         "Simões^João".to_string(),
///     ][..].into()),
/// );
/// let value = dicom_value!(U16, [5, 6, 7]);
/// assert_eq!(
///     value,
///     PrimitiveValue::U16([5, 6, 7][..].into()),
/// );
/// ```
///
/// The output is a [`PrimitiveValue`],
/// which can be converted to a `DicomValue` as long as its type parameters
/// are specified or inferable.
///
/// ```
/// # use dicom_core::header::EmptyObject;
/// # use dicom_core::value::PrimitiveValue;
/// # use dicom_core::{DicomValue, dicom_value};
/// # let value = dicom_value!(U16, [5, 6, 7]);
/// // conversion to a DicomValue only requires its type parameters
/// // to be specified or inferable.
/// assert_eq!(
///     DicomValue::from(value),
///     DicomValue::<EmptyObject, ()>::Primitive(
///         PrimitiveValue::U16([5, 6, 7][..].into())),
/// );
/// ```
///
/// [`PrimitiveValue`]: ./enum.PrimitiveValue.html
#[macro_export]
macro_rules! dicom_value {
    // Empty value
    () => { $crate::value::PrimitiveValue::Empty };
    // Multiple strings
    (Strs, [ $($elem: expr),+ , ]) => {
        $crate::value::PrimitiveValue :: Strs ($crate::smallvec::smallvec![$($elem.to_owned(),)*])
    };
    (Strs, [ $($elem: expr),+ ]) => {
        $crate::value::PrimitiveValue :: Strs ($crate::smallvec::smallvec![$($elem.to_owned(),)*])
    };
    ($typ: ident, [ $($elem: expr),+ , ]) => {
        $crate::value::PrimitiveValue :: $typ ($crate::smallvec::smallvec![$($elem,)*])
    };
    ($typ: ident, [ $($elem: expr),+ ]) => {
        $crate::value::PrimitiveValue :: $typ ($crate::smallvec::smallvec![$($elem,)*])
    };
    (Str, $elem: expr) => {
        $crate::value::PrimitiveValue :: Str (String::from($elem))
    };
    ($typ: ident, $elem: expr) => {
        $crate::value::PrimitiveValue :: $typ ($crate::value::C::from_elem($elem, 1))
    };
    ($elem: expr) => {
        $crate::value::PrimitiveValue::from($elem)
    };
}

#[cfg(test)]
mod tests {
    use crate::value::PrimitiveValue;
    use smallvec::smallvec;

    #[test]
    fn macro_dicom_value() {
        // single string with variant
        assert_eq!(
            dicom_value!(Str, "PALETTE COLOR "),
            PrimitiveValue::Str("PALETTE COLOR ".to_owned()),
        );

        // single string without variant
        assert_eq!(
            dicom_value!("PALETTE COLOR "),
            PrimitiveValue::Str("PALETTE COLOR ".to_owned()),
        );

        // multiple string literals with variant, no trailing comma
        assert_eq!(
            dicom_value!(Strs, ["BASE", "LIGHT", "DARK"]),
            PrimitiveValue::Strs(smallvec![
                "BASE".to_owned(),
                "LIGHT".to_owned(),
                "DARK".to_owned(),
            ]),
        );

        // multiple strings and string slices with variant, no trailing comma
        assert_eq!(
            dicom_value!(
                Strs,
                [
                    "DERIVED",
                    "PRIMARY".to_string(), // accepts both &str and String
                    "WHOLE BODY",
                    "EMISSION"
                ]
            ),
            PrimitiveValue::Strs(smallvec![
                "DERIVED".to_string(),
                "PRIMARY".to_string(),
                "WHOLE BODY".to_string(),
                "EMISSION".to_string(),
            ]),
        );

        // multiple string literals with variant, with trailing comma
        assert_eq!(
            dicom_value!(Strs, ["DERIVED", "PRIMARY", "WHOLE BODY", "EMISSION",]),
            PrimitiveValue::Strs(smallvec![
                "DERIVED".to_string(),
                "PRIMARY".to_string(),
                "WHOLE BODY".to_string(),
                "EMISSION".to_string(),
            ]),
        );

        // single number with variant
        assert_eq!(dicom_value!(U16, 55), PrimitiveValue::U16(smallvec![55]),);

        // single number without variant
        assert_eq!(dicom_value!(55_u32), PrimitiveValue::U32(smallvec![55]),);

        // multiple numbers without variant, no trailing comma
        assert_eq!(
            dicom_value!(I32, [11, 22, 33]),
            PrimitiveValue::I32(smallvec![11, 22, 33]),
        );

        // empty value
        assert_eq!(dicom_value!(), PrimitiveValue::Empty,);
    }
}