dicom_core/value/primitive.rs
1//! Declaration and implementation of a DICOM primitive value.
2//!
3//! See [`PrimitiveValue`](./enum.PrimitiveValue.html).
4
5use super::{AsRange, DicomValueType};
6use crate::header::{HasLength, Length, Tag};
7use crate::value::partial::{DateComponent, DicomDate, DicomDateTime, DicomTime};
8use crate::value::person_name::PersonName;
9use crate::value::range::{AmbiguousDtRangeParser, DateRange, DateTimeRange, TimeRange};
10use itertools::Itertools;
11use num_traits::NumCast;
12use safe_transmute::to_bytes::transmute_to_bytes;
13use smallvec::SmallVec;
14use snafu::{Backtrace, ResultExt, Snafu};
15use std::borrow::Cow;
16use std::fmt::{self, Display};
17use std::str::FromStr;
18
19/// Triggered when a value reading attempt fails.
20#[derive(Debug, Snafu)]
21#[non_exhaustive]
22pub enum InvalidValueReadError {
23 /// Attempted to retrieve a complex value as primitive.
24 #[snafu(display("Sequence cannot be read as a primitive value"))]
25 NonPrimitiveType { backtrace: Backtrace },
26 /// Invalid or ambiguous combination of date with time.
27 #[snafu(display("Invalid or ambiguous combination of date with time"))]
28 DateTimeZone { backtrace: Backtrace },
29 /// The value cannot be parsed to a floating point number.
30 #[snafu(display("Failed to read text as a floating point number"))]
31 ParseFloat {
32 backtrace: Backtrace,
33 source: std::num::ParseFloatError,
34 },
35 /// The value cannot be parsed to an integer.
36 #[snafu(display("Failed to read text as an integer"))]
37 ParseInteger {
38 backtrace: Backtrace,
39 source: std::num::ParseIntError,
40 },
41 /// An attempt of reading more than the number of bytes in the length attribute was made.
42 #[snafu(display("Unexpected end of element"))]
43 UnexpectedEndOfElement {},
44 /// The value cannot be converted to the target type requested.
45 #[snafu(display("Cannot convert `{}` to the target type requested", value))]
46 NarrowConvert { value: String, backtrace: Backtrace },
47 #[snafu(display("Failed to read text as a date"))]
48 ParseDate {
49 #[snafu(backtrace)]
50 source: crate::value::deserialize::Error,
51 },
52 #[snafu(display("Failed to read text as a time"))]
53 ParseTime {
54 #[snafu(backtrace)]
55 source: crate::value::deserialize::Error,
56 },
57 #[snafu(display("Failed to read text as a date-time"))]
58 ParseDateTime {
59 #[snafu(backtrace)]
60 source: crate::value::deserialize::Error,
61 },
62 #[snafu(display("Failed to convert into a DicomDate"))]
63 IntoDicomDate {
64 #[snafu(backtrace)]
65 source: crate::value::partial::Error,
66 },
67 #[snafu(display("Failed to convert into a DicomTime"))]
68 IntoDicomTime {
69 #[snafu(backtrace)]
70 source: crate::value::partial::Error,
71 },
72 #[snafu(display("Failed to convert into a DicomDateTime"))]
73 IntoDicomDateTime {
74 #[snafu(backtrace)]
75 source: crate::value::partial::Error,
76 },
77 #[snafu(display("Failed to read text as a date range"))]
78 ParseDateRange {
79 #[snafu(backtrace)]
80 source: crate::value::range::Error,
81 },
82 #[snafu(display("Failed to read text as a time range"))]
83 ParseTimeRange {
84 #[snafu(backtrace)]
85 source: crate::value::range::Error,
86 },
87 #[snafu(display("Failed to read text as a date-time range"))]
88 ParseDateTimeRange {
89 #[snafu(backtrace)]
90 source: crate::value::range::Error,
91 },
92}
93
94/// Error type for a failed attempt to modify an existing DICOM primitive value.
95#[derive(Debug, Snafu)]
96#[non_exhaustive]
97pub enum ModifyValueError {
98 /// The modification using strings cannot proceed
99 /// due to the value's current type,
100 /// as that would lead to mixed representations.
101 #[snafu(display("cannot not modify {:?} value as string values", original))]
102 IncompatibleStringType { original: ValueType },
103
104 /// The modification using numbers cannot proceed
105 /// due to the value's current type,
106 /// as that would lead to mixed representations.
107 #[snafu(display("cannot not modify {:?} value as numeric values", original))]
108 IncompatibleNumberType { original: ValueType },
109}
110
111/// An error type for an attempt of accessing a value
112/// in one internal representation as another.
113///
114/// This error is raised whenever it is not possible to retrieve the requested
115/// value, either because the inner representation is not compatible with the
116/// requested value type, or a conversion would be required. In other words,
117/// if a reference to the inner value cannot be obtained with
118/// the requested target type (for example, retrieving a date from a string),
119/// an error of this type is returned.
120///
121/// If such a conversion is acceptable, please use conversion methods instead:
122/// `to_date` instead of `date`, `to_str` instead of `string`, and so on.
123/// The error type would then be [`ConvertValueError`].
124///
125/// [`ConvertValueError`]: ./struct.ConvertValueError.html
126#[derive(Debug, Clone, PartialEq)]
127pub struct CastValueError {
128 /// The value format requested
129 pub requested: &'static str,
130 /// The value's actual representation
131 pub got: ValueType,
132}
133
134impl Display for CastValueError {
135 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
136 write!(
137 f,
138 "bad value cast: requested {} but value is {:?}",
139 self.requested, self.got
140 )
141 }
142}
143
144impl std::error::Error for CastValueError {}
145
146/// An error type for a failed attempt at converting a value
147/// into another representation.
148#[derive(Debug)]
149pub struct ConvertValueError {
150 /// The value format requested
151 pub requested: &'static str,
152 /// The value's original representation
153 pub original: ValueType,
154 /// The reason why the conversion was unsuccessful,
155 /// or none if a conversion from the given original representation
156 /// is not possible
157 pub cause: Option<Box<InvalidValueReadError>>,
158}
159
160impl Display for ConvertValueError {
161 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
162 write!(
163 f,
164 "could not convert {:?} to a {}: ",
165 self.original, self.requested
166 )?;
167 if let Some(cause) = &self.cause {
168 write!(f, "{cause}")?;
169 } else {
170 write!(f, "conversion not possible")?;
171 }
172 Ok(())
173 }
174}
175
176impl std::error::Error for ConvertValueError {
177 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
178 self.cause.as_ref().map(|x| x as _)
179 }
180}
181
182pub type Result<T, E = InvalidValueReadError> = std::result::Result<T, E>;
183
184// Re-exported from chrono
185pub use chrono::{NaiveDate, NaiveTime};
186
187/// An aggregation of one or more elements in a value.
188pub type C<T> = SmallVec<[T; 2]>;
189
190/// An enum representing a primitive value from a DICOM element.
191/// The result of decoding an element's data value
192/// may be one of the enumerated types
193/// depending on its content and value representation.
194///
195/// Multiple elements are contained in a [`smallvec`] vector,
196/// conveniently aliased to the type [`C`].
197///
198/// See the macro [`dicom_value!`] for a more intuitive means
199/// of constructing these values.
200/// Alternatively, `From` conversions into `PrimitiveValue` exist
201/// for single element types,
202/// including numeric types, `String`, and `&str`.
203///
204/// # Example
205///
206/// ```
207/// # use dicom_core::PrimitiveValue;
208/// # use smallvec::smallvec;
209/// let value = PrimitiveValue::from("Smith^John");
210/// assert_eq!(value, PrimitiveValue::Str("Smith^John".to_string()));
211/// assert_eq!(value.multiplicity(), 1);
212///
213/// let value = PrimitiveValue::from(512_u16);
214/// assert_eq!(value, PrimitiveValue::U16(smallvec![512]));
215/// ```
216///
217/// [`smallvec`]: ../../smallvec/index.html
218/// [`C`]: ./type.C.html
219/// [`dicom_value!`]: ../macro.dicom_value.html
220#[derive(Debug, Clone)]
221pub enum PrimitiveValue {
222 /// No data. Usually employed for zero-length values.
223 Empty,
224
225 /// A sequence of strings.
226 /// Used for AE, AS, PN, SH, CS, LO, UI and UC.
227 /// Can also be used for IS, SS, DS, DA, DT and TM when decoding
228 /// with format preservation.
229 Strs(C<String>),
230
231 /// A single string.
232 /// Used for ST, LT, UT and UR, which are never multi-valued.
233 Str(String),
234
235 /// A sequence of attribute tags.
236 /// Used specifically for AT.
237 Tags(C<Tag>),
238
239 /// The value is a sequence of unsigned 8-bit integers.
240 /// Used for OB and UN.
241 U8(C<u8>),
242
243 /// The value is a sequence of signed 16-bit integers.
244 /// Used for SS.
245 I16(C<i16>),
246
247 /// A sequence of unsigned 16-bit integers.
248 /// Used for US and OW.
249 U16(C<u16>),
250
251 /// A sequence of signed 32-bit integers.
252 /// Used for SL and IS.
253 I32(C<i32>),
254
255 /// A sequence of unsigned 32-bit integers.
256 /// Used for UL and OL.
257 U32(C<u32>),
258
259 /// A sequence of signed 64-bit integers.
260 /// Used for SV.
261 I64(C<i64>),
262
263 /// A sequence of unsigned 64-bit integers.
264 /// Used for UV and OV.
265 U64(C<u64>),
266
267 /// The value is a sequence of 32-bit floating point numbers.
268 /// Used for OF and FL.
269 F32(C<f32>),
270
271 /// The value is a sequence of 64-bit floating point numbers.
272 /// Used for OD and FD, DS.
273 F64(C<f64>),
274
275 /// A sequence of dates with arbitrary precision.
276 /// Used for the DA representation.
277 Date(C<DicomDate>),
278
279 /// A sequence of date-time values with arbitrary precision.
280 /// Used for the DT representation.
281 DateTime(C<DicomDateTime>),
282
283 /// A sequence of time values with arbitrary precision.
284 /// Used for the TM representation.
285 Time(C<DicomTime>),
286}
287
288/// A utility macro for implementing the conversion from a core type into a
289/// DICOM primitive value with a single element.
290macro_rules! impl_from_for_primitive {
291 ($typ: ty, $variant: ident) => {
292 impl From<$typ> for PrimitiveValue {
293 fn from(value: $typ) -> Self {
294 PrimitiveValue::$variant(C::from_elem(value, 1))
295 }
296 }
297 };
298}
299
300impl_from_for_primitive!(u8, U8);
301impl_from_for_primitive!(u16, U16);
302impl_from_for_primitive!(i16, I16);
303impl_from_for_primitive!(u32, U32);
304impl_from_for_primitive!(i32, I32);
305impl_from_for_primitive!(u64, U64);
306impl_from_for_primitive!(i64, I64);
307impl_from_for_primitive!(f32, F32);
308impl_from_for_primitive!(f64, F64);
309
310impl_from_for_primitive!(Tag, Tags);
311impl_from_for_primitive!(DicomDate, Date);
312impl_from_for_primitive!(DicomTime, Time);
313impl_from_for_primitive!(DicomDateTime, DateTime);
314
315impl From<String> for PrimitiveValue {
316 fn from(value: String) -> Self {
317 PrimitiveValue::Str(value)
318 }
319}
320
321impl From<&str> for PrimitiveValue {
322 fn from(value: &str) -> Self {
323 PrimitiveValue::Str(value.to_owned())
324 }
325}
326
327impl From<Vec<u8>> for PrimitiveValue {
328 fn from(value: Vec<u8>) -> Self {
329 PrimitiveValue::U8(C::from(value))
330 }
331}
332
333impl From<&[u8]> for PrimitiveValue {
334 fn from(value: &[u8]) -> Self {
335 PrimitiveValue::U8(C::from(value))
336 }
337}
338
339impl From<PersonName<'_>> for PrimitiveValue {
340 fn from(p: PersonName) -> Self {
341 PrimitiveValue::Str(p.to_dicom_string())
342 }
343}
344
345impl From<()> for PrimitiveValue {
346 /// constructs an empty DICOM value
347 #[inline]
348 fn from(_value: ()) -> Self {
349 PrimitiveValue::Empty
350 }
351}
352
353macro_rules! impl_from_array_for_primitive {
354 ($typ: ty, $variant: ident) => {
355 impl From<$typ> for PrimitiveValue {
356 fn from(value: $typ) -> Self {
357 PrimitiveValue::$variant(C::from_slice(&value[..]))
358 }
359 }
360 };
361}
362
363macro_rules! impl_from_array_for_primitive_1_to_8 {
364 ($typ: ty, $variant: ident) => {
365 impl_from_array_for_primitive!([$typ; 1], $variant);
366 impl_from_array_for_primitive!([$typ; 2], $variant);
367 impl_from_array_for_primitive!([$typ; 3], $variant);
368 impl_from_array_for_primitive!([$typ; 4], $variant);
369 impl_from_array_for_primitive!([$typ; 5], $variant);
370 impl_from_array_for_primitive!([$typ; 6], $variant);
371 impl_from_array_for_primitive!([$typ; 7], $variant);
372 impl_from_array_for_primitive!([$typ; 8], $variant);
373 impl_from_array_for_primitive!(&[$typ; 1], $variant);
374 impl_from_array_for_primitive!(&[$typ; 2], $variant);
375 impl_from_array_for_primitive!(&[$typ; 3], $variant);
376 impl_from_array_for_primitive!(&[$typ; 4], $variant);
377 impl_from_array_for_primitive!(&[$typ; 5], $variant);
378 impl_from_array_for_primitive!(&[$typ; 6], $variant);
379 impl_from_array_for_primitive!(&[$typ; 7], $variant);
380 impl_from_array_for_primitive!(&[$typ; 8], $variant);
381 };
382}
383
384impl_from_array_for_primitive_1_to_8!(u8, U8);
385impl_from_array_for_primitive_1_to_8!(u16, U16);
386impl_from_array_for_primitive_1_to_8!(i16, I16);
387impl_from_array_for_primitive_1_to_8!(u32, U32);
388impl_from_array_for_primitive_1_to_8!(i32, I32);
389impl_from_array_for_primitive_1_to_8!(u64, U64);
390impl_from_array_for_primitive_1_to_8!(i64, I64);
391impl_from_array_for_primitive_1_to_8!(f32, F32);
392impl_from_array_for_primitive_1_to_8!(f64, F64);
393impl_from_array_for_primitive_1_to_8!(DicomDate, Date);
394impl_from_array_for_primitive_1_to_8!(DicomTime, Time);
395impl_from_array_for_primitive_1_to_8!(DicomDateTime, DateTime);
396
397impl PrimitiveValue {
398 /// Create a single unsigned 16-bit value.
399 pub fn new_u16(value: u16) -> Self {
400 PrimitiveValue::U16(C::from_elem(value, 1))
401 }
402
403 /// Create a single unsigned 32-bit value.
404 pub fn new_u32(value: u32) -> Self {
405 PrimitiveValue::U32(C::from_elem(value, 1))
406 }
407
408 /// Create a single I32 value.
409 pub fn new_i32(value: i32) -> Self {
410 PrimitiveValue::I32(C::from_elem(value, 1))
411 }
412
413 /// Obtain the number of individual elements. This number may not
414 /// match the DICOM value multiplicity in some value representations.
415 pub fn multiplicity(&self) -> u32 {
416 use self::PrimitiveValue::*;
417 match self {
418 Empty => 0,
419 Str(_) => 1,
420 Strs(c) => c.len() as u32,
421 Tags(c) => c.len() as u32,
422 U8(c) => c.len() as u32,
423 I16(c) => c.len() as u32,
424 U16(c) => c.len() as u32,
425 I32(c) => c.len() as u32,
426 U32(c) => c.len() as u32,
427 I64(c) => c.len() as u32,
428 U64(c) => c.len() as u32,
429 F32(c) => c.len() as u32,
430 F64(c) => c.len() as u32,
431 Date(c) => c.len() as u32,
432 DateTime(c) => c.len() as u32,
433 Time(c) => c.len() as u32,
434 }
435 }
436
437 /// Determine the length of the DICOM value in its encoded form.
438 ///
439 /// In other words,
440 /// this is the number of bytes that the value
441 /// would need to occupy in a DICOM file,
442 /// without compression and without the element header.
443 /// The output is always an even number,
444 /// so as to consider the mandatory trailing padding.
445 ///
446 /// This method is particularly useful for presenting an estimated
447 /// space occupation to the end user.
448 /// However, consumers should not depend on this number for
449 /// decoding or encoding values.
450 /// The calculated number does not need to match
451 /// the length of the original byte stream
452 /// from where the value was originally decoded.
453 pub fn calculate_byte_len(&self) -> usize {
454 use self::PrimitiveValue::*;
455 match self {
456 Empty => 0,
457 U8(c) => c.len(),
458 I16(c) => c.len() * 2,
459 U16(c) => c.len() * 2,
460 U32(c) => c.len() * 4,
461 I32(c) => c.len() * 4,
462 U64(c) => c.len() * 8,
463 I64(c) => c.len() * 8,
464 F32(c) => c.len() * 4,
465 F64(c) => c.len() * 8,
466 Tags(c) => c.len() * 4,
467 Str(s) => s.len(),
468 Strs(c) => c.iter().map(|s| s.len() + 1).sum::<usize>() & !1,
469 Date(c) => {
470 c.iter()
471 .map(|d| PrimitiveValue::da_byte_len(d) + 1)
472 .sum::<usize>()
473 & !1
474 }
475 Time(c) => {
476 c.iter()
477 .map(|t| PrimitiveValue::tm_byte_len(t) + 1)
478 .sum::<usize>()
479 & !1
480 }
481 DateTime(c) => {
482 c.iter()
483 .map(|dt| PrimitiveValue::dt_byte_len(dt) + 1)
484 .sum::<usize>()
485 & !1
486 }
487 }
488 }
489
490 fn da_byte_len(date: &DicomDate) -> usize {
491 match date.precision() {
492 DateComponent::Year => 4,
493 DateComponent::Month => 6,
494 DateComponent::Day => 8,
495 _ => panic!("Impossible precision for a DicomDate"),
496 }
497 }
498
499 fn tm_byte_len(time: &DicomTime) -> usize {
500 match time.precision() {
501 DateComponent::Hour => 2,
502 DateComponent::Minute => 4,
503 DateComponent::Second => 6,
504 DateComponent::Fraction => match time.fraction_and_precision() {
505 None => panic!("DicomTime has fraction precision but no fraction can be retrieved"),
506 Some((_, fp)) => 7 + fp as usize, // 1 is for the '.'
507 },
508 _ => panic!("Impossible precision for a Dicomtime"),
509 }
510 }
511
512 fn dt_byte_len(datetime: &DicomDateTime) -> usize {
513 PrimitiveValue::da_byte_len(datetime.date())
514 + match datetime.time() {
515 Some(time) => PrimitiveValue::tm_byte_len(time),
516 None => 0,
517 }
518 + match datetime.has_time_zone() {
519 true => 5,
520 false => 0,
521 }
522 }
523
524 /// Convert the primitive value into a string representation.
525 ///
526 /// String values already encoded with the `Str` and `Strs` variants
527 /// are provided as is.
528 /// In the case of `Strs`, the strings are first joined together
529 /// with a backslash (`'\\'`).
530 /// All other type variants are first converted to a string,
531 /// then joined together with a backslash.
532 ///
533 /// Trailing whitespace is stripped from each string.
534 ///
535 /// **Note:**
536 /// As the process of reading a DICOM value
537 /// may not always preserve its original nature,
538 /// it is not guaranteed that `to_str()` returns a string with
539 /// the exact same byte sequence as the one originally found
540 /// at the source of the value,
541 /// even for the string variants.
542 /// Therefore, this method is not reliable
543 /// for compliant DICOM serialization.
544 ///
545 /// # Examples
546 ///
547 /// ```
548 /// # use dicom_core::dicom_value;
549 /// # use dicom_core::value::{C, PrimitiveValue, DicomDate};
550 /// # use smallvec::smallvec;
551 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
552 /// assert_eq!(
553 /// dicom_value!(Str, "Smith^John").to_str(),
554 /// "Smith^John",
555 /// );
556 /// assert_eq!(
557 /// dicom_value!(Date, DicomDate::from_y(2014)?).to_str(),
558 /// "2014",
559 /// );
560 /// assert_eq!(
561 /// dicom_value!(Str, "Smith^John\0").to_str(),
562 /// "Smith^John",
563 /// );
564 /// assert_eq!(
565 /// dicom_value!(Strs, [
566 /// "DERIVED",
567 /// "PRIMARY",
568 /// "WHOLE BODY",
569 /// "EMISSION",
570 /// ])
571 /// .to_str(),
572 /// "DERIVED\\PRIMARY\\WHOLE BODY\\EMISSION",
573 /// );
574 /// Ok(())
575 /// }
576 /// ```
577 pub fn to_str(&self) -> Cow<'_, str> {
578 match self {
579 PrimitiveValue::Empty => Cow::from(""),
580 PrimitiveValue::Str(values) => Cow::from(values.trim_end_matches([' ', '\u{0}'])),
581 PrimitiveValue::Strs(values) => {
582 if values.len() == 1 {
583 Cow::from(values[0].trim_end_matches([' ', '\u{0}']))
584 } else {
585 Cow::Owned(
586 values
587 .iter()
588 .map(|s| s.trim_end_matches([' ', '\u{0}']))
589 .join("\\"),
590 )
591 }
592 }
593 prim => Cow::from(prim.to_string()),
594 }
595 }
596
597 /// Convert the primitive value into a raw string representation.
598 ///
599 /// String values already encoded with the `Str` and `Strs` variants
600 /// are provided as is.
601 /// In the case of `Strs`, the strings are first joined together
602 /// with a backslash (`'\\'`).
603 /// All other type variants are first converted to a string,
604 /// then joined together with a backslash.
605 ///
606 /// This method keeps all trailing whitespace,
607 /// unlike [`to_str()`](PrimitiveValue::to_str).
608 ///
609 /// **Note:**
610 /// As the process of reading a DICOM value
611 /// may not always preserve its original nature,
612 /// it is not guaranteed that `to_raw_str()` returns a string with
613 /// the exact same byte sequence as the one originally found
614 /// at the source of the value,
615 /// even for the string variants.
616 /// Therefore, this method is not reliable
617 /// for compliant DICOM serialization.
618 ///
619 /// # Examples
620 ///
621 /// ```
622 /// # use dicom_core::dicom_value;
623 /// # use dicom_core::value::{C, DicomDate, PrimitiveValue};
624 /// # use smallvec::smallvec;
625 /// assert_eq!(
626 /// dicom_value!(Str, "Smith^John\0").to_raw_str(),
627 /// "Smith^John\0",
628 /// );
629 /// assert_eq!(
630 /// dicom_value!(Date, DicomDate::from_ymd(2014, 10, 12).unwrap()).to_raw_str(),
631 /// "2014-10-12",
632 /// );
633 /// assert_eq!(
634 /// dicom_value!(Strs, [
635 /// "DERIVED",
636 /// " PRIMARY ",
637 /// "WHOLE BODY",
638 /// "EMISSION ",
639 /// ])
640 /// .to_raw_str(),
641 /// "DERIVED\\ PRIMARY \\WHOLE BODY\\EMISSION ",
642 /// );
643 /// ```
644 pub fn to_raw_str(&self) -> Cow<'_, str> {
645 match self {
646 PrimitiveValue::Empty => Cow::from(""),
647 PrimitiveValue::Str(values) => Cow::from(values.as_str()),
648 PrimitiveValue::Strs(values) => {
649 if values.len() == 1 {
650 Cow::from(&values[0])
651 } else {
652 Cow::from(values.iter().join("\\"))
653 }
654 }
655 prim => Cow::from(prim.to_string()),
656 }
657 }
658
659 /// Convert the primitive value into a multi-string representation.
660 ///
661 /// String values already encoded with the `Str` and `Strs` variants
662 /// are provided as is.
663 /// All other type variants are first converted to a string,
664 /// then collected into a vector.
665 ///
666 /// Trailing whitespace is stripped from each string.
667 /// If keeping it is desired,
668 /// use [`to_raw_str()`](PrimitiveValue::to_raw_str).
669 ///
670 /// **Note:**
671 /// As the process of reading a DICOM value
672 /// may not always preserve its original nature,
673 /// it is not guaranteed that `to_multi_str()` returns strings with
674 /// the exact same byte sequence as the one originally found
675 /// at the source of the value,
676 /// even for the string variants.
677 /// Therefore, this method is not reliable
678 /// for compliant DICOM serialization.
679 ///
680 /// # Examples
681 ///
682 /// ```
683 /// # use dicom_core::dicom_value;
684 /// # use dicom_core::value::{C, PrimitiveValue, DicomDate};
685 /// # use smallvec::smallvec;
686 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
687 /// assert_eq!(
688 /// dicom_value!(Strs, [
689 /// "DERIVED",
690 /// "PRIMARY",
691 /// "WHOLE BODY ",
692 /// " EMISSION ",
693 /// ])
694 /// .to_multi_str(),
695 /// &["DERIVED", "PRIMARY", "WHOLE BODY", " EMISSION"][..],
696 /// );
697 /// assert_eq!(
698 /// dicom_value!(Str, "Smith^John").to_multi_str(),
699 /// &["Smith^John"][..],
700 /// );
701 /// assert_eq!(
702 /// dicom_value!(Str, "Smith^John\0").to_multi_str(),
703 /// &["Smith^John"][..],
704 /// );
705 /// assert_eq!(
706 /// dicom_value!(Date, DicomDate::from_ym(2014, 10)?).to_multi_str(),
707 /// &["201410"][..],
708 /// );
709 /// assert_eq!(
710 /// dicom_value!(I64, [128, 256, 512]).to_multi_str(),
711 /// &["128", "256", "512"][..],
712 /// );
713 /// Ok(())
714 /// }
715 /// ```
716 pub fn to_multi_str(&self) -> Cow<'_, [String]> {
717 /// Auxiliary function for turning a sequence of values
718 /// into a sequence of strings.
719 fn seq_to_str<I>(iter: I) -> Vec<String>
720 where
721 I: IntoIterator,
722 I::Item: Display,
723 {
724 iter.into_iter().map(|x| x.to_string()).collect()
725 }
726
727 match self {
728 PrimitiveValue::Empty => Cow::from(&[][..]),
729 PrimitiveValue::Str(_) => Cow::Owned(vec![self.to_str().to_string()]),
730 PrimitiveValue::Strs(_) => {
731 Cow::Owned(self.to_str().split('\\').map(|s| s.to_string()).collect())
732 }
733 PrimitiveValue::Date(values) => values
734 .into_iter()
735 .map(|date| date.to_encoded())
736 .collect::<Vec<_>>()
737 .into(),
738 PrimitiveValue::Time(values) => values
739 .into_iter()
740 .map(|time| time.to_encoded())
741 .collect::<Vec<_>>()
742 .into(),
743 PrimitiveValue::DateTime(values) => values
744 .into_iter()
745 .map(|dt| dt.to_encoded())
746 .collect::<Vec<_>>()
747 .into(),
748 PrimitiveValue::U8(values) => Cow::Owned(seq_to_str(values)),
749 PrimitiveValue::U16(values) => Cow::Owned(seq_to_str(values)),
750 PrimitiveValue::U32(values) => Cow::Owned(seq_to_str(values)),
751 PrimitiveValue::I16(values) => Cow::Owned(seq_to_str(values)),
752 PrimitiveValue::I32(values) => Cow::Owned(seq_to_str(values)),
753 PrimitiveValue::U64(values) => Cow::Owned(seq_to_str(values)),
754 PrimitiveValue::I64(values) => Cow::Owned(seq_to_str(values)),
755 PrimitiveValue::F32(values) => Cow::Owned(seq_to_str(values)),
756 PrimitiveValue::F64(values) => Cow::Owned(seq_to_str(values)),
757 PrimitiveValue::Tags(values) => Cow::Owned(seq_to_str(values)),
758 }
759 }
760
761 /// Retrieve this DICOM value as raw bytes.
762 ///
763 /// Binary numeric values are returned with a reinterpretation
764 /// of the holding vector's occupied data block as bytes,
765 /// without copying,
766 /// under the platform's native byte order.
767 ///
768 /// String values already encoded with the `Str` and `Strs` variants
769 /// are provided as their respective bytes in UTF-8.
770 /// In the case of `Strs`, the strings are first joined together
771 /// with a backslash (`'\\'`).
772 /// Other type variants are first converted to a string,
773 /// joined together with a backslash,
774 /// then turned into a byte vector.
775 /// For values which are inherently textual according the standard,
776 /// this is equivalent to calling `as_bytes()` after [`to_str()`].
777 ///
778 /// **Note:**
779 /// As the process of reading a DICOM value
780 /// may not always preserve its original nature,
781 /// it is not guaranteed that `to_bytes()` returns the same byte sequence
782 /// as the one originally found at the source of the value.
783 /// Therefore, this method is not reliable
784 /// for compliant DICOM serialization.
785 ///
786 /// [`to_str()`]: #method.to_str
787 ///
788 /// # Examples
789 ///
790 /// `U8` provides a straight, zero-copy slice of bytes.
791 ///
792 /// ```
793 /// # use dicom_core::value::{C, PrimitiveValue, DicomDate};
794 /// # use smallvec::smallvec;
795 ///
796 /// assert_eq!(
797 /// PrimitiveValue::U8(smallvec![
798 /// 1, 2, 5,
799 /// ]).to_bytes(),
800 /// &[1, 2, 5][..],
801 /// );
802 /// ```
803 ///
804 /// Other values are converted to text first.
805 ///
806 /// ```
807 /// # use dicom_core::dicom_value;
808 /// # use dicom_core::value::{C, PrimitiveValue, DicomDate};
809 /// # use smallvec::smallvec;
810 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
811 /// assert_eq!(
812 /// PrimitiveValue::from("Smith^John").to_bytes(),
813 /// &b"Smith^John"[..],
814 /// );
815 /// assert_eq!(
816 /// PrimitiveValue::from(DicomDate::from_ymd(2014, 10, 12)?)
817 /// .to_bytes(),
818 /// &b"2014-10-12"[..],
819 /// );
820 /// assert_eq!(
821 /// dicom_value!(Strs, [
822 /// "DERIVED",
823 /// "PRIMARY",
824 /// "WHOLE BODY",
825 /// "EMISSION",
826 /// ])
827 /// .to_bytes(),
828 /// &b"DERIVED\\PRIMARY\\WHOLE BODY\\EMISSION"[..],
829 /// );
830 /// Ok(())
831 /// }
832 /// ```
833 pub fn to_bytes(&self) -> Cow<'_, [u8]> {
834 match self {
835 PrimitiveValue::Empty => Cow::from(&[][..]),
836 PrimitiveValue::U8(values) => Cow::from(&values[..]),
837 PrimitiveValue::U16(values) => Cow::Borrowed(transmute_to_bytes(values)),
838 PrimitiveValue::I16(values) => Cow::Borrowed(transmute_to_bytes(values)),
839 PrimitiveValue::U32(values) => Cow::Borrowed(transmute_to_bytes(values)),
840 PrimitiveValue::I32(values) => Cow::Borrowed(transmute_to_bytes(values)),
841 PrimitiveValue::I64(values) => Cow::Borrowed(transmute_to_bytes(values)),
842 PrimitiveValue::U64(values) => Cow::Borrowed(transmute_to_bytes(values)),
843 PrimitiveValue::F32(values) => Cow::Borrowed(transmute_to_bytes(values)),
844 PrimitiveValue::F64(values) => Cow::Borrowed(transmute_to_bytes(values)),
845 PrimitiveValue::Str(values) => Cow::from(values.as_bytes()),
846 PrimitiveValue::Strs(values) => {
847 if values.len() == 1 {
848 // no need to copy if it's a single string
849 Cow::from(values[0].as_bytes())
850 } else {
851 Cow::from(values.iter().join("\\").into_bytes())
852 }
853 }
854 prim => match prim.to_str() {
855 Cow::Borrowed(string) => Cow::Borrowed(string.as_bytes()),
856 Cow::Owned(string) => Cow::Owned(string.into_bytes()),
857 },
858 }
859 }
860
861 /// Retrieve a single integer of type `T` from this value.
862 ///
863 /// If the value is already represented as an integer,
864 /// it is returned after a conversion to the target type.
865 /// An error is returned if the integer cannot be represented
866 /// by the given integer type.
867 /// If the value is a string or sequence of strings,
868 /// the first string is parsed to obtain an integer,
869 /// potentially failing if the string does not represent a valid integer.
870 /// The string is stripped of leading/trailing whitespace before parsing.
871 /// If the value is a sequence of U8 bytes,
872 /// the bytes are individually interpreted as independent numbers.
873 /// Otherwise, the operation fails.
874 ///
875 /// Note that this method does not enable
876 /// the conversion of floating point numbers to integers via truncation.
877 /// If this is intentional,
878 /// retrieve a float via [`to_float32`] or [`to_float64`] instead,
879 /// then cast it to an integer.
880 ///
881 /// [`to_float32`]: #method.to_float32
882 /// [`to_float64`]: #method.to_float64
883 ///
884 /// # Example
885 ///
886 /// ```
887 /// # use dicom_core::value::{C, PrimitiveValue};
888 /// # use smallvec::smallvec;
889 ///
890 /// assert_eq!(
891 /// PrimitiveValue::I32(smallvec![
892 /// 1, 2, 5,
893 /// ])
894 /// .to_int::<u32>().ok(),
895 /// Some(1_u32),
896 /// );
897 ///
898 /// assert_eq!(
899 /// PrimitiveValue::from("505 ").to_int::<i32>().ok(),
900 /// Some(505),
901 /// );
902 /// ```
903 pub fn to_int<T>(&self) -> Result<T, ConvertValueError>
904 where
905 T: NumCast,
906 T: FromStr<Err = std::num::ParseIntError>,
907 {
908 match self {
909 PrimitiveValue::Str(s) => s
910 .trim_matches(whitespace_or_null)
911 .parse()
912 .context(ParseIntegerSnafu)
913 .map_err(|err| ConvertValueError {
914 requested: "integer",
915 original: self.value_type(),
916 cause: Some(Box::from(err)),
917 }),
918 PrimitiveValue::Strs(s) if !s.is_empty() => s[0]
919 .trim_matches(whitespace_or_null)
920 .parse()
921 .context(ParseIntegerSnafu)
922 .map_err(|err| ConvertValueError {
923 requested: "integer",
924 original: self.value_type(),
925 cause: Some(Box::from(err)),
926 }),
927 PrimitiveValue::U8(bytes) if !bytes.is_empty() => {
928 T::from(bytes[0]).ok_or_else(|| ConvertValueError {
929 requested: "integer",
930 original: self.value_type(),
931 cause: Some(
932 NarrowConvertSnafu {
933 value: bytes[0].to_string(),
934 }
935 .build()
936 .into(),
937 ),
938 })
939 }
940 PrimitiveValue::U16(s) if !s.is_empty() => {
941 T::from(s[0]).ok_or_else(|| ConvertValueError {
942 requested: "integer",
943 original: self.value_type(),
944 cause: Some(
945 NarrowConvertSnafu {
946 value: s[0].to_string(),
947 }
948 .build()
949 .into(),
950 ),
951 })
952 }
953 PrimitiveValue::I16(s) if !s.is_empty() => {
954 T::from(s[0]).ok_or_else(|| ConvertValueError {
955 requested: "integer",
956 original: self.value_type(),
957 cause: Some(
958 NarrowConvertSnafu {
959 value: s[0].to_string(),
960 }
961 .build()
962 .into(),
963 ),
964 })
965 }
966 PrimitiveValue::U32(s) if !s.is_empty() => {
967 T::from(s[0]).ok_or_else(|| ConvertValueError {
968 requested: "integer",
969 original: self.value_type(),
970 cause: Some(
971 NarrowConvertSnafu {
972 value: s[0].to_string(),
973 }
974 .build()
975 .into(),
976 ),
977 })
978 }
979 PrimitiveValue::I32(s) if !s.is_empty() => {
980 T::from(s[0]).ok_or_else(|| ConvertValueError {
981 requested: "integer",
982 original: self.value_type(),
983 cause: Some(
984 NarrowConvertSnafu {
985 value: s[0].to_string(),
986 }
987 .build()
988 .into(),
989 ),
990 })
991 }
992 PrimitiveValue::U64(s) if !s.is_empty() => {
993 T::from(s[0]).ok_or_else(|| ConvertValueError {
994 requested: "integer",
995 original: self.value_type(),
996 cause: Some(
997 NarrowConvertSnafu {
998 value: s[0].to_string(),
999 }
1000 .build()
1001 .into(),
1002 ),
1003 })
1004 }
1005 PrimitiveValue::I64(s) if !s.is_empty() => {
1006 T::from(s[0]).ok_or_else(|| ConvertValueError {
1007 requested: "integer",
1008 original: self.value_type(),
1009 cause: Some(
1010 NarrowConvertSnafu {
1011 value: s[0].to_string(),
1012 }
1013 .build()
1014 .into(),
1015 ),
1016 })
1017 }
1018 _ => Err(ConvertValueError {
1019 requested: "integer",
1020 original: self.value_type(),
1021 cause: None,
1022 }),
1023 }
1024 }
1025
1026 /// Retrieve a sequence of integers of type `T` from this value.
1027 ///
1028 /// If the values is already represented as an integer,
1029 /// it is returned after a [`NumCast`] conversion to the target type.
1030 /// An error is returned if any of the integers cannot be represented
1031 /// by the given integer type.
1032 /// If the value is a string or sequence of strings,
1033 /// each string is parsed to obtain an integer,
1034 /// potentially failing if the string does not represent a valid integer.
1035 /// The string is stripped of leading/trailing whitespace before parsing.
1036 /// If the value is a sequence of U8 bytes,
1037 /// the bytes are individually interpreted as independent numbers.
1038 /// Otherwise, the operation fails.
1039 ///
1040 /// Note that this method does not enable
1041 /// the conversion of floating point numbers to integers via truncation.
1042 /// If this is intentional,
1043 /// retrieve a float via [`to_float32`] or [`to_float64`] instead,
1044 /// then cast it to an integer.
1045 ///
1046 /// [`NumCast`]: ../num_traits/cast/trait.NumCast.html
1047 /// [`to_float32`]: #method.to_float32
1048 /// [`to_float64`]: #method.to_float64
1049 ///
1050 /// # Example
1051 ///
1052 /// ```
1053 /// # use dicom_core::value::{C, PrimitiveValue};
1054 /// # use dicom_core::dicom_value;
1055 /// # use smallvec::smallvec;
1056 ///
1057 /// assert_eq!(
1058 /// PrimitiveValue::I32(smallvec![
1059 /// 1, 2, 5,
1060 /// ])
1061 /// .to_multi_int::<u32>().ok(),
1062 /// Some(vec![1_u32, 2, 5]),
1063 /// );
1064 ///
1065 /// assert_eq!(
1066 /// dicom_value!(Strs, ["5050", "23 "]).to_multi_int::<i32>().ok(),
1067 /// Some(vec![5050, 23]),
1068 /// );
1069 /// ```
1070 pub fn to_multi_int<T>(&self) -> Result<Vec<T>, ConvertValueError>
1071 where
1072 T: NumCast,
1073 T: FromStr<Err = std::num::ParseIntError>,
1074 {
1075 match self {
1076 PrimitiveValue::Empty => Ok(Vec::new()),
1077 PrimitiveValue::Str(s) => {
1078 let out = s
1079 .trim_matches(whitespace_or_null)
1080 .parse()
1081 .context(ParseIntegerSnafu)
1082 .map_err(|err| ConvertValueError {
1083 requested: "integer",
1084 original: self.value_type(),
1085 cause: Some(Box::from(err)),
1086 })?;
1087 Ok(vec![out])
1088 }
1089 PrimitiveValue::Strs(s) => s
1090 .iter()
1091 .map(|v| {
1092 v.trim_matches(whitespace_or_null)
1093 .parse()
1094 .context(ParseIntegerSnafu)
1095 .map_err(|err| ConvertValueError {
1096 requested: "integer",
1097 original: self.value_type(),
1098 cause: Some(Box::from(err)),
1099 })
1100 })
1101 .collect::<Result<Vec<_>, _>>(),
1102 PrimitiveValue::U8(bytes) => bytes
1103 .iter()
1104 .map(|v| {
1105 T::from(*v).ok_or_else(|| ConvertValueError {
1106 requested: "integer",
1107 original: self.value_type(),
1108 cause: Some(
1109 NarrowConvertSnafu {
1110 value: v.to_string(),
1111 }
1112 .build()
1113 .into(),
1114 ),
1115 })
1116 })
1117 .collect::<Result<Vec<_>, _>>(),
1118 PrimitiveValue::U16(s) => s
1119 .iter()
1120 .map(|v| {
1121 T::from(*v).ok_or_else(|| ConvertValueError {
1122 requested: "integer",
1123 original: self.value_type(),
1124 cause: Some(
1125 NarrowConvertSnafu {
1126 value: v.to_string(),
1127 }
1128 .build()
1129 .into(),
1130 ),
1131 })
1132 })
1133 .collect::<Result<Vec<_>, _>>(),
1134 PrimitiveValue::I16(s) => s
1135 .iter()
1136 .map(|v| {
1137 T::from(*v).ok_or_else(|| ConvertValueError {
1138 requested: "integer",
1139 original: self.value_type(),
1140 cause: Some(
1141 NarrowConvertSnafu {
1142 value: v.to_string(),
1143 }
1144 .build()
1145 .into(),
1146 ),
1147 })
1148 })
1149 .collect::<Result<Vec<_>, _>>(),
1150 PrimitiveValue::U32(s) => s
1151 .iter()
1152 .map(|v| {
1153 T::from(*v).ok_or_else(|| ConvertValueError {
1154 requested: "integer",
1155 original: self.value_type(),
1156 cause: Some(
1157 NarrowConvertSnafu {
1158 value: v.to_string(),
1159 }
1160 .build()
1161 .into(),
1162 ),
1163 })
1164 })
1165 .collect::<Result<Vec<_>, _>>(),
1166 PrimitiveValue::I32(s) if !s.is_empty() => s
1167 .iter()
1168 .map(|v| {
1169 T::from(*v).ok_or_else(|| ConvertValueError {
1170 requested: "integer",
1171 original: self.value_type(),
1172 cause: Some(
1173 NarrowConvertSnafu {
1174 value: v.to_string(),
1175 }
1176 .build()
1177 .into(),
1178 ),
1179 })
1180 })
1181 .collect::<Result<Vec<_>, _>>(),
1182 PrimitiveValue::U64(s) if !s.is_empty() => s
1183 .iter()
1184 .map(|v| {
1185 T::from(*v).ok_or_else(|| ConvertValueError {
1186 requested: "integer",
1187 original: self.value_type(),
1188 cause: Some(
1189 NarrowConvertSnafu {
1190 value: v.to_string(),
1191 }
1192 .build()
1193 .into(),
1194 ),
1195 })
1196 })
1197 .collect::<Result<Vec<_>, _>>(),
1198 PrimitiveValue::I64(s) if !s.is_empty() => s
1199 .iter()
1200 .map(|v| {
1201 T::from(*v).ok_or_else(|| ConvertValueError {
1202 requested: "integer",
1203 original: self.value_type(),
1204 cause: Some(
1205 NarrowConvertSnafu {
1206 value: v.to_string(),
1207 }
1208 .build()
1209 .into(),
1210 ),
1211 })
1212 })
1213 .collect::<Result<Vec<_>, _>>(),
1214 _ => Err(ConvertValueError {
1215 requested: "integer",
1216 original: self.value_type(),
1217 cause: None,
1218 }),
1219 }
1220 }
1221
1222 /// Retrieve one single-precision floating point from this value.
1223 ///
1224 /// If the value is already represented as a number,
1225 /// it is returned after a conversion to `f32`.
1226 /// An error is returned if the number cannot be represented
1227 /// by the given number type.
1228 /// If the value is a string or sequence of strings,
1229 /// the first string is parsed to obtain a number,
1230 /// potentially failing if the string does not represent a valid number.
1231 /// The string is stripped of leading/trailing whitespace before parsing.
1232 /// If the value is a sequence of U8 bytes,
1233 /// the bytes are individually interpreted as independent numbers.
1234 /// Otherwise, the operation fails.
1235 ///
1236 /// # Example
1237 ///
1238 /// ```
1239 /// # use dicom_core::value::{C, PrimitiveValue};
1240 /// # use smallvec::smallvec;
1241 ///
1242 /// assert_eq!(
1243 /// PrimitiveValue::F32(smallvec![
1244 /// 1.5, 2., 5.,
1245 /// ])
1246 /// .to_float32().ok(),
1247 /// Some(1.5_f32),
1248 /// );
1249 ///
1250 /// assert_eq!(
1251 /// PrimitiveValue::from("-6.75 ").to_float32().ok(),
1252 /// Some(-6.75),
1253 /// );
1254 /// ```
1255 pub fn to_float32(&self) -> Result<f32, ConvertValueError> {
1256 match self {
1257 PrimitiveValue::Str(s) => s
1258 .trim_matches(whitespace_or_null)
1259 .parse()
1260 .context(ParseFloatSnafu)
1261 .map_err(|err| ConvertValueError {
1262 requested: "float32",
1263 original: self.value_type(),
1264 cause: Some(Box::from(err)),
1265 }),
1266 PrimitiveValue::Strs(s) if !s.is_empty() => s[0]
1267 .trim_matches(whitespace_or_null)
1268 .parse()
1269 .context(ParseFloatSnafu)
1270 .map_err(|err| ConvertValueError {
1271 requested: "float32",
1272 original: self.value_type(),
1273 cause: Some(Box::from(err)),
1274 }),
1275 PrimitiveValue::U8(bytes) if !bytes.is_empty() => {
1276 NumCast::from(bytes[0]).ok_or_else(|| ConvertValueError {
1277 requested: "float32",
1278 original: self.value_type(),
1279 cause: Some(
1280 NarrowConvertSnafu {
1281 value: bytes[0].to_string(),
1282 }
1283 .build()
1284 .into(),
1285 ),
1286 })
1287 }
1288 PrimitiveValue::U16(s) if !s.is_empty() => {
1289 NumCast::from(s[0]).ok_or_else(|| ConvertValueError {
1290 requested: "float32",
1291 original: self.value_type(),
1292 cause: Some(
1293 NarrowConvertSnafu {
1294 value: s[0].to_string(),
1295 }
1296 .build()
1297 .into(),
1298 ),
1299 })
1300 }
1301 PrimitiveValue::I16(s) if !s.is_empty() => {
1302 NumCast::from(s[0]).ok_or_else(|| ConvertValueError {
1303 requested: "float32",
1304 original: self.value_type(),
1305 cause: Some(
1306 NarrowConvertSnafu {
1307 value: s[0].to_string(),
1308 }
1309 .build()
1310 .into(),
1311 ),
1312 })
1313 }
1314 PrimitiveValue::U32(s) if !s.is_empty() => {
1315 NumCast::from(s[0]).ok_or_else(|| ConvertValueError {
1316 requested: "float32",
1317 original: self.value_type(),
1318 cause: Some(
1319 NarrowConvertSnafu {
1320 value: s[0].to_string(),
1321 }
1322 .build()
1323 .into(),
1324 ),
1325 })
1326 }
1327 PrimitiveValue::I32(s) if !s.is_empty() => {
1328 NumCast::from(s[0]).ok_or_else(|| ConvertValueError {
1329 requested: "float32",
1330 original: self.value_type(),
1331 cause: Some(
1332 NarrowConvertSnafu {
1333 value: s[0].to_string(),
1334 }
1335 .build()
1336 .into(),
1337 ),
1338 })
1339 }
1340 PrimitiveValue::U64(s) if !s.is_empty() => {
1341 NumCast::from(s[0]).ok_or_else(|| ConvertValueError {
1342 requested: "float32",
1343 original: self.value_type(),
1344 cause: Some(
1345 NarrowConvertSnafu {
1346 value: s[0].to_string(),
1347 }
1348 .build()
1349 .into(),
1350 ),
1351 })
1352 }
1353 PrimitiveValue::I64(s) if !s.is_empty() => {
1354 NumCast::from(s[0]).ok_or_else(|| ConvertValueError {
1355 requested: "float32",
1356 original: self.value_type(),
1357 cause: Some(
1358 NarrowConvertSnafu {
1359 value: s[0].to_string(),
1360 }
1361 .build()
1362 .into(),
1363 ),
1364 })
1365 }
1366 PrimitiveValue::F32(s) if !s.is_empty() => Ok(s[0]),
1367 PrimitiveValue::F64(s) if !s.is_empty() => {
1368 NumCast::from(s[0]).ok_or_else(|| ConvertValueError {
1369 requested: "float32",
1370 original: self.value_type(),
1371 cause: Some(
1372 NarrowConvertSnafu {
1373 value: s[0].to_string(),
1374 }
1375 .build()
1376 .into(),
1377 ),
1378 })
1379 }
1380 _ => Err(ConvertValueError {
1381 requested: "float32",
1382 original: self.value_type(),
1383 cause: None,
1384 }),
1385 }
1386 }
1387
1388 /// Retrieve a sequence of single-precision floating point numbers
1389 /// from this value.
1390 ///
1391 /// If the value is already represented as numbers,
1392 /// they are returned after a conversion to `f32`.
1393 /// An error is returned if any of the numbers cannot be represented
1394 /// by an `f32`.
1395 /// If the value is a string or sequence of strings,
1396 /// the strings are parsed to obtain a number,
1397 /// potentially failing if the string does not represent a valid number.
1398 /// The string is stripped of leading/trailing whitespace before parsing.
1399 /// If the value is a sequence of U8 bytes,
1400 /// the bytes are individually interpreted as independent numbers.
1401 /// Otherwise, the operation fails.
1402 ///
1403 /// # Example
1404 ///
1405 /// ```
1406 /// # use dicom_core::value::{C, PrimitiveValue};
1407 /// # use smallvec::smallvec;
1408 ///
1409 /// assert_eq!(
1410 /// PrimitiveValue::F32(smallvec![
1411 /// 1.5, 2., 5.,
1412 /// ])
1413 /// .to_multi_float32().ok(),
1414 /// Some(vec![1.5_f32, 2., 5.]),
1415 /// );
1416 ///
1417 /// assert_eq!(
1418 /// PrimitiveValue::from("-6.75 ").to_multi_float32().ok(),
1419 /// Some(vec![-6.75]),
1420 /// );
1421 /// ```
1422 pub fn to_multi_float32(&self) -> Result<Vec<f32>, ConvertValueError> {
1423 match self {
1424 PrimitiveValue::Empty => Ok(Vec::new()),
1425 PrimitiveValue::Str(s) => {
1426 let out = s
1427 .trim_matches(whitespace_or_null)
1428 .parse()
1429 .context(ParseFloatSnafu)
1430 .map_err(|err| ConvertValueError {
1431 requested: "float32",
1432 original: self.value_type(),
1433 cause: Some(Box::from(err)),
1434 })?;
1435 Ok(vec![out])
1436 }
1437 PrimitiveValue::Strs(s) => s
1438 .iter()
1439 .map(|v| {
1440 v.trim_matches(whitespace_or_null)
1441 .parse()
1442 .context(ParseFloatSnafu)
1443 .map_err(|err| ConvertValueError {
1444 requested: "float32",
1445 original: self.value_type(),
1446 cause: Some(Box::from(err)),
1447 })
1448 })
1449 .collect::<Result<Vec<_>, _>>(),
1450 PrimitiveValue::U8(bytes) => bytes
1451 .iter()
1452 .map(|v| {
1453 NumCast::from(*v).ok_or_else(|| ConvertValueError {
1454 requested: "float32",
1455 original: self.value_type(),
1456 cause: Some(
1457 NarrowConvertSnafu {
1458 value: v.to_string(),
1459 }
1460 .build()
1461 .into(),
1462 ),
1463 })
1464 })
1465 .collect::<Result<Vec<_>, _>>(),
1466 PrimitiveValue::U16(s) => s
1467 .iter()
1468 .map(|v| {
1469 NumCast::from(*v).ok_or_else(|| ConvertValueError {
1470 requested: "float32",
1471 original: self.value_type(),
1472 cause: Some(
1473 NarrowConvertSnafu {
1474 value: v.to_string(),
1475 }
1476 .build()
1477 .into(),
1478 ),
1479 })
1480 })
1481 .collect::<Result<Vec<_>, _>>(),
1482 PrimitiveValue::I16(s) => s
1483 .iter()
1484 .map(|v| {
1485 NumCast::from(*v).ok_or_else(|| ConvertValueError {
1486 requested: "float32",
1487 original: self.value_type(),
1488 cause: Some(
1489 NarrowConvertSnafu {
1490 value: v.to_string(),
1491 }
1492 .build()
1493 .into(),
1494 ),
1495 })
1496 })
1497 .collect::<Result<Vec<_>, _>>(),
1498 PrimitiveValue::U32(s) => s
1499 .iter()
1500 .map(|v| {
1501 NumCast::from(*v).ok_or_else(|| ConvertValueError {
1502 requested: "float32",
1503 original: self.value_type(),
1504 cause: Some(
1505 NarrowConvertSnafu {
1506 value: v.to_string(),
1507 }
1508 .build()
1509 .into(),
1510 ),
1511 })
1512 })
1513 .collect::<Result<Vec<_>, _>>(),
1514 PrimitiveValue::I32(s) => s
1515 .iter()
1516 .map(|v| {
1517 NumCast::from(*v).ok_or_else(|| ConvertValueError {
1518 requested: "float32",
1519 original: self.value_type(),
1520 cause: Some(
1521 NarrowConvertSnafu {
1522 value: v.to_string(),
1523 }
1524 .build()
1525 .into(),
1526 ),
1527 })
1528 })
1529 .collect::<Result<Vec<_>, _>>(),
1530 PrimitiveValue::U64(s) => s
1531 .iter()
1532 .map(|v| {
1533 NumCast::from(*v).ok_or_else(|| ConvertValueError {
1534 requested: "float32",
1535 original: self.value_type(),
1536 cause: Some(
1537 NarrowConvertSnafu {
1538 value: v.to_string(),
1539 }
1540 .build()
1541 .into(),
1542 ),
1543 })
1544 })
1545 .collect::<Result<Vec<_>, _>>(),
1546 PrimitiveValue::I64(s) => s
1547 .iter()
1548 .map(|v| {
1549 NumCast::from(*v).ok_or_else(|| ConvertValueError {
1550 requested: "float32",
1551 original: self.value_type(),
1552 cause: Some(
1553 NarrowConvertSnafu {
1554 value: v.to_string(),
1555 }
1556 .build()
1557 .into(),
1558 ),
1559 })
1560 })
1561 .collect::<Result<Vec<_>, _>>(),
1562 PrimitiveValue::F32(s) => Ok(s[..].to_owned()),
1563 PrimitiveValue::F64(s) => s
1564 .iter()
1565 .map(|v| {
1566 NumCast::from(*v).ok_or_else(|| ConvertValueError {
1567 requested: "float32",
1568 original: self.value_type(),
1569 cause: Some(
1570 NarrowConvertSnafu {
1571 value: v.to_string(),
1572 }
1573 .build()
1574 .into(),
1575 ),
1576 })
1577 })
1578 .collect::<Result<Vec<_>, _>>(),
1579 _ => Err(ConvertValueError {
1580 requested: "float32",
1581 original: self.value_type(),
1582 cause: None,
1583 }),
1584 }
1585 }
1586
1587 /// Retrieve one double-precision floating point from this value.
1588 ///
1589 /// If the value is already represented as a number,
1590 /// it is returned after a conversion to `f64`.
1591 /// An error is returned if the number cannot be represented
1592 /// by the given number type.
1593 /// If the value is a string or sequence of strings,
1594 /// the first string is parsed to obtain a number,
1595 /// potentially failing if the string does not represent a valid number.
1596 /// The string is stripped of leading/trailing whitespace before parsing.
1597 /// If the value is a sequence of U8 bytes,
1598 /// the bytes are individually interpreted as independent numbers.
1599 /// Otherwise, the operation fails.
1600 ///
1601 /// # Example
1602 ///
1603 /// ```
1604 /// # use dicom_core::value::{C, PrimitiveValue};
1605 /// # use smallvec::smallvec;
1606 ///
1607 /// assert_eq!(
1608 /// PrimitiveValue::F64(smallvec![
1609 /// 1.5, 2., 5.,
1610 /// ])
1611 /// .to_float64().ok(),
1612 /// Some(1.5_f64),
1613 /// );
1614 ///
1615 /// assert_eq!(
1616 /// PrimitiveValue::from("-6.75 ").to_float64().ok(),
1617 /// Some(-6.75),
1618 /// );
1619 /// ```
1620 pub fn to_float64(&self) -> Result<f64, ConvertValueError> {
1621 match self {
1622 PrimitiveValue::Str(s) => s
1623 .trim_matches(whitespace_or_null)
1624 .parse()
1625 .context(ParseFloatSnafu)
1626 .map_err(|err| ConvertValueError {
1627 requested: "float64",
1628 original: self.value_type(),
1629 cause: Some(Box::from(err)),
1630 }),
1631 PrimitiveValue::Strs(s) if !s.is_empty() => s[0]
1632 .trim_matches(whitespace_or_null)
1633 .parse()
1634 .context(ParseFloatSnafu)
1635 .map_err(|err| ConvertValueError {
1636 requested: "float64",
1637 original: self.value_type(),
1638 cause: Some(Box::from(err)),
1639 }),
1640 PrimitiveValue::U8(bytes) if !bytes.is_empty() => {
1641 NumCast::from(bytes[0]).ok_or_else(|| ConvertValueError {
1642 requested: "float64",
1643 original: self.value_type(),
1644 cause: Some(
1645 NarrowConvertSnafu {
1646 value: bytes[0].to_string(),
1647 }
1648 .build()
1649 .into(),
1650 ),
1651 })
1652 }
1653 PrimitiveValue::U16(s) if !s.is_empty() => {
1654 NumCast::from(s[0]).ok_or_else(|| ConvertValueError {
1655 requested: "float64",
1656 original: self.value_type(),
1657 cause: Some(
1658 NarrowConvertSnafu {
1659 value: s[0].to_string(),
1660 }
1661 .build()
1662 .into(),
1663 ),
1664 })
1665 }
1666 PrimitiveValue::I16(s) if !s.is_empty() => {
1667 NumCast::from(s[0]).ok_or_else(|| ConvertValueError {
1668 requested: "float64",
1669 original: self.value_type(),
1670 cause: Some(
1671 NarrowConvertSnafu {
1672 value: s[0].to_string(),
1673 }
1674 .build()
1675 .into(),
1676 ),
1677 })
1678 }
1679 PrimitiveValue::U32(s) if !s.is_empty() => {
1680 NumCast::from(s[0]).ok_or_else(|| ConvertValueError {
1681 requested: "float64",
1682 original: self.value_type(),
1683 cause: Some(
1684 NarrowConvertSnafu {
1685 value: s[0].to_string(),
1686 }
1687 .build()
1688 .into(),
1689 ),
1690 })
1691 }
1692 PrimitiveValue::I32(s) if !s.is_empty() => {
1693 NumCast::from(s[0]).ok_or_else(|| ConvertValueError {
1694 requested: "float64",
1695 original: self.value_type(),
1696 cause: Some(
1697 NarrowConvertSnafu {
1698 value: s[0].to_string(),
1699 }
1700 .build()
1701 .into(),
1702 ),
1703 })
1704 }
1705 PrimitiveValue::U64(s) if !s.is_empty() => {
1706 NumCast::from(s[0]).ok_or_else(|| ConvertValueError {
1707 requested: "float64",
1708 original: self.value_type(),
1709 cause: Some(
1710 NarrowConvertSnafu {
1711 value: s[0].to_string(),
1712 }
1713 .build()
1714 .into(),
1715 ),
1716 })
1717 }
1718 PrimitiveValue::I64(s) if !s.is_empty() => {
1719 NumCast::from(s[0]).ok_or_else(|| ConvertValueError {
1720 requested: "float64",
1721 original: self.value_type(),
1722 cause: Some(
1723 NarrowConvertSnafu {
1724 value: s[0].to_string(),
1725 }
1726 .build()
1727 .into(),
1728 ),
1729 })
1730 }
1731 PrimitiveValue::F32(s) if !s.is_empty() => {
1732 NumCast::from(s[0]).ok_or_else(|| ConvertValueError {
1733 requested: "float64",
1734 original: self.value_type(),
1735 cause: Some(
1736 NarrowConvertSnafu {
1737 value: s[0].to_string(),
1738 }
1739 .build()
1740 .into(),
1741 ),
1742 })
1743 }
1744 PrimitiveValue::F64(s) if !s.is_empty() => Ok(s[0]),
1745 _ => Err(ConvertValueError {
1746 requested: "float64",
1747 original: self.value_type(),
1748 cause: None,
1749 }),
1750 }
1751 }
1752
1753 /// Retrieve a sequence of double-precision floating point numbers
1754 /// from this value.
1755 ///
1756 /// If the value is already represented as numbers,
1757 /// they are returned after a conversion to `f64`.
1758 /// An error is returned if any of the numbers cannot be represented
1759 /// by an `f64`.
1760 /// If the value is a string or sequence of strings,
1761 /// the strings are parsed to obtain a number,
1762 /// potentially failing if the string does not represent a valid number.
1763 /// The string is stripped of leading/trailing whitespace before parsing.
1764 /// If the value is a sequence of U8 bytes,
1765 /// the bytes are individually interpreted as independent numbers.
1766 /// Otherwise, the operation fails.
1767 ///
1768 /// # Example
1769 ///
1770 /// ```
1771 /// # use dicom_core::value::{C, PrimitiveValue};
1772 /// # use smallvec::smallvec;
1773 ///
1774 /// assert_eq!(
1775 /// PrimitiveValue::F64(smallvec![
1776 /// 1.5, 2., 5.,
1777 /// ])
1778 /// .to_multi_float64().ok(),
1779 /// Some(vec![1.5_f64, 2., 5.]),
1780 /// );
1781 ///
1782 /// assert_eq!(
1783 /// PrimitiveValue::from("-6.75 ").to_multi_float64().ok(),
1784 /// Some(vec![-6.75]),
1785 /// );
1786 /// ```
1787 pub fn to_multi_float64(&self) -> Result<Vec<f64>, ConvertValueError> {
1788 match self {
1789 PrimitiveValue::Str(s) => {
1790 let out = s
1791 .trim_matches(whitespace_or_null)
1792 .parse()
1793 .context(ParseFloatSnafu)
1794 .map_err(|err| ConvertValueError {
1795 requested: "float64",
1796 original: self.value_type(),
1797 cause: Some(Box::from(err)),
1798 })?;
1799 Ok(vec![out])
1800 }
1801 PrimitiveValue::Strs(s) => s
1802 .iter()
1803 .map(|v| {
1804 v.trim_matches(whitespace_or_null)
1805 .parse()
1806 .context(ParseFloatSnafu)
1807 .map_err(|err| ConvertValueError {
1808 requested: "float64",
1809 original: self.value_type(),
1810 cause: Some(Box::from(err)),
1811 })
1812 })
1813 .collect::<Result<Vec<_>, _>>(),
1814 PrimitiveValue::U8(bytes) => bytes
1815 .iter()
1816 .map(|v| {
1817 NumCast::from(*v).ok_or_else(|| ConvertValueError {
1818 requested: "float64",
1819 original: self.value_type(),
1820 cause: Some(
1821 NarrowConvertSnafu {
1822 value: v.to_string(),
1823 }
1824 .build()
1825 .into(),
1826 ),
1827 })
1828 })
1829 .collect::<Result<Vec<_>, _>>(),
1830 PrimitiveValue::U16(s) => s
1831 .iter()
1832 .map(|v| {
1833 NumCast::from(*v).ok_or_else(|| ConvertValueError {
1834 requested: "float64",
1835 original: self.value_type(),
1836 cause: Some(
1837 NarrowConvertSnafu {
1838 value: v.to_string(),
1839 }
1840 .build()
1841 .into(),
1842 ),
1843 })
1844 })
1845 .collect::<Result<Vec<_>, _>>(),
1846 PrimitiveValue::I16(s) => s
1847 .iter()
1848 .map(|v| {
1849 NumCast::from(*v).ok_or_else(|| ConvertValueError {
1850 requested: "float64",
1851 original: self.value_type(),
1852 cause: Some(
1853 NarrowConvertSnafu {
1854 value: v.to_string(),
1855 }
1856 .build()
1857 .into(),
1858 ),
1859 })
1860 })
1861 .collect::<Result<Vec<_>, _>>(),
1862 PrimitiveValue::U32(s) => s
1863 .iter()
1864 .map(|v| {
1865 NumCast::from(*v).ok_or_else(|| ConvertValueError {
1866 requested: "float64",
1867 original: self.value_type(),
1868 cause: Some(
1869 NarrowConvertSnafu {
1870 value: v.to_string(),
1871 }
1872 .build()
1873 .into(),
1874 ),
1875 })
1876 })
1877 .collect::<Result<Vec<_>, _>>(),
1878 PrimitiveValue::I32(s) => s
1879 .iter()
1880 .map(|v| {
1881 NumCast::from(*v).ok_or_else(|| ConvertValueError {
1882 requested: "float64",
1883 original: self.value_type(),
1884 cause: Some(
1885 NarrowConvertSnafu {
1886 value: v.to_string(),
1887 }
1888 .build()
1889 .into(),
1890 ),
1891 })
1892 })
1893 .collect::<Result<Vec<_>, _>>(),
1894 PrimitiveValue::U64(s) => s
1895 .iter()
1896 .map(|v| {
1897 NumCast::from(*v).ok_or_else(|| ConvertValueError {
1898 requested: "float64",
1899 original: self.value_type(),
1900 cause: Some(
1901 NarrowConvertSnafu {
1902 value: v.to_string(),
1903 }
1904 .build()
1905 .into(),
1906 ),
1907 })
1908 })
1909 .collect::<Result<Vec<_>, _>>(),
1910 PrimitiveValue::I64(s) => s
1911 .iter()
1912 .map(|v| {
1913 NumCast::from(*v).ok_or_else(|| ConvertValueError {
1914 requested: "float64",
1915 original: self.value_type(),
1916 cause: Some(
1917 NarrowConvertSnafu {
1918 value: v.to_string(),
1919 }
1920 .build()
1921 .into(),
1922 ),
1923 })
1924 })
1925 .collect::<Result<Vec<_>, _>>(),
1926 PrimitiveValue::F32(s) => s
1927 .iter()
1928 .map(|v| {
1929 NumCast::from(*v).ok_or_else(|| ConvertValueError {
1930 requested: "float64",
1931 original: self.value_type(),
1932 cause: Some(
1933 NarrowConvertSnafu {
1934 value: v.to_string(),
1935 }
1936 .build()
1937 .into(),
1938 ),
1939 })
1940 })
1941 .collect::<Result<Vec<_>, _>>(),
1942 PrimitiveValue::F64(s) => Ok(s[..].to_owned()),
1943 _ => Err(ConvertValueError {
1944 requested: "float32",
1945 original: self.value_type(),
1946 cause: None,
1947 }),
1948 }
1949 }
1950
1951 /// Retrieve a single `chrono::NaiveDate` from this value.
1952 ///
1953 /// Please note, that this is a shortcut to obtain a usable date from a primitive value.
1954 /// As per standard, the stored value might not be precise. It is highly recommended to
1955 /// use [`.to_date()`](PrimitiveValue::to_date) as the only way to obtain dates.
1956 ///
1957 /// If the value is already represented as a precise `DicomDate`, it is converted
1958 /// to a `NaiveDate` value. It fails for imprecise values.
1959 /// If the value is a string or sequence of strings,
1960 /// the first string is decoded to obtain a date, potentially failing if the
1961 /// string does not represent a valid date.
1962 /// If the value is a sequence of U8 bytes, the bytes are
1963 /// first interpreted as an ASCII character string.
1964 ///
1965 /// Users are advised that this method is DICOM compliant and a full
1966 /// date representation of YYYYMMDD is required. Otherwise, the operation fails.
1967 ///
1968 /// Partial precision dates are handled by `DicomDate`, which can be retrieved
1969 /// by [`.to_date()`](PrimitiveValue::to_date).
1970 ///
1971 /// # Example
1972 ///
1973 /// ```
1974 /// # use dicom_core::value::{C, PrimitiveValue, DicomDate};
1975 /// # use smallvec::smallvec;
1976 /// # use chrono::NaiveDate;
1977 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1978 ///
1979 /// assert_eq!(
1980 /// PrimitiveValue::Date(smallvec![
1981 /// DicomDate::from_ymd(2014, 10, 12)?,
1982 /// ])
1983 /// .to_naive_date().ok(),
1984 /// Some(NaiveDate::from_ymd(2014, 10, 12)),
1985 /// );
1986 ///
1987 /// assert_eq!(
1988 /// PrimitiveValue::Strs(smallvec![
1989 /// "20141012".to_string(),
1990 /// ])
1991 /// .to_naive_date().ok(),
1992 /// Some(NaiveDate::from_ymd(2014, 10, 12)),
1993 /// );
1994 ///
1995 /// assert!(
1996 /// PrimitiveValue::Str("201410".to_string())
1997 /// .to_naive_date().is_err()
1998 /// );
1999 /// # Ok(())
2000 /// # }
2001 /// ```
2002 pub fn to_naive_date(&self) -> Result<NaiveDate, ConvertValueError> {
2003 match self {
2004 PrimitiveValue::Date(v) if !v.is_empty() => v[0]
2005 .to_naive_date()
2006 .context(ParseDateRangeSnafu)
2007 .map_err(|err| ConvertValueError {
2008 requested: "NaiveDate",
2009 original: self.value_type(),
2010 cause: Some(Box::from(err)),
2011 }),
2012 PrimitiveValue::Str(s) => super::deserialize::parse_date(s.as_bytes())
2013 .context(ParseDateSnafu)
2014 .map_err(|err| ConvertValueError {
2015 requested: "NaiveDate",
2016 original: self.value_type(),
2017 cause: Some(Box::from(err)),
2018 }),
2019 PrimitiveValue::Strs(s) => {
2020 super::deserialize::parse_date(s.first().map(|s| s.as_bytes()).unwrap_or(&[]))
2021 .context(ParseDateSnafu)
2022 .map_err(|err| ConvertValueError {
2023 requested: "NaiveDate",
2024 original: self.value_type(),
2025 cause: Some(Box::from(err)),
2026 })
2027 }
2028 PrimitiveValue::U8(bytes) => super::deserialize::parse_date(bytes)
2029 .context(ParseDateSnafu)
2030 .map_err(|err| ConvertValueError {
2031 requested: "NaiveDate",
2032 original: self.value_type(),
2033 cause: Some(Box::from(err)),
2034 }),
2035 _ => Err(ConvertValueError {
2036 requested: "NaiveDate",
2037 original: self.value_type(),
2038 cause: None,
2039 }),
2040 }
2041 }
2042
2043 /// Retrieve the full sequence of `chrono::NaiveDate`s from this value.
2044 ///
2045 /// Please note, that this is a shortcut to obtain usable dates from a primitive value.
2046 /// As per standard, the stored values might not be precise. It is highly recommended to
2047 /// use [`.to_multi_date()`](PrimitiveValue::to_multi_date) as the only way to obtain dates.
2048 ///
2049 /// If the value is already represented as a sequence of precise `DicomDate` values,
2050 /// it is converted. It fails for imprecise values.
2051 /// If the value is a string or sequence of strings,
2052 /// the strings are decoded to obtain a date, potentially failing if
2053 /// any of the strings does not represent a valid date.
2054 /// If the value is a sequence of U8 bytes, the bytes are
2055 /// first interpreted as an ASCII character string,
2056 /// then as a backslash-separated list of dates.
2057 ///
2058 /// Users are advised that this method is DICOM compliant and a full
2059 /// date representation of YYYYMMDD is required. Otherwise, the operation fails.
2060 ///
2061 /// Partial precision dates are handled by `DicomDate`, which can be retrieved
2062 /// by [`.to_multi_date()`](PrimitiveValue::to_multi_date).
2063 ///
2064 /// # Example
2065 ///
2066 /// ```
2067 /// # use dicom_core::value::{C, PrimitiveValue, DicomDate};
2068 /// # use smallvec::smallvec;
2069 /// # use chrono::NaiveDate;
2070 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
2071 ///
2072 /// assert_eq!(
2073 /// PrimitiveValue::Date(smallvec![
2074 /// DicomDate::from_ymd(2014, 10, 12)?,
2075 /// ]).to_multi_naive_date().ok(),
2076 /// Some(vec![NaiveDate::from_ymd(2014, 10, 12)]),
2077 /// );
2078 ///
2079 /// assert_eq!(
2080 /// PrimitiveValue::Strs(smallvec![
2081 /// "20141012".to_string(),
2082 /// "20200828".to_string(),
2083 /// ]).to_multi_naive_date().ok(),
2084 /// Some(vec![
2085 /// NaiveDate::from_ymd(2014, 10, 12),
2086 /// NaiveDate::from_ymd(2020, 8, 28),
2087 /// ]),
2088 /// );
2089 /// # Ok(())
2090 /// # }
2091 /// ```
2092 pub fn to_multi_naive_date(&self) -> Result<Vec<NaiveDate>, ConvertValueError> {
2093 match self {
2094 PrimitiveValue::Date(v) if !v.is_empty() => v
2095 .into_iter()
2096 .map(|d| d.to_naive_date())
2097 .collect::<Result<Vec<_>, _>>()
2098 .context(ParseDateRangeSnafu)
2099 .map_err(|err| ConvertValueError {
2100 requested: "NaiveDate",
2101 original: self.value_type(),
2102 cause: Some(Box::from(err)),
2103 }),
2104 PrimitiveValue::Str(s) => {
2105 super::deserialize::parse_date(s.trim_end_matches(whitespace_or_null).as_bytes())
2106 .map(|date| vec![date])
2107 .context(ParseDateSnafu)
2108 .map_err(|err| ConvertValueError {
2109 requested: "NaiveDate",
2110 original: self.value_type(),
2111 cause: Some(Box::from(err)),
2112 })
2113 }
2114 PrimitiveValue::Strs(s) => s
2115 .into_iter()
2116 .map(|s| {
2117 super::deserialize::parse_date(
2118 s.trim_end_matches(whitespace_or_null).as_bytes(),
2119 )
2120 })
2121 .collect::<Result<Vec<_>, _>>()
2122 .context(ParseDateSnafu)
2123 .map_err(|err| ConvertValueError {
2124 requested: "NaiveDate",
2125 original: self.value_type(),
2126 cause: Some(Box::from(err)),
2127 }),
2128 PrimitiveValue::U8(bytes) => trim_last_whitespace(bytes)
2129 .split(|c| *c == b'\\')
2130 .map(super::deserialize::parse_date)
2131 .collect::<Result<Vec<_>, _>>()
2132 .context(ParseDateSnafu)
2133 .map_err(|err| ConvertValueError {
2134 requested: "NaiveDate",
2135 original: self.value_type(),
2136 cause: Some(Box::from(err)),
2137 }),
2138 _ => Err(ConvertValueError {
2139 requested: "NaiveDate",
2140 original: self.value_type(),
2141 cause: None,
2142 }),
2143 }
2144 }
2145
2146 /// Retrieve a single `DicomDate` from this value.
2147 ///
2148 /// If the value is already represented as a `DicomDate`, it is returned.
2149 /// If the value is a string or sequence of strings,
2150 /// the first string is decoded to obtain a DicomDate, potentially failing if the
2151 /// string does not represent a valid DicomDate.
2152 /// If the value is a sequence of U8 bytes, the bytes are
2153 /// first interpreted as an ASCII character string.
2154 ///
2155 /// Unlike Rust's `chrono::NaiveDate`, `DicomDate` allows for missing date components.
2156 /// DicomDate implements `AsRange` trait, so specific `chrono::NaiveDate` values can be retrieved.
2157 /// - [`.exact()`](crate::value::range::AsRange::exact)
2158 /// - [`.earliest()`](crate::value::range::AsRange::earliest)
2159 /// - [`.latest()`](crate::value::range::AsRange::latest)
2160 /// - [`.range()`](crate::value::range::AsRange::range)
2161 ///
2162 /// # Example
2163 ///
2164 /// ```
2165 /// # use dicom_core::value::{C, PrimitiveValue};
2166 /// # use smallvec::smallvec;
2167 /// # use chrono::NaiveDate;
2168 /// # use std::error::Error;
2169 /// use dicom_core::value::{AsRange, DicomDate};
2170 /// # fn main() -> Result<(), Box<dyn Error>> {
2171 ///
2172 /// let value = PrimitiveValue::Str("200002".into());
2173 /// let date = value.to_date()?;
2174 ///
2175 /// // it is not precise, day of month is unspecified
2176 /// assert_eq!(
2177 /// date.is_precise(),
2178 /// false
2179 /// );
2180 /// assert_eq!(
2181 /// date.earliest()?,
2182 /// NaiveDate::from_ymd(2000,2,1)
2183 /// );
2184 /// assert_eq!(
2185 /// date.latest()?,
2186 /// NaiveDate::from_ymd(2000,2,29)
2187 /// );
2188 /// assert!(date.exact().is_err());
2189 ///
2190 /// let date = PrimitiveValue::Str("20000201".into()).to_date()?;
2191 /// assert_eq!(
2192 /// date.is_precise(),
2193 /// true
2194 /// );
2195 /// // .to_naive_date() works only for precise values
2196 /// assert_eq!(
2197 /// date.exact()?,
2198 /// date.to_naive_date()?
2199 /// );
2200 /// # Ok(())
2201 /// # }
2202 ///
2203 /// ```
2204 pub fn to_date(&self) -> Result<DicomDate, ConvertValueError> {
2205 match self {
2206 PrimitiveValue::Date(d) if !d.is_empty() => Ok(d[0]),
2207 PrimitiveValue::Str(s) => super::deserialize::parse_date_partial(s.as_bytes())
2208 .map(|(date, _)| date)
2209 .context(ParseDateSnafu)
2210 .map_err(|err| ConvertValueError {
2211 requested: "DicomDate",
2212 original: self.value_type(),
2213 cause: Some(Box::from(err)),
2214 }),
2215 PrimitiveValue::Strs(s) => super::deserialize::parse_date_partial(
2216 s.first().map(|s| s.as_bytes()).unwrap_or(&[]),
2217 )
2218 .map(|(date, _)| date)
2219 .context(ParseDateSnafu)
2220 .map_err(|err| ConvertValueError {
2221 requested: "DicomDate",
2222 original: self.value_type(),
2223 cause: Some(Box::from(err)),
2224 }),
2225 PrimitiveValue::U8(bytes) => super::deserialize::parse_date_partial(bytes)
2226 .map(|(date, _)| date)
2227 .context(ParseDateSnafu)
2228 .map_err(|err| ConvertValueError {
2229 requested: "DicomDate",
2230 original: self.value_type(),
2231 cause: Some(Box::from(err)),
2232 }),
2233 _ => Err(ConvertValueError {
2234 requested: "DicomDate",
2235 original: self.value_type(),
2236 cause: None,
2237 }),
2238 }
2239 }
2240
2241 /// Retrieve the full sequence of `DicomDate`s from this value.
2242 ///
2243 /// # Example
2244 /// ```
2245 /// # use dicom_core::value::{PrimitiveValue};
2246 /// # use dicom_core::dicom_value;
2247 /// use dicom_core::value::DicomDate;
2248 /// # use std::error::Error;
2249 /// # fn main() -> Result<(), Box<dyn Error>> {
2250 ///
2251 /// assert_eq!(
2252 /// dicom_value!(Strs, ["201410", "2020", "20200101"])
2253 /// .to_multi_date()?,
2254 /// vec![
2255 /// DicomDate::from_ym(2014, 10)?,
2256 /// DicomDate::from_y(2020)?,
2257 /// DicomDate::from_ymd(2020, 1, 1)?
2258 /// ]);
2259 ///
2260 /// # Ok(())
2261 /// # }
2262 /// ```
2263 ///
2264 pub fn to_multi_date(&self) -> Result<Vec<DicomDate>, ConvertValueError> {
2265 match self {
2266 PrimitiveValue::Date(d) => Ok(d.to_vec()),
2267 PrimitiveValue::Str(s) => super::deserialize::parse_date_partial(
2268 s.trim_end_matches(whitespace_or_null).as_bytes(),
2269 )
2270 .map(|(date, _)| vec![date])
2271 .context(ParseDateSnafu)
2272 .map_err(|err| ConvertValueError {
2273 requested: "DicomDate",
2274 original: self.value_type(),
2275 cause: Some(Box::from(err)),
2276 }),
2277 PrimitiveValue::Strs(s) => s
2278 .into_iter()
2279 .map(|s| {
2280 super::deserialize::parse_date_partial(
2281 s.trim_end_matches(whitespace_or_null).as_bytes(),
2282 )
2283 .map(|(date, _rest)| date)
2284 })
2285 .collect::<Result<Vec<_>, _>>()
2286 .context(ParseDateSnafu)
2287 .map_err(|err| ConvertValueError {
2288 requested: "DicomDate",
2289 original: self.value_type(),
2290 cause: Some(Box::from(err)),
2291 }),
2292 PrimitiveValue::U8(bytes) => trim_last_whitespace(bytes)
2293 .split(|c| *c == b'\\')
2294 .map(|s| super::deserialize::parse_date_partial(s).map(|(date, _rest)| date))
2295 .collect::<Result<Vec<_>, _>>()
2296 .context(ParseDateSnafu)
2297 .map_err(|err| ConvertValueError {
2298 requested: "DicomDate",
2299 original: self.value_type(),
2300 cause: Some(Box::from(err)),
2301 }),
2302 _ => Err(ConvertValueError {
2303 requested: "DicomDate",
2304 original: self.value_type(),
2305 cause: None,
2306 }),
2307 }
2308 }
2309
2310 /// Retrieve a single `chrono::NaiveTime` from this value.
2311 ///
2312 /// Please note, that this is a shortcut to obtain a usable time from a primitive value.
2313 /// As per standard, the stored value might not be precise. It is highly recommended to
2314 /// use [`.to_time()`](PrimitiveValue::to_time) as the only way to obtain times.
2315 ///
2316 /// If the value is represented as a precise `DicomTime`,
2317 /// it is converted to a `NaiveTime`.
2318 /// It fails for imprecise values,
2319 /// as in, those which do not specify up to at least the seconds.
2320 /// If the value is a string or sequence of strings,
2321 /// the first string is decoded to obtain a time, potentially failing if the
2322 /// string does not represent a valid time.
2323 /// If the value is a sequence of U8 bytes, the bytes are
2324 /// first interpreted as an ASCII character string.
2325 /// Otherwise, the operation fails.
2326 ///
2327 /// Partial precision times are handled by `DicomTime`,
2328 /// which can be retrieved by [`.to_time()`](PrimitiveValue::to_time).
2329 ///
2330 /// # Example
2331 ///
2332 /// ```
2333 /// # use dicom_core::value::{C, PrimitiveValue, DicomTime};
2334 /// # use smallvec::smallvec;
2335 /// # use chrono::NaiveTime;
2336 /// # use std::error::Error;
2337 /// # fn main() -> Result<(), Box<dyn Error>> {
2338 ///
2339 /// assert_eq!(
2340 /// PrimitiveValue::from(DicomTime::from_hms(11, 2, 45)?).to_naive_time().ok(),
2341 /// Some(NaiveTime::from_hms(11, 2, 45)),
2342 /// );
2343 ///
2344 /// assert_eq!(
2345 /// PrimitiveValue::from("110245.78").to_naive_time().ok(),
2346 /// Some(NaiveTime::from_hms_milli(11, 2, 45, 780)),
2347 /// );
2348 /// # Ok(())
2349 /// # }
2350 /// ```
2351 pub fn to_naive_time(&self) -> Result<NaiveTime, ConvertValueError> {
2352 match self {
2353 PrimitiveValue::Time(v) if !v.is_empty() => v[0]
2354 .to_naive_time()
2355 .context(ParseTimeRangeSnafu)
2356 .map_err(|err| ConvertValueError {
2357 requested: "NaiveTime",
2358 original: self.value_type(),
2359 cause: Some(Box::from(err)),
2360 }),
2361 PrimitiveValue::Str(s) => {
2362 super::deserialize::parse_time(s.trim_end_matches(whitespace_or_null).as_bytes())
2363 .map(|(date, _rest)| date)
2364 .context(ParseTimeSnafu)
2365 .map_err(|err| ConvertValueError {
2366 requested: "NaiveTime",
2367 original: self.value_type(),
2368 cause: Some(Box::from(err)),
2369 })
2370 }
2371 PrimitiveValue::Strs(s) => super::deserialize::parse_time(
2372 s.first()
2373 .map(|s| s.trim_end_matches(whitespace_or_null).as_bytes())
2374 .unwrap_or(&[]),
2375 )
2376 .map(|(date, _rest)| date)
2377 .context(ParseTimeSnafu)
2378 .map_err(|err| ConvertValueError {
2379 requested: "NaiveTime",
2380 original: self.value_type(),
2381 cause: Some(Box::from(err)),
2382 }),
2383 PrimitiveValue::U8(bytes) => {
2384 super::deserialize::parse_time(trim_last_whitespace(bytes))
2385 .map(|(date, _rest)| date)
2386 .context(ParseTimeSnafu)
2387 .map_err(|err| ConvertValueError {
2388 requested: "NaiveTime",
2389 original: self.value_type(),
2390 cause: Some(Box::from(err)),
2391 })
2392 }
2393 _ => Err(ConvertValueError {
2394 requested: "NaiveTime",
2395 original: self.value_type(),
2396 cause: None,
2397 }),
2398 }
2399 }
2400
2401 /// Retrieve the full sequence of `chrono::NaiveTime`s from this value.
2402 ///
2403 /// Please note, that this is a shortcut to obtain a usable time from a primitive value.
2404 /// As per standard, the stored values might not be precise. It is highly recommended to
2405 /// use [`.to_multi_time()`](PrimitiveValue::to_multi_time) as the only way to obtain times.
2406 ///
2407 /// If the value is already represented as a sequence of precise `DicomTime` values,
2408 /// it is converted to a sequence of `NaiveTime` values. It fails for imprecise values.
2409 /// If the value is a string or sequence of strings,
2410 /// the strings are decoded to obtain a date, potentially failing if
2411 /// any of the strings does not represent a valid date.
2412 /// If the value is a sequence of U8 bytes, the bytes are
2413 /// first interpreted as an ASCII character string,
2414 /// then as a backslash-separated list of times.
2415 /// Otherwise, the operation fails.
2416 ///
2417 /// Users are advised that this method requires at least 1 out of 6 digits of the second
2418 /// fraction .F to be present. Otherwise, the operation fails.
2419 ///
2420 /// Partial precision times are handled by `DicomTime`,
2421 /// which can be retrieved by [`.to_multi_time()`](PrimitiveValue::to_multi_time).
2422 ///
2423 /// # Example
2424 ///
2425 /// ```
2426 /// # use dicom_core::value::{C, PrimitiveValue, DicomTime};
2427 /// # use smallvec::smallvec;
2428 /// # use chrono::NaiveTime;
2429 /// # use std::error::Error;
2430 /// # fn main() -> Result<(), Box<dyn Error>> {
2431 ///
2432 /// assert_eq!(
2433 /// PrimitiveValue::from(DicomTime::from_hms(22, 58, 2)?).to_multi_naive_time().ok(),
2434 /// Some(vec![NaiveTime::from_hms(22, 58, 2)]),
2435 /// );
2436 ///
2437 /// assert_eq!(
2438 /// PrimitiveValue::Strs(smallvec![
2439 /// "225802.1".to_string(),
2440 /// "225916.742388".to_string(),
2441 /// ]).to_multi_naive_time().ok(),
2442 /// Some(vec![
2443 /// NaiveTime::from_hms_micro(22, 58, 2, 100_000),
2444 /// NaiveTime::from_hms_micro(22, 59, 16, 742_388),
2445 /// ]),
2446 /// );
2447 /// # Ok(())
2448 /// # }
2449 /// ```
2450 pub fn to_multi_naive_time(&self) -> Result<Vec<NaiveTime>, ConvertValueError> {
2451 match self {
2452 PrimitiveValue::Time(v) if !v.is_empty() => v
2453 .into_iter()
2454 .map(|t| t.to_naive_time())
2455 .collect::<Result<Vec<_>, _>>()
2456 .context(ParseTimeRangeSnafu)
2457 .map_err(|err| ConvertValueError {
2458 requested: "NaiveTime",
2459 original: self.value_type(),
2460 cause: Some(Box::from(err)),
2461 }),
2462 PrimitiveValue::Str(s) => {
2463 super::deserialize::parse_time(s.trim_end_matches(whitespace_or_null).as_bytes())
2464 .map(|(date, _rest)| vec![date])
2465 .context(ParseDateSnafu)
2466 .map_err(|err| ConvertValueError {
2467 requested: "NaiveTime",
2468 original: self.value_type(),
2469 cause: Some(Box::from(err)),
2470 })
2471 }
2472 PrimitiveValue::Strs(s) => s
2473 .into_iter()
2474 .map(|s| {
2475 super::deserialize::parse_time(
2476 s.trim_end_matches(whitespace_or_null).as_bytes(),
2477 )
2478 .map(|(date, _rest)| date)
2479 })
2480 .collect::<Result<Vec<_>, _>>()
2481 .context(ParseDateSnafu)
2482 .map_err(|err| ConvertValueError {
2483 requested: "NaiveTime",
2484 original: self.value_type(),
2485 cause: Some(Box::from(err)),
2486 }),
2487 PrimitiveValue::U8(bytes) => trim_last_whitespace(bytes)
2488 .split(|c| *c == b'\\')
2489 .map(|s| super::deserialize::parse_time(s).map(|(date, _rest)| date))
2490 .collect::<Result<Vec<_>, _>>()
2491 .context(ParseDateSnafu)
2492 .map_err(|err| ConvertValueError {
2493 requested: "NaiveTime",
2494 original: self.value_type(),
2495 cause: Some(Box::from(err)),
2496 }),
2497 _ => Err(ConvertValueError {
2498 requested: "NaiveTime",
2499 original: self.value_type(),
2500 cause: None,
2501 }),
2502 }
2503 }
2504
2505 /// Retrieve a single `DicomTime` from this value.
2506 ///
2507 /// If the value is already represented as a time, it is converted into DicomTime.
2508 /// If the value is a string or sequence of strings,
2509 /// the first string is decoded to obtain a DicomTime, potentially failing if the
2510 /// string does not represent a valid DicomTime.
2511 /// If the value is a sequence of U8 bytes, the bytes are
2512 /// first interpreted as an ASCII character string.
2513 ///
2514 /// Unlike Rust's `chrono::NaiveTime`, `DicomTime` allows for missing time components.
2515 /// DicomTime implements `AsRange` trait, so specific `chrono::NaiveTime` values can be retrieved.
2516 /// - [`.exact()`](crate::value::range::AsRange::exact)
2517 /// - [`.earliest()`](crate::value::range::AsRange::earliest)
2518 /// - [`.latest()`](crate::value::range::AsRange::latest)
2519 /// - [`.range()`](crate::value::range::AsRange::range)
2520 ///
2521 /// # Example
2522 ///
2523 /// ```
2524 /// # use dicom_core::value::{C, PrimitiveValue};
2525 /// # use chrono::NaiveTime;
2526 /// use dicom_core::value::{AsRange, DicomTime};
2527 /// # use std::error::Error;
2528 /// # fn main() -> Result<(), Box<dyn Error>> {
2529 ///
2530 /// let value = PrimitiveValue::Str("10".into());
2531 /// let time = value.to_time()?;
2532 ///
2533 /// // is not precise, minute, second and second fraction are unspecified
2534 /// assert_eq!(
2535 /// time.is_precise(),
2536 /// false
2537 /// );
2538 /// assert_eq!(
2539 /// time.earliest()?,
2540 /// NaiveTime::from_hms(10,0,0)
2541 /// );
2542 /// assert_eq!(
2543 /// time.latest()?,
2544 /// NaiveTime::from_hms_micro(10,59,59,999_999)
2545 /// );
2546 /// assert!(time.exact().is_err());
2547 ///
2548 /// let second = PrimitiveValue::Str("101259".into());
2549 /// // not a precise value, fraction of second is unspecified
2550 /// assert!(second.to_time()?.exact().is_err());
2551 ///
2552 /// // .to_naive_time() yields a result, for at least second precision values
2553 /// // second fraction defaults to zeros
2554 /// assert_eq!(
2555 /// second.to_time()?.to_naive_time()?,
2556 /// NaiveTime::from_hms(10,12,59)
2557 /// );
2558 ///
2559 /// let fraction6 = PrimitiveValue::Str("101259.123456".into());
2560 /// let fraction5 = PrimitiveValue::Str("101259.12345".into());
2561 ///
2562 /// // is not precise, last digit of second fraction is unspecified
2563 /// assert!(
2564 /// fraction5.to_time()?.exact().is_err()
2565 /// );
2566 /// assert!(
2567 /// fraction6.to_time()?.exact().is_ok()
2568 /// );
2569 ///
2570 /// assert_eq!(
2571 /// fraction6.to_time()?.exact()?,
2572 /// fraction6.to_time()?.to_naive_time()?
2573 /// );
2574 ///
2575 /// # Ok(())
2576 /// # }
2577 /// ```
2578 pub fn to_time(&self) -> Result<DicomTime, ConvertValueError> {
2579 match self {
2580 PrimitiveValue::Time(t) if !t.is_empty() => Ok(t[0]),
2581 PrimitiveValue::Str(s) => super::deserialize::parse_time_partial(
2582 s.trim_end_matches(whitespace_or_null).as_bytes(),
2583 )
2584 .map(|(date, _rest)| date)
2585 .context(ParseTimeSnafu)
2586 .map_err(|err| ConvertValueError {
2587 requested: "DicomTime",
2588 original: self.value_type(),
2589 cause: Some(Box::from(err)),
2590 }),
2591 PrimitiveValue::Strs(s) => super::deserialize::parse_time_partial(
2592 s.first()
2593 .map(|s| s.trim_end_matches(whitespace_or_null).as_bytes())
2594 .unwrap_or(&[]),
2595 )
2596 .map(|(date, _rest)| date)
2597 .context(ParseTimeSnafu)
2598 .map_err(|err| ConvertValueError {
2599 requested: "DicomTime",
2600 original: self.value_type(),
2601 cause: Some(Box::from(err)),
2602 }),
2603 PrimitiveValue::U8(bytes) => {
2604 super::deserialize::parse_time_partial(trim_last_whitespace(bytes))
2605 .map(|(date, _rest)| date)
2606 .context(ParseTimeSnafu)
2607 .map_err(|err| ConvertValueError {
2608 requested: "DicomTime",
2609 original: self.value_type(),
2610 cause: Some(Box::from(err)),
2611 })
2612 }
2613 _ => Err(ConvertValueError {
2614 requested: "DicomTime",
2615 original: self.value_type(),
2616 cause: None,
2617 }),
2618 }
2619 }
2620
2621 /// Retrieve the full sequence of `DicomTime`s from this value.
2622 ///
2623 /// If the value is already represented as a time, it is converted into DicomTime.
2624 /// If the value is a string or sequence of strings,
2625 /// the first string is decoded to obtain a DicomTime, potentially failing if the
2626 /// string does not represent a valid DicomTime.
2627 /// If the value is a sequence of U8 bytes, the bytes are
2628 /// first interpreted as an ASCII character string.
2629 ///
2630 /// Unlike Rust's `chrono::NaiveTime`, `DicomTime` allows for missing time components.
2631 /// DicomTime implements `AsRange` trait, so specific `chrono::NaiveTime` values can be retrieved.
2632 /// - [`.exact()`](crate::value::range::AsRange::exact)
2633 /// - [`.earliest()`](crate::value::range::AsRange::earliest)
2634 /// - [`.latest()`](crate::value::range::AsRange::latest)
2635 /// - [`.range()`](crate::value::range::AsRange::range)
2636 ///
2637 /// # Example
2638 ///
2639 /// ```
2640 /// # use std::error::Error;
2641 /// # use dicom_core::value::{C, PrimitiveValue};
2642 /// # use smallvec::smallvec;
2643 /// use dicom_core::value::DicomTime;
2644 /// # fn main() -> Result<(), Box<dyn Error>> {
2645 ///
2646 /// assert_eq!(
2647 /// PrimitiveValue::Strs(smallvec![
2648 /// "2258".to_string(),
2649 /// "225916.000742".to_string(),
2650 /// ]).to_multi_time()?,
2651 /// vec![
2652 /// DicomTime::from_hm(22, 58)?,
2653 /// DicomTime::from_hms_micro(22, 59, 16, 742)?,
2654 /// ],
2655 /// );
2656 ///
2657 /// # Ok(())
2658 /// # }
2659 /// ```
2660 pub fn to_multi_time(&self) -> Result<Vec<DicomTime>, ConvertValueError> {
2661 match self {
2662 PrimitiveValue::Time(t) => Ok(t.to_vec()),
2663 PrimitiveValue::Str(s) => super::deserialize::parse_time_partial(
2664 s.trim_end_matches(whitespace_or_null).as_bytes(),
2665 )
2666 .map(|(date, _rest)| vec![date])
2667 .context(ParseDateSnafu)
2668 .map_err(|err| ConvertValueError {
2669 requested: "DicomTime",
2670 original: self.value_type(),
2671 cause: Some(Box::from(err)),
2672 }),
2673 PrimitiveValue::Strs(s) => s
2674 .into_iter()
2675 .map(|s| {
2676 super::deserialize::parse_time_partial(
2677 s.trim_end_matches(whitespace_or_null).as_bytes(),
2678 )
2679 .map(|(date, _rest)| date)
2680 })
2681 .collect::<Result<Vec<_>, _>>()
2682 .context(ParseDateSnafu)
2683 .map_err(|err| ConvertValueError {
2684 requested: "DicomTime",
2685 original: self.value_type(),
2686 cause: Some(Box::from(err)),
2687 }),
2688 PrimitiveValue::U8(bytes) => trim_last_whitespace(bytes)
2689 .split(|c| *c == b'\\')
2690 .map(|s| super::deserialize::parse_time_partial(s).map(|(date, _rest)| date))
2691 .collect::<Result<Vec<_>, _>>()
2692 .context(ParseDateSnafu)
2693 .map_err(|err| ConvertValueError {
2694 requested: "DicomTime",
2695 original: self.value_type(),
2696 cause: Some(Box::from(err)),
2697 }),
2698 _ => Err(ConvertValueError {
2699 requested: "DicomTime",
2700 original: self.value_type(),
2701 cause: None,
2702 }),
2703 }
2704 }
2705
2706 /// Retrieve a single `DicomDateTime` from this value.
2707 ///
2708 /// If the value is already represented as a date-time, it is converted into DicomDateTime.
2709 /// If the value is a string or sequence of strings,
2710 /// the first string is decoded to obtain a DicomDateTime, potentially failing if the
2711 /// string does not represent a valid DicomDateTime.
2712 /// If the value is a sequence of U8 bytes, the bytes are
2713 /// first interpreted as an ASCII character string.
2714 ///
2715 /// Unlike Rust's `chrono::DateTime`, `DicomDateTime` allows for missing date or time components.
2716 /// DicomDateTime implements `AsRange` trait, so specific `chrono::DateTime` values can be retrieved.
2717 /// - [`.exact()`](crate::value::range::AsRange::exact)
2718 /// - [`.earliest()`](crate::value::range::AsRange::earliest)
2719 /// - [`.latest()`](crate::value::range::AsRange::latest)
2720 /// - [`.range()`](crate::value::range::AsRange::range)
2721 /// # Example
2722 ///
2723 /// ```
2724 /// # use dicom_core::value::{C, PrimitiveValue};
2725 /// # use smallvec::smallvec;
2726 /// # use chrono::{DateTime, FixedOffset, TimeZone, NaiveDateTime, NaiveDate, NaiveTime};
2727 /// # use std::error::Error;
2728 /// use dicom_core::value::{DicomDateTime, AsRange, DateTimeRange, PreciseDateTime};
2729 ///
2730 /// # fn main() -> Result<(), Box<dyn Error>> {
2731 ///
2732 /// // let's parse a date-time text value with 0.1 second precision without a time-zone.
2733 /// let dt_value = PrimitiveValue::from("20121221093001.1").to_datetime()?;
2734 ///
2735 /// assert_eq!(
2736 /// dt_value.earliest()?,
2737 /// PreciseDateTime::Naive(NaiveDateTime::new(
2738 /// NaiveDate::from_ymd_opt(2012, 12, 21).unwrap(),
2739 /// NaiveTime::from_hms_micro_opt(9, 30, 1, 100_000).unwrap()
2740 /// ))
2741 /// );
2742 /// assert_eq!(
2743 /// dt_value.latest()?,
2744 /// PreciseDateTime::Naive(NaiveDateTime::new(
2745 /// NaiveDate::from_ymd_opt(2012, 12, 21).unwrap(),
2746 /// NaiveTime::from_hms_micro_opt(9, 30, 1, 199_999).unwrap()
2747 /// ))
2748 /// );
2749 ///
2750 /// let default_offset = FixedOffset::east_opt(3600).unwrap();
2751 /// // let's parse a date-time text value with full precision with a time-zone east +01:00.
2752 /// let dt_value = PrimitiveValue::from("20121221093001.123456+0100").to_datetime()?;
2753 ///
2754 /// // date-time has all components
2755 /// assert_eq!(dt_value.is_precise(), true);
2756 ///
2757 /// assert_eq!(
2758 /// dt_value.exact()?,
2759 /// PreciseDateTime::TimeZone(
2760 /// default_offset
2761 /// .ymd_opt(2012, 12, 21).unwrap()
2762 /// .and_hms_micro_opt(9, 30, 1, 123_456).unwrap()
2763 /// )
2764 ///
2765 /// );
2766 ///
2767 /// // ranges are inclusive, for a precise value, two identical values are returned
2768 /// assert_eq!(
2769 /// dt_value.range()?,
2770 /// DateTimeRange::from_start_to_end_with_time_zone(
2771 /// FixedOffset::east_opt(3600).unwrap()
2772 /// .ymd_opt(2012, 12, 21).unwrap()
2773 /// .and_hms_micro_opt(9, 30, 1, 123_456).unwrap(),
2774 /// FixedOffset::east_opt(3600).unwrap()
2775 /// .ymd_opt(2012, 12, 21).unwrap()
2776 /// .and_hms_micro_opt(9, 30, 1, 123_456).unwrap()
2777 /// )?
2778 ///
2779 /// );
2780 /// # Ok(())
2781 /// # }
2782 /// ```
2783 pub fn to_datetime(&self) -> Result<DicomDateTime, ConvertValueError> {
2784 match self {
2785 PrimitiveValue::DateTime(v) if !v.is_empty() => Ok(v[0]),
2786 PrimitiveValue::Str(s) => super::deserialize::parse_datetime_partial(
2787 s.trim_end_matches(whitespace_or_null).as_bytes(),
2788 )
2789 .context(ParseDateTimeSnafu)
2790 .map_err(|err| ConvertValueError {
2791 requested: "DicomDateTime",
2792 original: self.value_type(),
2793 cause: Some(Box::from(err)),
2794 }),
2795 PrimitiveValue::Strs(s) => super::deserialize::parse_datetime_partial(
2796 s.first()
2797 .map(|s| s.trim_end_matches(whitespace_or_null).as_bytes())
2798 .unwrap_or(&[]),
2799 )
2800 .context(ParseDateTimeSnafu)
2801 .map_err(|err| ConvertValueError {
2802 requested: "DicomDateTime",
2803 original: self.value_type(),
2804 cause: Some(Box::from(err)),
2805 }),
2806 PrimitiveValue::U8(bytes) => {
2807 super::deserialize::parse_datetime_partial(trim_last_whitespace(bytes))
2808 .context(ParseDateTimeSnafu)
2809 .map_err(|err| ConvertValueError {
2810 requested: "DicomDateTime",
2811 original: self.value_type(),
2812 cause: Some(Box::from(err)),
2813 })
2814 }
2815 _ => Err(ConvertValueError {
2816 requested: "DicomDateTime",
2817 original: self.value_type(),
2818 cause: None,
2819 }),
2820 }
2821 }
2822
2823 /// Retrieve the full sequence of `DicomDateTime`s from this value.
2824 ///
2825 pub fn to_multi_datetime(&self) -> Result<Vec<DicomDateTime>, ConvertValueError> {
2826 match self {
2827 PrimitiveValue::DateTime(v) => Ok(v.to_vec()),
2828 PrimitiveValue::Str(s) => super::deserialize::parse_datetime_partial(
2829 s.trim_end_matches(whitespace_or_null).as_bytes(),
2830 )
2831 .map(|date| vec![date])
2832 .context(ParseDateSnafu)
2833 .map_err(|err| ConvertValueError {
2834 requested: "DicomDateTime",
2835 original: self.value_type(),
2836 cause: Some(Box::from(err)),
2837 }),
2838 PrimitiveValue::Strs(s) => s
2839 .into_iter()
2840 .map(|s| {
2841 super::deserialize::parse_datetime_partial(
2842 s.trim_end_matches(whitespace_or_null).as_bytes(),
2843 )
2844 })
2845 .collect::<Result<Vec<_>, _>>()
2846 .context(ParseDateSnafu)
2847 .map_err(|err| ConvertValueError {
2848 requested: "DicomDateTime",
2849 original: self.value_type(),
2850 cause: Some(Box::from(err)),
2851 }),
2852 PrimitiveValue::U8(bytes) => trim_last_whitespace(bytes)
2853 .split(|c| *c == b'\\')
2854 .map(super::deserialize::parse_datetime_partial)
2855 .collect::<Result<Vec<_>, _>>()
2856 .context(ParseDateSnafu)
2857 .map_err(|err| ConvertValueError {
2858 requested: "DicomDateTime",
2859 original: self.value_type(),
2860 cause: Some(Box::from(err)),
2861 }),
2862 _ => Err(ConvertValueError {
2863 requested: "DicomDateTime",
2864 original: self.value_type(),
2865 cause: None,
2866 }),
2867 }
2868 }
2869 /// Retrieve a single `DateRange` from this value.
2870 ///
2871 /// If the value is already represented as a `DicomDate`, it is converted into `DateRange`.
2872 /// If the value is a string or sequence of strings,
2873 /// the first string is decoded to obtain a `DateRange`, potentially failing if the
2874 /// string does not represent a valid `DateRange`.
2875 /// If the value is a sequence of U8 bytes, the bytes are
2876 /// first interpreted as an ASCII character string.
2877 ///
2878 /// # Example
2879 ///
2880 /// ```
2881 /// # use dicom_core::value::{C, PrimitiveValue};
2882 /// use chrono::{NaiveDate};
2883 /// # use std::error::Error;
2884 /// use dicom_core::value::{DateRange};
2885 ///
2886 /// # fn main() -> Result<(), Box<dyn Error>> {
2887 ///
2888 /// let da_range = PrimitiveValue::from("2012-201305").to_date_range()?;
2889 ///
2890 /// assert_eq!(
2891 /// da_range.start(),
2892 /// Some(&NaiveDate::from_ymd(2012, 1, 1))
2893 /// );
2894 /// assert_eq!(
2895 /// da_range.end(),
2896 /// Some(&NaiveDate::from_ymd(2013, 05, 31))
2897 /// );
2898 ///
2899 /// let range_from = PrimitiveValue::from("2012-").to_date_range()?;
2900 ///
2901 /// assert!(range_from.end().is_none());
2902 ///
2903 /// # Ok(())
2904 /// # }
2905 /// ```
2906 pub fn to_date_range(&self) -> Result<DateRange, ConvertValueError> {
2907 match self {
2908 PrimitiveValue::Date(da) if !da.is_empty() => da[0]
2909 .range()
2910 .context(ParseDateRangeSnafu)
2911 .map_err(|err| ConvertValueError {
2912 requested: "DateRange",
2913 original: self.value_type(),
2914 cause: Some(Box::from(err)),
2915 }),
2916 PrimitiveValue::Str(s) => {
2917 super::range::parse_date_range(s.trim_end_matches(whitespace_or_null).as_bytes())
2918 .context(ParseDateRangeSnafu)
2919 .map_err(|err| ConvertValueError {
2920 requested: "DateRange",
2921 original: self.value_type(),
2922 cause: Some(Box::from(err)),
2923 })
2924 }
2925 PrimitiveValue::Strs(s) => super::range::parse_date_range(
2926 s.first()
2927 .map(|s| s.trim_end_matches(whitespace_or_null).as_bytes())
2928 .unwrap_or(&[]),
2929 )
2930 .context(ParseDateRangeSnafu)
2931 .map_err(|err| ConvertValueError {
2932 requested: "DateRange",
2933 original: self.value_type(),
2934 cause: Some(Box::from(err)),
2935 }),
2936 PrimitiveValue::U8(bytes) => {
2937 super::range::parse_date_range(trim_last_whitespace(bytes))
2938 .context(ParseDateRangeSnafu)
2939 .map_err(|err| ConvertValueError {
2940 requested: "DateRange",
2941 original: self.value_type(),
2942 cause: Some(Box::from(err)),
2943 })
2944 }
2945 _ => Err(ConvertValueError {
2946 requested: "DateRange",
2947 original: self.value_type(),
2948 cause: None,
2949 }),
2950 }
2951 }
2952
2953 /// Retrieve a single `TimeRange` from this value.
2954 ///
2955 /// If the value is already represented as a `DicomTime`, it is converted into a `TimeRange`.
2956 /// If the value is a string or sequence of strings,
2957 /// the first string is decoded to obtain a `TimeRange`, potentially failing if the
2958 /// string does not represent a valid `DateRange`.
2959 /// If the value is a sequence of U8 bytes, the bytes are
2960 /// first interpreted as an ASCII character string.
2961 ///
2962 /// # Example
2963 ///
2964 /// ```
2965 /// # use dicom_core::value::{C, PrimitiveValue};
2966 /// use chrono::{NaiveTime};
2967 /// # use std::error::Error;
2968 /// use dicom_core::value::{TimeRange};
2969 ///
2970 /// # fn main() -> Result<(), Box<dyn Error>> {
2971 ///
2972 /// let tm_range = PrimitiveValue::from("02-153000.123").to_time_range()?;
2973 ///
2974 /// // null components default to zeros
2975 /// assert_eq!(
2976 /// tm_range.start(),
2977 /// Some(&NaiveTime::from_hms(2, 0, 0))
2978 /// );
2979 ///
2980 /// // unspecified part of second fraction defaults to latest possible
2981 /// assert_eq!(
2982 /// tm_range.end(),
2983 /// Some(&NaiveTime::from_hms_micro(15, 30, 0, 123_999))
2984 /// );
2985 ///
2986 /// let range_from = PrimitiveValue::from("01-").to_time_range()?;
2987 ///
2988 /// assert!(range_from.end().is_none());
2989 ///
2990 /// # Ok(())
2991 /// # }
2992 /// ```
2993 pub fn to_time_range(&self) -> Result<TimeRange, ConvertValueError> {
2994 match self {
2995 PrimitiveValue::Time(t) if !t.is_empty() => t[0]
2996 .range()
2997 .context(ParseTimeRangeSnafu)
2998 .map_err(|err| ConvertValueError {
2999 requested: "TimeRange",
3000 original: self.value_type(),
3001 cause: Some(Box::from(err)),
3002 }),
3003 PrimitiveValue::Str(s) => {
3004 super::range::parse_time_range(s.trim_end_matches(whitespace_or_null).as_bytes())
3005 .context(ParseTimeRangeSnafu)
3006 .map_err(|err| ConvertValueError {
3007 requested: "TimeRange",
3008 original: self.value_type(),
3009 cause: Some(Box::from(err)),
3010 })
3011 }
3012 PrimitiveValue::Strs(s) => super::range::parse_time_range(
3013 s.first()
3014 .map(|s| s.trim_end_matches(whitespace_or_null).as_bytes())
3015 .unwrap_or(&[]),
3016 )
3017 .context(ParseTimeRangeSnafu)
3018 .map_err(|err| ConvertValueError {
3019 requested: "TimeRange",
3020 original: self.value_type(),
3021 cause: Some(Box::from(err)),
3022 }),
3023 PrimitiveValue::U8(bytes) => {
3024 super::range::parse_time_range(trim_last_whitespace(bytes))
3025 .context(ParseTimeRangeSnafu)
3026 .map_err(|err| ConvertValueError {
3027 requested: "TimeRange",
3028 original: self.value_type(),
3029 cause: Some(Box::from(err)),
3030 })
3031 }
3032 _ => Err(ConvertValueError {
3033 requested: "TimeRange",
3034 original: self.value_type(),
3035 cause: None,
3036 }),
3037 }
3038 }
3039
3040 /// Retrieve a single `DateTimeRange` from this value.
3041 ///
3042 /// If the value is already represented as a `DicomDateTime`, it is converted into `DateTimeRange`.
3043 /// If the value is a string or sequence of strings,
3044 /// the first string is decoded to obtain a `DateTimeRange`, potentially failing if the
3045 /// string does not represent a valid `DateTimeRange`.
3046 /// If the value is a sequence of U8 bytes, the bytes are
3047 /// first interpreted as an ASCII character string.
3048 ///
3049 /// # Example
3050 ///
3051 /// ```
3052 /// # use dicom_core::value::{C, PrimitiveValue};
3053 /// use chrono::{DateTime, NaiveDate, NaiveTime, NaiveDateTime, FixedOffset, TimeZone, Local};
3054 /// # use std::error::Error;
3055 /// use dicom_core::value::{DateTimeRange, PreciseDateTime};
3056 ///
3057 /// # fn main() -> Result<(), Box<dyn Error>> {
3058 ///
3059 /// // let's parse a text representation of a date-time range, where the lower bound is a microsecond
3060 /// // precision value with a time-zone (east +05:00) and the upper bound is a minimum precision value
3061 /// // with a time-zone
3062 /// let dt_range = PrimitiveValue::from("19920101153020.123+0500-1993+0300").to_datetime_range()?;
3063 ///
3064 /// // lower bound of range is parsed into a PreciseDateTimeResult::TimeZone variant
3065 /// assert_eq!(
3066 /// dt_range.start(),
3067 /// Some(PreciseDateTime::TimeZone(
3068 /// FixedOffset::east_opt(5*3600).unwrap().ymd_opt(1992, 1, 1).unwrap()
3069 /// .and_hms_micro_opt(15, 30, 20, 123_000).unwrap()
3070 /// )
3071 /// )
3072 /// );
3073 ///
3074 /// // upper bound of range is parsed into a PreciseDateTimeResult::TimeZone variant
3075 /// assert_eq!(
3076 /// dt_range.end(),
3077 /// Some(PreciseDateTime::TimeZone(
3078 /// FixedOffset::east_opt(3*3600).unwrap().ymd_opt(1993, 12, 31).unwrap()
3079 /// .and_hms_micro_opt(23, 59, 59, 999_999).unwrap()
3080 /// )
3081 /// )
3082 /// );
3083 ///
3084 /// let lower = PrimitiveValue::from("2012-").to_datetime_range()?;
3085 ///
3086 /// // range has no upper bound
3087 /// assert!(lower.end().is_none());
3088 ///
3089 /// // One time-zone in a range is missing
3090 /// let dt_range = PrimitiveValue::from("1992+0500-1993").to_datetime_range()?;
3091 ///
3092 /// // It will be replaced with the local clock time-zone offset
3093 /// // This can be customized with [to_datetime_range_custom()]
3094 /// assert_eq!(
3095 /// dt_range,
3096 /// DateTimeRange::TimeZone{
3097 /// start: Some(FixedOffset::east_opt(5*3600).unwrap()
3098 /// .ymd_opt(1992, 1, 1).unwrap()
3099 /// .and_hms_micro_opt(0, 0, 0, 0).unwrap()
3100 /// ),
3101 /// end: Some(Local::now().offset()
3102 /// .ymd_opt(1993, 12, 31).unwrap()
3103 /// .and_hms_micro_opt(23, 59, 59, 999_999).unwrap()
3104 /// )
3105 /// }
3106 /// );
3107 ///
3108 /// # Ok(())
3109 /// # }
3110 /// ```
3111 pub fn to_datetime_range(&self) -> Result<DateTimeRange, ConvertValueError> {
3112 match self {
3113 PrimitiveValue::DateTime(dt) if !dt.is_empty() => dt[0]
3114 .range()
3115 .context(ParseDateTimeRangeSnafu)
3116 .map_err(|err| ConvertValueError {
3117 requested: "DateTimeRange",
3118 original: self.value_type(),
3119 cause: Some(Box::from(err)),
3120 }),
3121 PrimitiveValue::Str(s) => super::range::parse_datetime_range(
3122 s.trim_end_matches(whitespace_or_null).as_bytes(),
3123 )
3124 .context(ParseDateTimeRangeSnafu)
3125 .map_err(|err| ConvertValueError {
3126 requested: "DateTimeRange",
3127 original: self.value_type(),
3128 cause: Some(Box::from(err)),
3129 }),
3130 PrimitiveValue::Strs(s) => super::range::parse_datetime_range(
3131 s.first()
3132 .map(|s| s.trim_end_matches(whitespace_or_null).as_bytes())
3133 .unwrap_or(&[]),
3134 )
3135 .context(ParseDateTimeRangeSnafu)
3136 .map_err(|err| ConvertValueError {
3137 requested: "DateTimeRange",
3138 original: self.value_type(),
3139 cause: Some(Box::from(err)),
3140 }),
3141 PrimitiveValue::U8(bytes) => {
3142 super::range::parse_datetime_range(trim_last_whitespace(bytes))
3143 .context(ParseDateTimeRangeSnafu)
3144 .map_err(|err| ConvertValueError {
3145 requested: "DateTimeRange",
3146 original: self.value_type(),
3147 cause: Some(Box::from(err)),
3148 })
3149 }
3150 _ => Err(ConvertValueError {
3151 requested: "DateTimeRange",
3152 original: self.value_type(),
3153 cause: None,
3154 }),
3155 }
3156 }
3157
3158 /// Retrieve a single `DateTimeRange` from this value.
3159 ///
3160 /// Use a custom ambiguous date-time range parser.
3161 ///
3162 /// For full description see [PrimitiveValue::to_datetime_range] and [AmbiguousDtRangeParser].
3163 /// # Example
3164 ///
3165 /// ```
3166 /// # use dicom_core::value::{C, PrimitiveValue};
3167 /// # use std::error::Error;
3168 /// use dicom_core::value::range::{AmbiguousDtRangeParser, ToKnownTimeZone, IgnoreTimeZone, FailOnAmbiguousRange, DateTimeRange};
3169 /// use chrono::{NaiveDate, NaiveTime, NaiveDateTime};
3170 /// # fn main() -> Result<(), Box<dyn Error>> {
3171 ///
3172 /// // The upper bound time-zone is missing
3173 /// // the default behavior in this case is to use the local clock time-zone.
3174 /// // But we want to use the known (parsed) time-zone from the lower bound instead.
3175 /// let dt_range = PrimitiveValue::from("1992+0500-1993")
3176 /// .to_datetime_range_custom::<ToKnownTimeZone>()?;
3177 ///
3178 /// // values are in the same time-zone
3179 /// assert_eq!(
3180 /// dt_range.start().unwrap()
3181 /// .as_datetime().unwrap()
3182 /// .offset(),
3183 /// dt_range.end().unwrap()
3184 /// .as_datetime().unwrap()
3185 /// .offset()
3186 /// );
3187 ///
3188 /// // ignore parsed time-zone, retrieve a time-zone naive range
3189 /// let naive_range = PrimitiveValue::from("1992+0599-1993")
3190 /// .to_datetime_range_custom::<IgnoreTimeZone>()?;
3191 ///
3192 /// assert_eq!(
3193 /// naive_range,
3194 /// DateTimeRange::from_start_to_end(
3195 /// NaiveDateTime::new(
3196 /// NaiveDate::from_ymd_opt(1992, 1, 1).unwrap(),
3197 /// NaiveTime::from_hms_micro_opt(0, 0, 0, 0).unwrap()
3198 /// ),
3199 /// NaiveDateTime::new(
3200 /// NaiveDate::from_ymd_opt(1993, 12, 31).unwrap(),
3201 /// NaiveTime::from_hms_micro_opt(23, 59, 59, 999_999).unwrap()
3202 /// )
3203 /// ).unwrap()
3204 /// );
3205 ///
3206 /// // always fail upon parsing an ambiguous DT range
3207 /// assert!(
3208 /// PrimitiveValue::from("1992+0599-1993")
3209 /// .to_datetime_range_custom::<FailOnAmbiguousRange>().is_err()
3210 /// );
3211 ///
3212 ///
3213 ///
3214 /// # Ok(())
3215 /// # }
3216 /// ```
3217 pub fn to_datetime_range_custom<T: AmbiguousDtRangeParser>(
3218 &self,
3219 ) -> Result<DateTimeRange, ConvertValueError> {
3220 match self {
3221 PrimitiveValue::DateTime(dt) if !dt.is_empty() => dt[0]
3222 .range()
3223 .context(ParseDateTimeRangeSnafu)
3224 .map_err(|err| ConvertValueError {
3225 requested: "DateTimeRange",
3226 original: self.value_type(),
3227 cause: Some(Box::from(err)),
3228 }),
3229 PrimitiveValue::Str(s) => super::range::parse_datetime_range_custom::<T>(
3230 s.trim_end_matches(whitespace_or_null).as_bytes(),
3231 )
3232 .context(ParseDateTimeRangeSnafu)
3233 .map_err(|err| ConvertValueError {
3234 requested: "DateTimeRange",
3235 original: self.value_type(),
3236 cause: Some(Box::from(err)),
3237 }),
3238 PrimitiveValue::Strs(s) => super::range::parse_datetime_range_custom::<T>(
3239 s.first()
3240 .map(|s| s.trim_end_matches(whitespace_or_null).as_bytes())
3241 .unwrap_or(&[]),
3242 )
3243 .context(ParseDateTimeRangeSnafu)
3244 .map_err(|err| ConvertValueError {
3245 requested: "DateTimeRange",
3246 original: self.value_type(),
3247 cause: Some(Box::from(err)),
3248 }),
3249 PrimitiveValue::U8(bytes) => {
3250 super::range::parse_datetime_range_custom::<T>(trim_last_whitespace(bytes))
3251 .context(ParseDateTimeRangeSnafu)
3252 .map_err(|err| ConvertValueError {
3253 requested: "DateTimeRange",
3254 original: self.value_type(),
3255 cause: Some(Box::from(err)),
3256 })
3257 }
3258 _ => Err(ConvertValueError {
3259 requested: "DateTimeRange",
3260 original: self.value_type(),
3261 cause: None,
3262 }),
3263 }
3264 }
3265
3266 /// Retrieve a single [`PersonName`][1] from this value.
3267 ///
3268 /// If the value is a string or sequence of strings,
3269 /// the first string is split to obtain a `PersonName`.
3270 ///
3271 /// [1]: super::person_name::PersonName
3272 ///
3273 /// # Example
3274 ///
3275 /// ```
3276 /// # use dicom_core::value::{C, PrimitiveValue};
3277 /// # use std::error::Error;
3278 /// use dicom_core::value::PersonName;
3279 /// # fn main() -> Result<(), Box<dyn Error>> {
3280 ///
3281 /// let value = PrimitiveValue::from("Tooms^Victor^Eugene");
3282 /// // PersonName contains borrowed values
3283 /// let pn = value.to_person_name()?;
3284 ///
3285 /// assert_eq!(pn.given(), Some("Victor"));
3286 /// assert_eq!(pn.middle(), Some("Eugene"));
3287 /// assert!(pn.prefix().is_none());
3288 ///
3289 /// let value2 = PrimitiveValue::from(pn);
3290 ///
3291 /// assert_eq!(value, value2);
3292 ///
3293 /// # Ok(())
3294 /// # }
3295 /// ```
3296 pub fn to_person_name(&self) -> Result<PersonName<'_>, ConvertValueError> {
3297 match self {
3298 PrimitiveValue::Str(s) => Ok(PersonName::from_text(s)),
3299 PrimitiveValue::Strs(s) => s.first().map_or_else(
3300 || {
3301 Err(ConvertValueError {
3302 requested: "PersonName",
3303 original: self.value_type(),
3304 cause: None,
3305 })
3306 },
3307 |s| Ok(PersonName::from_text(s)),
3308 ),
3309 _ => Err(ConvertValueError {
3310 requested: "PersonName",
3311 original: self.value_type(),
3312 cause: None,
3313 }),
3314 }
3315 }
3316}
3317
3318/// Macro for implementing getters to single and multi-values of each variant.
3319///
3320/// Should be placed inside `PrimitiveValue`'s impl block.
3321macro_rules! impl_primitive_getters {
3322 ($name_single: ident, $name_multi: ident, $variant: ident, $ret: ty) => {
3323 /// Get a single value of the requested type.
3324 /// If it contains multiple values,
3325 /// only the first one is returned.
3326 /// An error is returned if the variant is not compatible.
3327 pub fn $name_single(&self) -> Result<$ret, CastValueError> {
3328 match self {
3329 PrimitiveValue::$variant(c) if c.is_empty() => Err(CastValueError {
3330 requested: stringify!($name_single),
3331 got: ValueType::Empty,
3332 }),
3333 PrimitiveValue::$variant(c) => Ok(c[0]),
3334 value => Err(CastValueError {
3335 requested: stringify!($name_single),
3336 got: value.value_type(),
3337 }),
3338 }
3339 }
3340
3341 /// Get a sequence of values of the requested type without copying.
3342 /// An error is returned if the variant is not compatible.
3343 pub fn $name_multi(&self) -> Result<&[$ret], CastValueError> {
3344 match self {
3345 PrimitiveValue::$variant(c) => Ok(&c),
3346 value => Err(CastValueError {
3347 requested: stringify!($name_multi),
3348 got: value.value_type(),
3349 }),
3350 }
3351 }
3352 };
3353}
3354
3355/// Per variant, strongly checked getters to DICOM values.
3356///
3357/// Conversions from one representation to another do not take place
3358/// when using these methods.
3359impl PrimitiveValue {
3360 /// Get a single string value.
3361 ///
3362 /// If it contains multiple strings,
3363 /// only the first one is returned.
3364 ///
3365 /// An error is returned if the variant is not compatible.
3366 ///
3367 /// To enable conversions of other variants to a textual representation,
3368 /// see [`to_str()`] instead.
3369 ///
3370 /// [`to_str()`]: #method.to_str
3371 pub fn string(&self) -> Result<&str, CastValueError> {
3372 use self::PrimitiveValue::*;
3373 match self {
3374 Strs(c) if c.is_empty() => Err(CastValueError {
3375 requested: "Str",
3376 got: ValueType::Empty,
3377 }),
3378 Strs(c) if !c.is_empty() => Ok(&c[0]),
3379 Str(s) => Ok(s),
3380 value => Err(CastValueError {
3381 requested: "Str",
3382 got: value.value_type(),
3383 }),
3384 }
3385 }
3386
3387 /// Get the inner sequence of string values
3388 /// if the variant is either `Str` or `Strs`.
3389 ///
3390 /// An error is returned if the variant is not compatible.
3391 ///
3392 /// To enable conversions of other variants to a textual representation,
3393 /// see [`to_str()`] instead.
3394 ///
3395 /// [`to_str()`]: #method.to_str
3396 pub fn strings(&self) -> Result<&[String], CastValueError> {
3397 use self::PrimitiveValue::*;
3398 match self {
3399 Strs(c) => Ok(c),
3400 Str(s) => Ok(std::slice::from_ref(s)),
3401 value => Err(CastValueError {
3402 requested: "strings",
3403 got: value.value_type(),
3404 }),
3405 }
3406 }
3407
3408 impl_primitive_getters!(tag, tags, Tags, Tag);
3409 impl_primitive_getters!(date, dates, Date, DicomDate);
3410 impl_primitive_getters!(time, times, Time, DicomTime);
3411 impl_primitive_getters!(datetime, datetimes, DateTime, DicomDateTime);
3412 impl_primitive_getters!(uint8, uint8_slice, U8, u8);
3413 impl_primitive_getters!(uint16, uint16_slice, U16, u16);
3414 impl_primitive_getters!(int16, int16_slice, I16, i16);
3415 impl_primitive_getters!(uint32, uint32_slice, U32, u32);
3416 impl_primitive_getters!(int32, int32_slice, I32, i32);
3417 impl_primitive_getters!(int64, int64_slice, I64, i64);
3418 impl_primitive_getters!(uint64, uint64_slice, U64, u64);
3419 impl_primitive_getters!(float32, float32_slice, F32, f32);
3420 impl_primitive_getters!(float64, float64_slice, F64, f64);
3421
3422 /// Extend a textual value by appending
3423 /// more strings to an existing text or empty value.
3424 ///
3425 /// An error is returned if the current value is not textual.
3426 ///
3427 /// # Example
3428 ///
3429 /// ```
3430 /// use dicom_core::dicom_value;
3431 /// # use dicom_core::value::ModifyValueError;
3432 ///
3433 /// # fn main() -> Result<(), ModifyValueError> {
3434 /// let mut value = dicom_value!(Strs, ["Hello"]);
3435 /// value.extend_str(["DICOM"])?;
3436 /// assert_eq!(value.to_string(), "Hello\\DICOM");
3437 /// # Ok(())
3438 /// # }
3439 /// ```
3440 pub fn extend_str<T>(
3441 &mut self,
3442 strings: impl IntoIterator<Item = T>,
3443 ) -> Result<(), ModifyValueError>
3444 where
3445 T: Into<String>,
3446 {
3447 match self {
3448 PrimitiveValue::Empty => {
3449 *self = PrimitiveValue::Strs(strings.into_iter().map(T::into).collect());
3450 Ok(())
3451 }
3452 PrimitiveValue::Strs(elements) => {
3453 elements.extend(strings.into_iter().map(T::into));
3454 Ok(())
3455 }
3456 PrimitiveValue::Str(s) => {
3457 // for lack of better ways to move the string out from the mutable borrow,
3458 // we create a copy for now
3459 let s = s.clone();
3460 *self = PrimitiveValue::Strs(
3461 std::iter::once(s)
3462 .chain(strings.into_iter().map(T::into))
3463 .collect(),
3464 );
3465 Ok(())
3466 }
3467 PrimitiveValue::Tags(_)
3468 | PrimitiveValue::U8(_)
3469 | PrimitiveValue::I16(_)
3470 | PrimitiveValue::U16(_)
3471 | PrimitiveValue::I32(_)
3472 | PrimitiveValue::U32(_)
3473 | PrimitiveValue::I64(_)
3474 | PrimitiveValue::U64(_)
3475 | PrimitiveValue::F32(_)
3476 | PrimitiveValue::F64(_)
3477 | PrimitiveValue::Date(_)
3478 | PrimitiveValue::DateTime(_)
3479 | PrimitiveValue::Time(_) => IncompatibleStringTypeSnafu {
3480 original: self.value_type(),
3481 }
3482 .fail(),
3483 }
3484 }
3485
3486 /// Extend a value of numbers by appending
3487 /// 16-bit unsigned integers to an existing value.
3488 ///
3489 /// The value may be empty
3490 /// or already contain numeric or textual values.
3491 ///
3492 /// If the current value is textual,
3493 /// the numbers provided are converted to text.
3494 /// For the case of numeric values,
3495 /// the given numbers are _converted to the current number type
3496 /// through casting_,
3497 /// meaning that loss of precision may occur.
3498 /// If this is undesirable,
3499 /// read the current value and replace it manually.
3500 ///
3501 /// An error is returned
3502 /// if the current value is not compatible with the insertion of integers,
3503 /// such as `Tag` or `Date`.
3504 ///
3505 /// # Example
3506 ///
3507 /// ```
3508 /// use dicom_core::dicom_value;
3509 ///
3510 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
3511 /// let mut value = dicom_value!(U16, [1, 2]);
3512 /// value.extend_u16([5])?;
3513 /// assert_eq!(value.to_multi_int::<u16>()?, vec![1, 2, 5]);
3514 ///
3515 /// let mut value = dicom_value!(Strs, ["City"]);
3516 /// value.extend_u16([17])?;
3517 /// assert_eq!(value.to_string(), "City\\17");
3518 /// # Ok(())
3519 /// # }
3520 /// ```
3521 pub fn extend_u16(
3522 &mut self,
3523 numbers: impl IntoIterator<Item = u16>,
3524 ) -> Result<(), ModifyValueError> {
3525 match self {
3526 PrimitiveValue::Empty => {
3527 *self = PrimitiveValue::U16(numbers.into_iter().collect());
3528 Ok(())
3529 }
3530 PrimitiveValue::Strs(elements) => {
3531 elements.extend(numbers.into_iter().map(|n| n.to_string()));
3532 Ok(())
3533 }
3534 PrimitiveValue::Str(s) => {
3535 // for lack of better ways to move the string out from the mutable borrow,
3536 // we create a copy for now
3537 let s = s.clone();
3538 *self = PrimitiveValue::Strs(
3539 std::iter::once(s)
3540 .chain(numbers.into_iter().map(|n| n.to_string()))
3541 .collect(),
3542 );
3543 Ok(())
3544 }
3545 PrimitiveValue::U16(elements) => {
3546 elements.extend(numbers);
3547 Ok(())
3548 }
3549 PrimitiveValue::U8(elements) => {
3550 elements.extend(numbers.into_iter().map(|n| n as u8));
3551 Ok(())
3552 }
3553 PrimitiveValue::I16(elements) => {
3554 elements.extend(numbers.into_iter().map(|n| n as i16));
3555 Ok(())
3556 }
3557 PrimitiveValue::U32(elements) => {
3558 elements.extend(numbers.into_iter().map(|n| n as u32));
3559 Ok(())
3560 }
3561 PrimitiveValue::I32(elements) => {
3562 elements.extend(numbers.into_iter().map(|n| n as i32));
3563 Ok(())
3564 }
3565 PrimitiveValue::I64(elements) => {
3566 elements.extend(numbers.into_iter().map(|n| n as i64));
3567 Ok(())
3568 }
3569 PrimitiveValue::U64(elements) => {
3570 elements.extend(numbers.into_iter().map(|n| n as u64));
3571 Ok(())
3572 }
3573 PrimitiveValue::F32(elements) => {
3574 elements.extend(numbers.into_iter().map(|n| n as f32));
3575 Ok(())
3576 }
3577 PrimitiveValue::F64(elements) => {
3578 elements.extend(numbers.into_iter().map(|n| n as f64));
3579 Ok(())
3580 }
3581 PrimitiveValue::Tags(_)
3582 | PrimitiveValue::Date(_)
3583 | PrimitiveValue::DateTime(_)
3584 | PrimitiveValue::Time(_) => IncompatibleNumberTypeSnafu {
3585 original: self.value_type(),
3586 }
3587 .fail(),
3588 }
3589 }
3590
3591 /// Extend a value of numbers by appending
3592 /// 16-bit signed integers to an existing value.
3593 ///
3594 /// The value may be empty
3595 /// or already contain numeric or textual values.
3596 ///
3597 /// If the current value is textual,
3598 /// the numbers provided are converted to text.
3599 /// For the case of numeric values,
3600 /// the given numbers are _converted to the current number type
3601 /// through casting_,
3602 /// meaning that loss of precision may occur.
3603 /// If this is undesirable,
3604 /// read the current value and replace it manually.
3605 ///
3606 /// An error is returned
3607 /// if the current value is not compatible with the insertion of integers,
3608 /// such as `Tag` or `Date`.
3609 ///
3610 /// # Example
3611 ///
3612 /// ```
3613 /// use dicom_core::dicom_value;
3614 ///
3615 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
3616 /// let mut value = dicom_value!(I16, [1, 2]);
3617 /// value.extend_i16([-5])?;
3618 /// assert_eq!(value.to_multi_int::<i16>()?, vec![1, 2, -5]);
3619 ///
3620 /// let mut value = dicom_value!(Strs, ["City"]);
3621 /// value.extend_i16([17])?;
3622 /// assert_eq!(value.to_string(), "City\\17");
3623 /// # Ok(())
3624 /// # }
3625 /// ```
3626 pub fn extend_i16(
3627 &mut self,
3628 numbers: impl IntoIterator<Item = i16>,
3629 ) -> Result<(), ModifyValueError> {
3630 match self {
3631 PrimitiveValue::Empty => {
3632 *self = PrimitiveValue::I16(numbers.into_iter().collect());
3633 Ok(())
3634 }
3635 PrimitiveValue::Strs(elements) => {
3636 elements.extend(numbers.into_iter().map(|n| n.to_string()));
3637 Ok(())
3638 }
3639 PrimitiveValue::Str(s) => {
3640 // for lack of better ways to move the string out from the mutable borrow,
3641 // we create a copy for now
3642 let s = s.clone();
3643 *self = PrimitiveValue::Strs(
3644 std::iter::once(s)
3645 .chain(numbers.into_iter().map(|n| n.to_string()))
3646 .collect(),
3647 );
3648 Ok(())
3649 }
3650 PrimitiveValue::I16(elements) => {
3651 elements.extend(numbers);
3652 Ok(())
3653 }
3654 PrimitiveValue::U8(elements) => {
3655 elements.extend(numbers.into_iter().map(|n| n as u8));
3656 Ok(())
3657 }
3658 PrimitiveValue::U16(elements) => {
3659 elements.extend(numbers.into_iter().map(|n| n as u16));
3660 Ok(())
3661 }
3662 PrimitiveValue::U32(elements) => {
3663 elements.extend(numbers.into_iter().map(|n| n as u32));
3664 Ok(())
3665 }
3666 PrimitiveValue::I32(elements) => {
3667 elements.extend(numbers.into_iter().map(|n| n as i32));
3668 Ok(())
3669 }
3670 PrimitiveValue::I64(elements) => {
3671 elements.extend(numbers.into_iter().map(|n| n as i64));
3672 Ok(())
3673 }
3674 PrimitiveValue::U64(elements) => {
3675 elements.extend(numbers.into_iter().map(|n| n as u64));
3676 Ok(())
3677 }
3678 PrimitiveValue::F32(elements) => {
3679 elements.extend(numbers.into_iter().map(|n| n as f32));
3680 Ok(())
3681 }
3682 PrimitiveValue::F64(elements) => {
3683 elements.extend(numbers.into_iter().map(|n| n as f64));
3684 Ok(())
3685 }
3686 PrimitiveValue::Tags(_)
3687 | PrimitiveValue::Date(_)
3688 | PrimitiveValue::DateTime(_)
3689 | PrimitiveValue::Time(_) => IncompatibleNumberTypeSnafu {
3690 original: self.value_type(),
3691 }
3692 .fail(),
3693 }
3694 }
3695
3696 /// Extend a value of numbers by appending
3697 /// 32-bit signed integers to an existing value.
3698 ///
3699 /// The value may be empty
3700 /// or already contain numeric or textual values.
3701 ///
3702 /// If the current value is textual,
3703 /// the numbers provided are converted to text.
3704 /// For the case of numeric values,
3705 /// the given numbers are _converted to the current number type
3706 /// through casting_,
3707 /// meaning that loss of precision may occur.
3708 /// If this is undesirable,
3709 /// read the current value and replace it manually.
3710 ///
3711 /// An error is returned
3712 /// if the current value is not compatible with the insertion of integers,
3713 /// such as `Tag` or `Date`.
3714 ///
3715 /// # Example
3716 ///
3717 /// ```
3718 /// use dicom_core::dicom_value;
3719 ///
3720 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
3721 /// let mut value = dicom_value!(I32, [1, 2]);
3722 /// value.extend_i32([5])?;
3723 /// assert_eq!(value.to_multi_int::<i32>()?, vec![1, 2, 5]);
3724 ///
3725 /// let mut value = dicom_value!(Strs, ["City"]);
3726 /// value.extend_i32([17])?;
3727 /// assert_eq!(value.to_string(), "City\\17");
3728 /// # Ok(())
3729 /// # }
3730 /// ```
3731 pub fn extend_i32(
3732 &mut self,
3733 numbers: impl IntoIterator<Item = i32>,
3734 ) -> Result<(), ModifyValueError> {
3735 match self {
3736 PrimitiveValue::Empty => {
3737 *self = PrimitiveValue::I32(numbers.into_iter().collect());
3738 Ok(())
3739 }
3740 PrimitiveValue::Strs(elements) => {
3741 elements.extend(numbers.into_iter().map(|n| n.to_string()));
3742 Ok(())
3743 }
3744 PrimitiveValue::Str(s) => {
3745 // for lack of better ways to move the string out from the mutable borrow,
3746 // we create a copy for now
3747 let s = s.clone();
3748 *self = PrimitiveValue::Strs(
3749 std::iter::once(s)
3750 .chain(numbers.into_iter().map(|n| n.to_string()))
3751 .collect(),
3752 );
3753 Ok(())
3754 }
3755 PrimitiveValue::I32(elements) => {
3756 elements.extend(numbers);
3757 Ok(())
3758 }
3759 PrimitiveValue::U8(elements) => {
3760 elements.extend(numbers.into_iter().map(|n| n as u8));
3761 Ok(())
3762 }
3763 PrimitiveValue::I16(elements) => {
3764 elements.extend(numbers.into_iter().map(|n| n as i16));
3765 Ok(())
3766 }
3767 PrimitiveValue::U16(elements) => {
3768 elements.extend(numbers.into_iter().map(|n| n as u16));
3769 Ok(())
3770 }
3771 PrimitiveValue::U32(elements) => {
3772 elements.extend(numbers.into_iter().map(|n| n as u32));
3773 Ok(())
3774 }
3775 PrimitiveValue::I64(elements) => {
3776 elements.extend(numbers.into_iter().map(|n| n as i64));
3777 Ok(())
3778 }
3779 PrimitiveValue::U64(elements) => {
3780 elements.extend(numbers.into_iter().map(|n| n as u64));
3781 Ok(())
3782 }
3783 PrimitiveValue::F32(elements) => {
3784 elements.extend(numbers.into_iter().map(|n| n as f32));
3785 Ok(())
3786 }
3787 PrimitiveValue::F64(elements) => {
3788 elements.extend(numbers.into_iter().map(|n| n as f64));
3789 Ok(())
3790 }
3791 PrimitiveValue::Tags(_)
3792 | PrimitiveValue::Date(_)
3793 | PrimitiveValue::DateTime(_)
3794 | PrimitiveValue::Time(_) => IncompatibleNumberTypeSnafu {
3795 original: self.value_type(),
3796 }
3797 .fail(),
3798 }
3799 }
3800
3801 /// Extend a value of numbers by appending
3802 /// 32-bit unsigned integers to an existing value.
3803 ///
3804 /// The value may be empty
3805 /// or already contain numeric or textual values.
3806 ///
3807 /// If the current value is textual,
3808 /// the numbers provided are converted to text.
3809 /// For the case of numeric values,
3810 /// the given numbers are _converted to the current number type
3811 /// through casting_,
3812 /// meaning that loss of precision may occur.
3813 /// If this is undesirable,
3814 /// read the current value and replace it manually.
3815 ///
3816 /// An error is returned
3817 /// if the current value is not compatible with the insertion of integers,
3818 /// such as `Tag` or `Date`.
3819 ///
3820 /// # Example
3821 ///
3822 /// ```
3823 /// use dicom_core::dicom_value;
3824 ///
3825 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
3826 /// let mut value = dicom_value!(U32, [1, 2]);
3827 /// value.extend_u32([5])?;
3828 /// assert_eq!(value.to_multi_int::<u32>()?, vec![1, 2, 5]);
3829 ///
3830 /// let mut value = dicom_value!(Strs, ["City"]);
3831 /// value.extend_u32([17])?;
3832 /// assert_eq!(value.to_string(), "City\\17");
3833 /// # Ok(())
3834 /// # }
3835 /// ```
3836 pub fn extend_u32(
3837 &mut self,
3838 numbers: impl IntoIterator<Item = u32>,
3839 ) -> Result<(), ModifyValueError> {
3840 match self {
3841 PrimitiveValue::Empty => {
3842 *self = PrimitiveValue::U32(numbers.into_iter().collect());
3843 Ok(())
3844 }
3845 PrimitiveValue::Strs(elements) => {
3846 elements.extend(numbers.into_iter().map(|n| n.to_string()));
3847 Ok(())
3848 }
3849 PrimitiveValue::Str(s) => {
3850 // for lack of better ways to move the string out from the mutable borrow,
3851 // we create a copy for now
3852 let s = s.clone();
3853 *self = PrimitiveValue::Strs(
3854 std::iter::once(s)
3855 .chain(numbers.into_iter().map(|n| n.to_string()))
3856 .collect(),
3857 );
3858 Ok(())
3859 }
3860 PrimitiveValue::U32(elements) => {
3861 elements.extend(numbers);
3862 Ok(())
3863 }
3864 PrimitiveValue::U8(elements) => {
3865 elements.extend(numbers.into_iter().map(|n| n as u8));
3866 Ok(())
3867 }
3868 PrimitiveValue::I16(elements) => {
3869 elements.extend(numbers.into_iter().map(|n| n as i16));
3870 Ok(())
3871 }
3872 PrimitiveValue::U16(elements) => {
3873 elements.extend(numbers.into_iter().map(|n| n as u16));
3874 Ok(())
3875 }
3876 PrimitiveValue::I32(elements) => {
3877 elements.extend(numbers.into_iter().map(|n| n as i32));
3878 Ok(())
3879 }
3880 PrimitiveValue::I64(elements) => {
3881 elements.extend(numbers.into_iter().map(|n| n as i64));
3882 Ok(())
3883 }
3884 PrimitiveValue::U64(elements) => {
3885 elements.extend(numbers.into_iter().map(|n| n as u64));
3886 Ok(())
3887 }
3888 PrimitiveValue::F32(elements) => {
3889 elements.extend(numbers.into_iter().map(|n| n as f32));
3890 Ok(())
3891 }
3892 PrimitiveValue::F64(elements) => {
3893 elements.extend(numbers.into_iter().map(|n| n as f64));
3894 Ok(())
3895 }
3896 PrimitiveValue::Tags(_)
3897 | PrimitiveValue::Date(_)
3898 | PrimitiveValue::DateTime(_)
3899 | PrimitiveValue::Time(_) => IncompatibleNumberTypeSnafu {
3900 original: self.value_type(),
3901 }
3902 .fail(),
3903 }
3904 }
3905
3906 /// Extend a value of numbers by appending
3907 /// 32-bit floating point numbers to an existing value.
3908 ///
3909 /// The value may be empty
3910 /// or already contain numeric or textual values.
3911 ///
3912 /// If the current value is textual,
3913 /// the numbers provided are converted to text.
3914 /// For the case of numeric values,
3915 /// the given numbers are _converted to the current number type
3916 /// through casting_,
3917 /// meaning that loss of precision may occur.
3918 /// If this is undesirable,
3919 /// read the current value and replace it manually.
3920 ///
3921 /// An error is returned
3922 /// if the current value is not compatible with the insertion of integers,
3923 /// such as `Tag` or `Date`.
3924 ///
3925 /// # Example
3926 ///
3927 /// ```
3928 /// use dicom_core::dicom_value;
3929 ///
3930 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
3931 /// let mut value = dicom_value!(F32, [1., 2.]);
3932 /// value.extend_f32([5.])?;
3933 /// assert_eq!(value.to_multi_float32()?, vec![1., 2., 5.]);
3934 ///
3935 /// let mut value = dicom_value!(Strs, ["1.25"]);
3936 /// value.extend_f32([0.5])?;
3937 /// assert_eq!(value.to_string(), "1.25\\0.5");
3938 /// # Ok(())
3939 /// # }
3940 /// ```
3941 pub fn extend_f32(
3942 &mut self,
3943 numbers: impl IntoIterator<Item = f32>,
3944 ) -> Result<(), ModifyValueError> {
3945 match self {
3946 PrimitiveValue::Empty => {
3947 *self = PrimitiveValue::F32(numbers.into_iter().collect());
3948 Ok(())
3949 }
3950 PrimitiveValue::Strs(elements) => {
3951 elements.extend(numbers.into_iter().map(|n| n.to_string()));
3952 Ok(())
3953 }
3954 PrimitiveValue::Str(s) => {
3955 // for lack of better ways to move the string out from the mutable borrow,
3956 // we create a copy for now
3957 let s = s.clone();
3958 *self = PrimitiveValue::Strs(
3959 std::iter::once(s)
3960 .chain(numbers.into_iter().map(|n| n.to_string()))
3961 .collect(),
3962 );
3963 Ok(())
3964 }
3965 PrimitiveValue::F32(elements) => {
3966 elements.extend(numbers);
3967 Ok(())
3968 }
3969 PrimitiveValue::U8(elements) => {
3970 elements.extend(numbers.into_iter().map(|n| n as u8));
3971 Ok(())
3972 }
3973 PrimitiveValue::I16(elements) => {
3974 elements.extend(numbers.into_iter().map(|n| n as i16));
3975 Ok(())
3976 }
3977 PrimitiveValue::U16(elements) => {
3978 elements.extend(numbers.into_iter().map(|n| n as u16));
3979 Ok(())
3980 }
3981 PrimitiveValue::I32(elements) => {
3982 elements.extend(numbers.into_iter().map(|n| n as i32));
3983 Ok(())
3984 }
3985 PrimitiveValue::I64(elements) => {
3986 elements.extend(numbers.into_iter().map(|n| n as i64));
3987 Ok(())
3988 }
3989 PrimitiveValue::U32(elements) => {
3990 elements.extend(numbers.into_iter().map(|n| n as u32));
3991 Ok(())
3992 }
3993 PrimitiveValue::U64(elements) => {
3994 elements.extend(numbers.into_iter().map(|n| n as u64));
3995 Ok(())
3996 }
3997 PrimitiveValue::F64(elements) => {
3998 elements.extend(numbers.into_iter().map(|n| n as f64));
3999 Ok(())
4000 }
4001 PrimitiveValue::Tags(_)
4002 | PrimitiveValue::Date(_)
4003 | PrimitiveValue::DateTime(_)
4004 | PrimitiveValue::Time(_) => IncompatibleNumberTypeSnafu {
4005 original: self.value_type(),
4006 }
4007 .fail(),
4008 }
4009 }
4010
4011 /// Extend a value of numbers by appending
4012 /// 64-bit floating point numbers to an existing value.
4013 ///
4014 /// The value may be empty
4015 /// or already contain numeric or textual values.
4016 ///
4017 /// If the current value is textual,
4018 /// the numbers provided are converted to text.
4019 /// For the case of numeric values,
4020 /// the given numbers are _converted to the current number type
4021 /// through casting_,
4022 /// meaning that loss of precision may occur.
4023 /// If this is undesirable,
4024 /// read the current value and replace it manually.
4025 ///
4026 /// An error is returned
4027 /// if the current value is not compatible with the insertion of integers,
4028 /// such as `Tag` or `Date`.
4029 ///
4030 /// # Example
4031 ///
4032 /// ```
4033 /// use dicom_core::dicom_value;
4034 ///
4035 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
4036 /// let mut value = dicom_value!(F64, [1., 2.]);
4037 /// value.extend_f64([5.])?;
4038 /// assert_eq!(value.to_multi_float64()?, vec![1., 2., 5.]);
4039 ///
4040 /// let mut value = dicom_value!(Strs, ["1.25"]);
4041 /// value.extend_f64([0.5])?;
4042 /// assert_eq!(value.to_string(), "1.25\\0.5");
4043 /// # Ok(())
4044 /// # }
4045 /// ```
4046 pub fn extend_f64(
4047 &mut self,
4048 numbers: impl IntoIterator<Item = f64>,
4049 ) -> Result<(), ModifyValueError> {
4050 match self {
4051 PrimitiveValue::Empty => {
4052 *self = PrimitiveValue::F64(numbers.into_iter().collect());
4053 Ok(())
4054 }
4055 PrimitiveValue::Strs(elements) => {
4056 elements.extend(numbers.into_iter().map(|n| n.to_string()));
4057 Ok(())
4058 }
4059 PrimitiveValue::Str(s) => {
4060 // for lack of better ways to move the string out from the mutable borrow,
4061 // we create a copy for now
4062 let s = s.clone();
4063 *self = PrimitiveValue::Strs(
4064 std::iter::once(s)
4065 .chain(numbers.into_iter().map(|n| n.to_string()))
4066 .collect(),
4067 );
4068 Ok(())
4069 }
4070 PrimitiveValue::F64(elements) => {
4071 elements.extend(numbers);
4072 Ok(())
4073 }
4074 PrimitiveValue::U8(elements) => {
4075 elements.extend(numbers.into_iter().map(|n| n as u8));
4076 Ok(())
4077 }
4078 PrimitiveValue::I16(elements) => {
4079 elements.extend(numbers.into_iter().map(|n| n as i16));
4080 Ok(())
4081 }
4082 PrimitiveValue::U16(elements) => {
4083 elements.extend(numbers.into_iter().map(|n| n as u16));
4084 Ok(())
4085 }
4086 PrimitiveValue::I32(elements) => {
4087 elements.extend(numbers.into_iter().map(|n| n as i32));
4088 Ok(())
4089 }
4090 PrimitiveValue::I64(elements) => {
4091 elements.extend(numbers.into_iter().map(|n| n as i64));
4092 Ok(())
4093 }
4094 PrimitiveValue::U32(elements) => {
4095 elements.extend(numbers.into_iter().map(|n| n as u32));
4096 Ok(())
4097 }
4098 PrimitiveValue::U64(elements) => {
4099 elements.extend(numbers.into_iter().map(|n| n as u64));
4100 Ok(())
4101 }
4102 PrimitiveValue::F32(elements) => {
4103 elements.extend(numbers.into_iter().map(|n| n as f32));
4104 Ok(())
4105 }
4106 PrimitiveValue::Tags(_)
4107 | PrimitiveValue::Date(_)
4108 | PrimitiveValue::DateTime(_)
4109 | PrimitiveValue::Time(_) => Err(IncompatibleNumberTypeSnafu {
4110 original: self.value_type(),
4111 }
4112 .build()),
4113 }
4114 }
4115
4116 /// Shorten this value by removing trailing elements
4117 /// to fit the given limit.
4118 ///
4119 /// Elements are counted by the number of individual value items
4120 /// (note that bytes in a [`PrimitiveValue::U8`]
4121 /// are treated as individual items).
4122 ///
4123 /// Nothing is done if the value's cardinality
4124 /// is already lower than or equal to the limit.
4125 ///
4126 /// # Example
4127 ///
4128 /// ```
4129 /// # use dicom_core::dicom_value;
4130 /// # use dicom_core::value::PrimitiveValue;
4131 /// let mut value = dicom_value!(I32, [1, 2, 5]);
4132 /// value.truncate(2);
4133 /// assert_eq!(value.to_multi_int::<i32>()?, vec![1, 2]);
4134 ///
4135 /// value.truncate(0);
4136 /// assert_eq!(value.multiplicity(), 0);
4137 /// # Ok::<_, Box<dyn std::error::Error>>(())
4138 /// ```
4139 pub fn truncate(&mut self, limit: usize) {
4140 match self {
4141 PrimitiveValue::Empty | PrimitiveValue::Str(_) => { /* no-op */ }
4142 PrimitiveValue::Strs(l) => l.truncate(limit),
4143 PrimitiveValue::Tags(l) => l.truncate(limit),
4144 PrimitiveValue::U8(l) => l.truncate(limit),
4145 PrimitiveValue::I16(l) => l.truncate(limit),
4146 PrimitiveValue::U16(l) => l.truncate(limit),
4147 PrimitiveValue::I32(l) => l.truncate(limit),
4148 PrimitiveValue::U32(l) => l.truncate(limit),
4149 PrimitiveValue::I64(l) => l.truncate(limit),
4150 PrimitiveValue::U64(l) => l.truncate(limit),
4151 PrimitiveValue::F32(l) => l.truncate(limit),
4152 PrimitiveValue::F64(l) => l.truncate(limit),
4153 PrimitiveValue::Date(l) => l.truncate(limit),
4154 PrimitiveValue::DateTime(l) => l.truncate(limit),
4155 PrimitiveValue::Time(l) => l.truncate(limit),
4156 }
4157 }
4158}
4159
4160/// The output of this method is equivalent to calling the method `to_str`
4161impl Display for PrimitiveValue {
4162 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
4163 /// Auxiliary function for turning a sequence of values
4164 /// into a backslash-delimited string.
4165 fn seq_to_str<I>(iter: I) -> String
4166 where
4167 I: IntoIterator,
4168 I::Item: Display,
4169 {
4170 iter.into_iter().map(|x| x.to_string()).join("\\")
4171 }
4172
4173 match self {
4174 PrimitiveValue::Empty => Ok(()),
4175 PrimitiveValue::Str(_) => f.write_str(&self.to_str()),
4176 PrimitiveValue::Strs(_) => f.write_str(&self.to_str()),
4177 PrimitiveValue::Date(values) => {
4178 f.write_str(&values.into_iter().map(|date| date.to_string()).join("\\"))
4179 }
4180 PrimitiveValue::Time(values) => {
4181 f.write_str(&values.into_iter().map(|time| time.to_string()).join("\\"))
4182 }
4183 PrimitiveValue::DateTime(values) => f.write_str(
4184 &values
4185 .into_iter()
4186 .map(|datetime| datetime.to_string())
4187 .join("\\"),
4188 ),
4189 PrimitiveValue::U8(values) => f.write_str(&seq_to_str(values)),
4190 PrimitiveValue::U16(values) => f.write_str(&seq_to_str(values)),
4191 PrimitiveValue::U32(values) => f.write_str(&seq_to_str(values)),
4192 PrimitiveValue::I16(values) => f.write_str(&seq_to_str(values)),
4193 PrimitiveValue::I32(values) => f.write_str(&seq_to_str(values)),
4194 PrimitiveValue::U64(values) => f.write_str(&seq_to_str(values)),
4195 PrimitiveValue::I64(values) => f.write_str(&seq_to_str(values)),
4196 PrimitiveValue::F32(values) => f.write_str(&seq_to_str(values)),
4197 PrimitiveValue::F64(values) => f.write_str(&seq_to_str(values)),
4198 PrimitiveValue::Tags(values) => f.write_str(&seq_to_str(values)),
4199 }
4200 }
4201}
4202
4203impl HasLength for PrimitiveValue {
4204 fn length(&self) -> Length {
4205 Length::defined(self.calculate_byte_len() as u32)
4206 }
4207}
4208
4209impl PartialEq for PrimitiveValue {
4210 fn eq(&self, other: &Self) -> bool {
4211 match (self, other) {
4212 (PrimitiveValue::Empty, PrimitiveValue::Empty) => true,
4213 (PrimitiveValue::Strs(v1), PrimitiveValue::Str(_)) => {
4214 v1.len() == 1 && self.to_str() == other.to_str()
4215 }
4216 (PrimitiveValue::Str(_), PrimitiveValue::Strs(v2)) => {
4217 v2.len() == 1 && self.to_str() == other.to_str()
4218 }
4219 (PrimitiveValue::Strs(_), PrimitiveValue::Strs(_)) => self.to_str() == other.to_str(),
4220 (PrimitiveValue::Str(_), PrimitiveValue::Str(_)) => self.to_str() == other.to_str(),
4221 (PrimitiveValue::Tags(v1), PrimitiveValue::Tags(v2)) => v1 == v2,
4222 (PrimitiveValue::U8(v1), PrimitiveValue::U8(v2)) => v1 == v2,
4223 (PrimitiveValue::I16(v1), PrimitiveValue::I16(v2)) => v1 == v2,
4224 (PrimitiveValue::U16(v1), PrimitiveValue::U16(v2)) => v1 == v2,
4225 (PrimitiveValue::I32(v1), PrimitiveValue::I32(v2)) => v1 == v2,
4226 (PrimitiveValue::U32(v1), PrimitiveValue::U32(v2)) => v1 == v2,
4227 (PrimitiveValue::I64(v1), PrimitiveValue::I64(v2)) => v1 == v2,
4228 (PrimitiveValue::U64(v1), PrimitiveValue::U64(v2)) => v1 == v2,
4229 (PrimitiveValue::F32(v1), PrimitiveValue::F32(v2)) => v1 == v2,
4230 (PrimitiveValue::F64(v1), PrimitiveValue::F64(v2)) => v1 == v2,
4231 (PrimitiveValue::Date(v1), PrimitiveValue::Date(v2)) => v1 == v2,
4232 (PrimitiveValue::DateTime(v1), PrimitiveValue::DateTime(v2)) => v1 == v2,
4233 (PrimitiveValue::Time(v1), PrimitiveValue::Time(v2)) => v1 == v2,
4234 _ => false,
4235 }
4236 }
4237}
4238
4239impl PartialEq<str> for PrimitiveValue {
4240 fn eq(&self, other: &str) -> bool {
4241 match self {
4242 PrimitiveValue::Strs(v) => v.len() == 1 && v[0] == other,
4243 PrimitiveValue::Str(v) => v == other,
4244 _ => false,
4245 }
4246 }
4247}
4248
4249impl PartialEq<&str> for PrimitiveValue {
4250 fn eq(&self, other: &&str) -> bool {
4251 self.eq(*other)
4252 }
4253}
4254
4255/// An enum representing an abstraction of a DICOM element's data value type.
4256/// This should be the equivalent of `PrimitiveValue` without the content,
4257/// plus the `DataSetSequence` and `PixelSequence` entries.
4258#[derive(Debug, PartialEq, Eq, Clone, Copy)]
4259pub enum ValueType {
4260 /// No data. Used for any value of length 0.
4261 Empty,
4262
4263 /// A data set sequence.
4264 /// Used for values with the SQ representation when not empty.
4265 DataSetSequence,
4266
4267 /// An item. Used for the values of encapsulated pixel data.
4268 PixelSequence,
4269
4270 /// A sequence of strings.
4271 /// Used for AE, AS, PN, SH, CS, LO, UI and UC.
4272 /// Can also be used for IS, SS, DS, DA, DT and TM when decoding
4273 /// with format preservation.
4274 Strs,
4275
4276 /// A single string.
4277 /// Used for ST, LT, UT and UR, which are never multi-valued.
4278 Str,
4279
4280 /// A sequence of attribute tags.
4281 /// Used specifically for AT.
4282 Tags,
4283
4284 /// The value is a sequence of unsigned 8-bit integers.
4285 /// Used for OB and UN.
4286 U8,
4287
4288 /// The value is a sequence of signed 16-bit integers.
4289 /// Used for SS.
4290 I16,
4291
4292 /// A sequence of unsigned 16-bit integers.
4293 /// Used for US and OW.
4294 U16,
4295
4296 /// A sequence of signed 32-bit integers.
4297 /// Used for SL and IS.
4298 I32,
4299
4300 /// A sequence of unsigned 32-bit integers.
4301 /// Used for UL and OL.
4302 U32,
4303
4304 /// A sequence of signed 64-bit integers.
4305 /// Used for SV.
4306 I64,
4307
4308 /// A sequence of unsigned 64-bit integers.
4309 /// Used for UV and OV.
4310 U64,
4311
4312 /// The value is a sequence of 32-bit floating point numbers.
4313 /// Used for OF and FL.
4314 F32,
4315
4316 /// The value is a sequence of 64-bit floating point numbers.
4317 /// Used for OD, FD and DS.
4318 F64,
4319
4320 /// A sequence of dates.
4321 /// Used for the DA representation.
4322 Date,
4323
4324 /// A sequence of date-time values.
4325 /// Used for the DT representation.
4326 DateTime,
4327
4328 /// A sequence of time values.
4329 /// Used for the TM representation.
4330 Time,
4331}
4332
4333impl DicomValueType for PrimitiveValue {
4334 fn value_type(&self) -> ValueType {
4335 match *self {
4336 PrimitiveValue::Empty => ValueType::Empty,
4337 PrimitiveValue::Date(_) => ValueType::Date,
4338 PrimitiveValue::DateTime(_) => ValueType::DateTime,
4339 PrimitiveValue::F32(_) => ValueType::F32,
4340 PrimitiveValue::F64(_) => ValueType::F64,
4341 PrimitiveValue::I16(_) => ValueType::I16,
4342 PrimitiveValue::I32(_) => ValueType::I32,
4343 PrimitiveValue::I64(_) => ValueType::I64,
4344 PrimitiveValue::Str(_) => ValueType::Str,
4345 PrimitiveValue::Strs(_) => ValueType::Strs,
4346 PrimitiveValue::Tags(_) => ValueType::Tags,
4347 PrimitiveValue::Time(_) => ValueType::Time,
4348 PrimitiveValue::U16(_) => ValueType::U16,
4349 PrimitiveValue::U32(_) => ValueType::U32,
4350 PrimitiveValue::U64(_) => ValueType::U64,
4351 PrimitiveValue::U8(_) => ValueType::U8,
4352 }
4353 }
4354
4355 fn cardinality(&self) -> usize {
4356 match self {
4357 PrimitiveValue::Empty => 0,
4358 PrimitiveValue::Str(_) => 1,
4359 PrimitiveValue::Date(b) => b.len(),
4360 PrimitiveValue::DateTime(b) => b.len(),
4361 PrimitiveValue::F32(b) => b.len(),
4362 PrimitiveValue::F64(b) => b.len(),
4363 PrimitiveValue::I16(b) => b.len(),
4364 PrimitiveValue::I32(b) => b.len(),
4365 PrimitiveValue::I64(b) => b.len(),
4366 PrimitiveValue::Strs(b) => b.len(),
4367 PrimitiveValue::Tags(b) => b.len(),
4368 PrimitiveValue::Time(b) => b.len(),
4369 PrimitiveValue::U16(b) => b.len(),
4370 PrimitiveValue::U32(b) => b.len(),
4371 PrimitiveValue::U64(b) => b.len(),
4372 PrimitiveValue::U8(b) => b.len(),
4373 }
4374 }
4375}
4376
4377fn trim_last_whitespace(x: &[u8]) -> &[u8] {
4378 match x.last() {
4379 Some(b' ') | Some(b'\0') => &x[..x.len() - 1],
4380 _ => x,
4381 }
4382}
4383
4384#[inline]
4385fn whitespace_or_null(c: char) -> bool {
4386 c.is_whitespace() || c == '\0'
4387}
4388
4389#[cfg(test)]
4390mod tests {
4391 use super::{CastValueError, ConvertValueError, InvalidValueReadError};
4392 use crate::dicom_value;
4393 use crate::value::partial::{DicomDate, DicomDateTime, DicomTime};
4394 use crate::value::range::{DateRange, DateTimeRange, TimeRange};
4395 use crate::value::{PrimitiveValue, ValueType};
4396 use chrono::{FixedOffset, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone};
4397 use smallvec::smallvec;
4398
4399 #[test]
4400 fn primitive_value_to_str() {
4401 assert_eq!(PrimitiveValue::Empty.to_str(), "");
4402
4403 // does not copy on a single string
4404 let value = PrimitiveValue::Str("Smith^John".to_string());
4405 let string = value.to_str();
4406 assert_eq!(string, "Smith^John",);
4407 match string {
4408 std::borrow::Cow::Borrowed(_) => {} // good
4409 _ => panic!("expected string to be borrowed, but was owned"),
4410 }
4411
4412 assert_eq!(
4413 PrimitiveValue::Date(smallvec![DicomDate::from_ymd(2014, 10, 12).unwrap()]).to_str(),
4414 "2014-10-12",
4415 );
4416 assert_eq!(
4417 dicom_value!(Strs, ["DERIVED", "PRIMARY", "WHOLE BODY", "EMISSION"]).to_str(),
4418 "DERIVED\\PRIMARY\\WHOLE BODY\\EMISSION",
4419 );
4420
4421 // sequence of numbers
4422 let value = PrimitiveValue::from(vec![10, 11, 12]);
4423 assert_eq!(value.to_str(), "10\\11\\12",);
4424
4425 // now test that trailing whitespace is trimmed
4426 // removes whitespace at the end of a string
4427 let value = PrimitiveValue::from("1.2.345\0".to_string());
4428 assert_eq!(&value.to_str(), "1.2.345");
4429 let value = PrimitiveValue::from("1.2.345 ".to_string());
4430 assert_eq!(&value.to_str(), "1.2.345");
4431
4432 // removes whitespace at the end on multiple strings
4433 let value = dicom_value!(Strs, ["ONE ", "TWO", "THREE", "SIX "]);
4434 assert_eq!(&value.to_str(), "ONE\\TWO\\THREE\\SIX");
4435
4436 // maintains the leading whitespace on a string and removes at the end
4437 let value = PrimitiveValue::from("\x001.2.345\0".to_string());
4438 assert_eq!(&value.to_str(), "\x001.2.345");
4439
4440 // maintains the leading whitespace on multiple strings and removes at the end
4441 let value = dicom_value!(Strs, [" ONE", "TWO", "THREE", " SIX "]);
4442 assert_eq!(&value.to_str(), " ONE\\TWO\\THREE\\ SIX");
4443 }
4444
4445 #[test]
4446 fn primitive_value_to_raw_str() {
4447 // maintains whitespace at the end of a string
4448 let value = PrimitiveValue::from("1.2.345\0".to_string());
4449 assert_eq!(&value.to_raw_str(), "1.2.345\0");
4450
4451 // maintains whitespace at the end on multiple strings
4452 let value = dicom_value!(Strs, ["ONE", "TWO", "THREE", "SIX "]);
4453 assert_eq!(&value.to_raw_str(), "ONE\\TWO\\THREE\\SIX ");
4454
4455 // maintains the leading whitespace on a string and maintains at the end
4456 let value = PrimitiveValue::from("\x001.2.345\0".to_string());
4457 assert_eq!(&value.to_raw_str(), "\x001.2.345\0");
4458
4459 // maintains the leading whitespace on multiple strings and maintains at the end
4460 let value = dicom_value!(Strs, [" ONE", "TWO", "THREE", " SIX "]);
4461 assert_eq!(&value.to_raw_str(), " ONE\\TWO\\THREE\\ SIX ");
4462 }
4463
4464 #[test]
4465 fn primitive_value_to_bytes() {
4466 assert_eq!(PrimitiveValue::Empty.to_bytes(), &[][..]);
4467
4468 if cfg!(target_endian = "little") {
4469 assert_eq!(
4470 PrimitiveValue::U16(smallvec![1, 2, 0x0601,]).to_bytes(),
4471 &[0x01, 0x00, 0x02, 0x00, 0x01, 0x06][..],
4472 );
4473 } else {
4474 assert_eq!(
4475 PrimitiveValue::U16(smallvec![0x0001, 0x0002, 0x0601,]).to_bytes(),
4476 &[0x00, 0x01, 0x00, 0x02, 0x06, 0x01][..],
4477 );
4478 }
4479
4480 // does not copy on a single string
4481 let value = PrimitiveValue::from("Smith^John");
4482 let bytes = value.to_bytes();
4483 assert_eq!(bytes, &b"Smith^John"[..],);
4484 match bytes {
4485 std::borrow::Cow::Borrowed(_) => {} // good
4486 _ => panic!("expected bytes to be borrowed, but are owned"),
4487 }
4488
4489 assert_eq!(
4490 PrimitiveValue::Date(smallvec![DicomDate::from_ym(2014, 10).unwrap()]).to_bytes(),
4491 &b"2014-10"[..],
4492 );
4493 assert_eq!(
4494 dicom_value!(Strs, ["DERIVED", "PRIMARY", "WHOLE BODY", "EMISSION",]).to_bytes(),
4495 &b"DERIVED\\PRIMARY\\WHOLE BODY\\EMISSION"[..],
4496 );
4497
4498 // does not copy on bytes
4499 let value = PrimitiveValue::from(vec![0x99; 16]);
4500 let bytes = value.to_bytes();
4501 assert_eq!(bytes, &[0x99; 16][..],);
4502 match bytes {
4503 std::borrow::Cow::Borrowed(_) => {} // good
4504 _ => panic!("expected bytes to be borrowed, but are owned"),
4505 }
4506 }
4507
4508 #[test]
4509 fn primitive_value_to_float() {
4510 // DS conversion to f32
4511 assert_eq!(dicom_value!(Str, "-73.4 ").to_float32().ok(), Some(-73.4));
4512
4513 // DS conversion with leading whitespaces
4514 assert_eq!(dicom_value!(Str, " -73.4 ").to_float32().ok(), Some(-73.4));
4515
4516 // DS conversion with leading whitespaces
4517 assert_eq!(dicom_value!(Str, " -73.4 ").to_float64().ok(), Some(-73.4));
4518
4519 // DS conversion with exponential
4520 assert_eq!(dicom_value!(Str, "1e1").to_float32().ok(), Some(10.0));
4521 }
4522
4523 #[test]
4524 fn primitive_value_to_int() {
4525 assert!(PrimitiveValue::Empty.to_int::<i32>().is_err());
4526
4527 // exact match
4528 assert_eq!(
4529 PrimitiveValue::from(0x0601_u16).to_int().ok(),
4530 Some(0x0601_u16),
4531 );
4532 // conversions are automatically applied
4533 assert_eq!(
4534 PrimitiveValue::from(0x0601_u16).to_int().ok(),
4535 Some(0x0601_u32),
4536 );
4537 assert_eq!(
4538 PrimitiveValue::from(0x0601_u16).to_int().ok(),
4539 Some(0x0601_i64),
4540 );
4541 assert_eq!(
4542 PrimitiveValue::from(0x0601_u16).to_int().ok(),
4543 Some(0x0601_u64),
4544 );
4545
4546 // takes the first number
4547 assert_eq!(dicom_value!(I32, [1, 2, 5]).to_int().ok(), Some(1),);
4548
4549 // admits an integer as text
4550 assert_eq!(dicom_value!(Strs, ["-73", "2"]).to_int().ok(), Some(-73),);
4551
4552 // admits an integer as text with leading spaces
4553 assert_eq!(dicom_value!(Strs, [" -73", " 2"]).to_int().ok(), Some(-73),);
4554
4555 // does not admit destructive conversions
4556 assert!(PrimitiveValue::from(-1).to_int::<u32>().is_err());
4557
4558 // does not admit strings which are not numbers
4559 assert!(matches!(
4560 dicom_value!(Strs, ["Smith^John"]).to_int::<u8>(),
4561 Err(ConvertValueError {
4562 requested: _,
4563 original: ValueType::Strs,
4564 // would try to parse as an integer and fail
4565 cause: Some(cause),
4566 }) if matches!(&*cause, InvalidValueReadError::ParseInteger { .. })
4567 ));
4568 }
4569
4570 #[test]
4571 fn primitive_value_to_multi_int() {
4572 assert_eq!(PrimitiveValue::Empty.to_multi_int::<i32>().unwrap(), vec![]);
4573
4574 let test_value = dicom_value!(U16, [0x0601, 0x5353, 3, 4]);
4575 // exact match
4576 let numbers = test_value.to_multi_int::<u16>().unwrap();
4577 assert_eq!(numbers, vec![0x0601, 0x5353, 3, 4],);
4578 // type is inferred on context
4579 let numbers: Vec<u32> = test_value.to_multi_int().unwrap();
4580 assert_eq!(numbers, vec![0x0601_u32, 0x5353, 3, 4],);
4581 let numbers: Vec<i64> = test_value.to_multi_int().unwrap();
4582 assert_eq!(numbers, vec![0x0601_i64, 0x5353, 3, 4],);
4583 assert_eq!(
4584 test_value.to_multi_int::<u64>().unwrap(),
4585 vec![0x0601_u64, 0x5353, 3, 4],
4586 );
4587
4588 // takes all numbers
4589 assert_eq!(
4590 dicom_value!(I32, [1, 2, 5]).to_multi_int().ok(),
4591 Some(vec![1, 2, 5]),
4592 );
4593
4594 // admits a integer as text, trailing space too
4595 assert_eq!(
4596 dicom_value!(Strs, ["-73", "2 "]).to_multi_int().ok(),
4597 Some(vec![-73, 2]),
4598 );
4599
4600 // does not admit destructive conversions
4601 assert!(matches!(
4602 dicom_value!(I32, [0, 1, -1]).to_multi_int::<u64>(),
4603 Err(ConvertValueError {
4604 original: ValueType::I32,
4605 // the cast from -1_i32 to u32 would fail
4606 cause: Some(cause),
4607 ..
4608 }) if matches!(&*cause,
4609 InvalidValueReadError::NarrowConvert {
4610 value: x,
4611 ..
4612 } if x == "-1")
4613 ));
4614
4615 // not even from strings
4616 assert!(matches!(
4617 dicom_value!(Strs, ["0", "1", "-1"]).to_multi_int::<u16>(),
4618 Err(ConvertValueError {
4619 original: ValueType::Strs,
4620 // the conversion from "-1" to u32 would fail
4621 cause: Some(cause),
4622 ..
4623 }) if matches!(&*cause, InvalidValueReadError::ParseInteger { .. })
4624 ));
4625
4626 // does not admit strings which are not numbers
4627 assert!(matches!(
4628 dicom_value!(Strs, ["Smith^John"]).to_int::<u8>(),
4629 Err(ConvertValueError {
4630 requested: _,
4631 original: ValueType::Strs,
4632 // would try to parse as an integer and fail
4633 cause: Some(cause),
4634 }) if matches!(&*cause, InvalidValueReadError::ParseInteger { .. })
4635 ));
4636 }
4637
4638 #[test]
4639 fn primitive_value_to_multi_floats() {
4640 assert_eq!(PrimitiveValue::Empty.to_multi_float32().ok(), Some(vec![]));
4641
4642 let test_value = dicom_value!(U16, [1, 2, 3, 4]);
4643
4644 assert_eq!(
4645 test_value.to_multi_float32().ok(),
4646 Some(vec![1., 2., 3., 4.]),
4647 );
4648 assert_eq!(
4649 test_value.to_multi_float64().ok(),
4650 Some(vec![1., 2., 3., 4.]),
4651 );
4652
4653 // admits a number as text, trailing space too
4654 assert_eq!(
4655 dicom_value!(Strs, ["7.25", "-12.5 "])
4656 .to_multi_float64()
4657 .ok(),
4658 Some(vec![7.25, -12.5]),
4659 );
4660
4661 // does not admit strings which are not numbers
4662 assert!(matches!(
4663 dicom_value!(Strs, ["Smith^John"]).to_multi_float64(),
4664 Err(ConvertValueError {
4665 requested: _,
4666 original: ValueType::Strs,
4667 // would try to parse as a float and fail
4668 cause: Some(cause),
4669 }) if matches!(&*cause, InvalidValueReadError::ParseFloat { .. })
4670 ));
4671 }
4672
4673 #[test]
4674 fn primitive_value_to_naive_date() {
4675 // to NaiveDate
4676 assert_eq!(
4677 PrimitiveValue::Date(smallvec![DicomDate::from_ymd(2014, 10, 12).unwrap()])
4678 .to_naive_date()
4679 .unwrap(),
4680 NaiveDate::from_ymd_opt(2014, 10, 12).unwrap(),
4681 );
4682 // from text (Str)
4683 assert_eq!(
4684 dicom_value!(Str, "20141012").to_naive_date().unwrap(),
4685 NaiveDate::from_ymd_opt(2014, 10, 12).unwrap(),
4686 );
4687 // from text (Strs)
4688 assert_eq!(
4689 dicom_value!(Strs, ["20141012"]).to_naive_date().unwrap(),
4690 NaiveDate::from_ymd_opt(2014, 10, 12).unwrap(),
4691 );
4692 // from bytes
4693 assert_eq!(
4694 PrimitiveValue::from(b"20200229").to_naive_date().unwrap(),
4695 NaiveDate::from_ymd_opt(2020, 2, 29).unwrap(),
4696 );
4697 // not a date
4698 assert!(matches!(
4699 PrimitiveValue::Str("Smith^John".to_string()).to_naive_date(),
4700 Err(ConvertValueError {
4701 requested: "NaiveDate",
4702 original: ValueType::Str,
4703 // would try to parse as a date and fail
4704 cause: Some(_),
4705 })
4706 ));
4707 }
4708
4709 #[test]
4710 fn primitive_value_to_dicom_date() {
4711 // primitive conversion
4712 assert_eq!(
4713 PrimitiveValue::Date(smallvec![DicomDate::from_ymd(2014, 10, 12).unwrap()])
4714 .to_date()
4715 .ok(),
4716 Some(DicomDate::from_ymd(2014, 10, 12).unwrap()),
4717 );
4718
4719 // from Strs
4720 assert_eq!(
4721 dicom_value!(Strs, ["201410", "2020", "20200101"])
4722 .to_date()
4723 .unwrap(),
4724 DicomDate::from_ym(2014, 10).unwrap()
4725 );
4726
4727 // from bytes
4728 assert_eq!(
4729 PrimitiveValue::from(b"202002").to_date().ok(),
4730 Some(DicomDate::from_ym(2020, 2).unwrap())
4731 );
4732 }
4733
4734 #[test]
4735 fn primitive_value_to_multi_dicom_date() {
4736 assert_eq!(
4737 dicom_value!(Strs, ["201410", "2020", "20200101"])
4738 .to_multi_date()
4739 .unwrap(),
4740 vec![
4741 DicomDate::from_ym(2014, 10).unwrap(),
4742 DicomDate::from_y(2020).unwrap(),
4743 DicomDate::from_ymd(2020, 1, 1).unwrap()
4744 ]
4745 );
4746
4747 assert!(dicom_value!(Strs, ["-44"]).to_multi_date().is_err());
4748 }
4749
4750 #[test]
4751 fn primitive_value_to_naive_time() {
4752 // trivial conversion
4753 assert_eq!(
4754 PrimitiveValue::from(DicomTime::from_hms(11, 9, 26).unwrap())
4755 .to_naive_time()
4756 .unwrap(),
4757 NaiveTime::from_hms_opt(11, 9, 26).unwrap(),
4758 );
4759 // from text (Str)
4760 assert_eq!(
4761 dicom_value!(Str, "110926.3").to_naive_time().unwrap(),
4762 NaiveTime::from_hms_milli_opt(11, 9, 26, 300).unwrap(),
4763 );
4764 // from text with fraction of a second + padding
4765 assert_eq!(
4766 PrimitiveValue::from("110926.38 ").to_naive_time().unwrap(),
4767 NaiveTime::from_hms_milli_opt(11, 9, 26, 380).unwrap(),
4768 );
4769 // from text (Strs)
4770 assert_eq!(
4771 dicom_value!(Strs, ["110926.38"]).to_naive_time().unwrap(),
4772 NaiveTime::from_hms_milli_opt(11, 9, 26, 380).unwrap(),
4773 );
4774
4775 // from text without fraction of a second (assumes 0 ms in fraction)
4776 assert_eq!(
4777 dicom_value!(Str, "110926").to_naive_time().unwrap(),
4778 NaiveTime::from_hms_opt(11, 9, 26).unwrap(),
4779 );
4780
4781 // absence of seconds is considered to be an incomplete value
4782 assert!(PrimitiveValue::from("1109").to_naive_time().is_err(),);
4783 assert!(dicom_value!(Strs, ["1109"]).to_naive_time().is_err());
4784 assert!(dicom_value!(Strs, ["11"]).to_naive_time().is_err());
4785
4786 // not a time
4787 assert!(matches!(
4788 PrimitiveValue::Str("Smith^John".to_string()).to_naive_time(),
4789 Err(ConvertValueError {
4790 requested: "NaiveTime",
4791 original: ValueType::Str,
4792 ..
4793 })
4794 ));
4795 }
4796
4797 #[test]
4798 fn primitive_value_to_dicom_time() {
4799 // from NaiveTime - results in exact DicomTime with default fraction
4800 assert_eq!(
4801 PrimitiveValue::from(DicomTime::from_hms_micro(11, 9, 26, 0).unwrap())
4802 .to_time()
4803 .unwrap(),
4804 DicomTime::from_hms_micro(11, 9, 26, 0).unwrap(),
4805 );
4806 // from NaiveTime with milli precision
4807 assert_eq!(
4808 PrimitiveValue::from(DicomTime::from_hms_milli(11, 9, 26, 123).unwrap())
4809 .to_time()
4810 .unwrap(),
4811 DicomTime::from_hms_milli(11, 9, 26, 123).unwrap(),
4812 );
4813 // from NaiveTime with micro precision
4814 assert_eq!(
4815 PrimitiveValue::from(DicomTime::from_hms_micro(11, 9, 26, 123).unwrap())
4816 .to_time()
4817 .unwrap(),
4818 DicomTime::from_hms_micro(11, 9, 26, 123).unwrap(),
4819 );
4820 // from text (Str)
4821 assert_eq!(
4822 dicom_value!(Str, "110926").to_time().unwrap(),
4823 DicomTime::from_hms(11, 9, 26).unwrap(),
4824 );
4825 // from text with fraction of a second + padding
4826 assert_eq!(
4827 PrimitiveValue::from("110926.38 ").to_time().unwrap(),
4828 DicomTime::from_hmsf(11, 9, 26, 38, 2).unwrap(),
4829 );
4830 // from text (Strs)
4831 assert_eq!(
4832 dicom_value!(Strs, ["110926"]).to_time().unwrap(),
4833 DicomTime::from_hms(11, 9, 26).unwrap(),
4834 );
4835 // from text (Strs) with fraction of a second
4836 assert_eq!(
4837 dicom_value!(Strs, ["110926.123456"]).to_time().unwrap(),
4838 DicomTime::from_hms_micro(11, 9, 26, 123_456).unwrap(),
4839 );
4840 // from bytes with fraction of a second
4841 assert_eq!(
4842 PrimitiveValue::from(&b"110926.987"[..]).to_time().unwrap(),
4843 DicomTime::from_hms_milli(11, 9, 26, 987).unwrap(),
4844 );
4845 // from bytes with fraction of a second + padding
4846 assert_eq!(
4847 PrimitiveValue::from(&b"110926.38 "[..]).to_time().unwrap(),
4848 DicomTime::from_hmsf(11, 9, 26, 38, 2).unwrap(),
4849 );
4850 // not a time
4851 assert!(matches!(
4852 PrimitiveValue::Str("Smith^John".to_string()).to_time(),
4853 Err(ConvertValueError {
4854 requested: "DicomTime",
4855 original: ValueType::Str,
4856 ..
4857 })
4858 ));
4859 }
4860
4861 #[test]
4862 fn primitive_value_to_dicom_datetime() {
4863 let offset = FixedOffset::east_opt(1).unwrap();
4864
4865 // try from chrono::DateTime<FixedOffset>
4866 assert_eq!(
4867 PrimitiveValue::from(
4868 DicomDateTime::from_date_and_time_with_time_zone(
4869 DicomDate::from_ymd(2012, 12, 21).unwrap(),
4870 DicomTime::from_hms_micro(11, 9, 26, /* 000 */ 123).unwrap(),
4871 offset
4872 )
4873 .unwrap()
4874 )
4875 .to_datetime()
4876 .unwrap(),
4877 DicomDateTime::from_date_and_time_with_time_zone(
4878 DicomDate::from_ymd(2012, 12, 21).unwrap(),
4879 DicomTime::from_hms_micro(11, 9, 26, /* 000 */ 123).unwrap(),
4880 offset
4881 )
4882 .unwrap()
4883 );
4884 // try from chrono::NaiveDateTime
4885 assert_eq!(
4886 PrimitiveValue::from(
4887 DicomDateTime::from_date_and_time(
4888 DicomDate::from_ymd(2012, 12, 21).unwrap(),
4889 DicomTime::from_hms_micro(11, 9, 26, /* 000 */ 123).unwrap()
4890 )
4891 .unwrap()
4892 )
4893 .to_datetime()
4894 .unwrap(),
4895 DicomDateTime::from_date_and_time(
4896 DicomDate::from_ymd(2012, 12, 21).unwrap(),
4897 DicomTime::from_hms_micro(11, 9, 26, /* 000 */ 123).unwrap()
4898 )
4899 .unwrap()
4900 );
4901 // from text (Str) - minimum allowed is a YYYY
4902 assert_eq!(
4903 dicom_value!(Str, "2012").to_datetime().unwrap(),
4904 DicomDateTime::from_date(DicomDate::from_y(2012).unwrap())
4905 );
4906 // from text with fraction of a second + padding
4907 assert_eq!(
4908 PrimitiveValue::from("20121221110926.38 ")
4909 .to_datetime()
4910 .unwrap(),
4911 DicomDateTime::from_date_and_time(
4912 DicomDate::from_ymd(2012, 12, 21).unwrap(),
4913 DicomTime::from_hmsf(11, 9, 26, 38, 2).unwrap()
4914 )
4915 .unwrap()
4916 );
4917 // from text (Strs) with fraction of a second + padding
4918 assert_eq!(
4919 dicom_value!(Strs, ["20121221110926.38 "])
4920 .to_datetime()
4921 .unwrap(),
4922 DicomDateTime::from_date_and_time(
4923 DicomDate::from_ymd(2012, 12, 21).unwrap(),
4924 DicomTime::from_hmsf(11, 9, 26, 38, 2).unwrap()
4925 )
4926 .unwrap()
4927 );
4928 // not a dicom_datetime
4929 assert!(matches!(
4930 PrimitiveValue::from("Smith^John").to_datetime(),
4931 Err(ConvertValueError {
4932 requested: "DicomDateTime",
4933 original: ValueType::Str,
4934 ..
4935 })
4936 ));
4937 }
4938
4939 #[test]
4940 fn primitive_value_to_multi_dicom_datetime() {
4941 // from text (Strs)
4942 assert_eq!(
4943 dicom_value!(
4944 Strs,
4945 ["20121221110926.38 ", "1992", "19901010-0500", "1990+0501"]
4946 )
4947 .to_multi_datetime()
4948 .unwrap(),
4949 vec!(
4950 DicomDateTime::from_date_and_time(
4951 DicomDate::from_ymd(2012, 12, 21).unwrap(),
4952 DicomTime::from_hmsf(11, 9, 26, 38, 2).unwrap()
4953 )
4954 .unwrap(),
4955 DicomDateTime::from_date(DicomDate::from_y(1992).unwrap()),
4956 DicomDateTime::from_date_with_time_zone(
4957 DicomDate::from_ymd(1990, 10, 10).unwrap(),
4958 FixedOffset::west_opt(5 * 3600).unwrap()
4959 ),
4960 DicomDateTime::from_date_with_time_zone(
4961 DicomDate::from_y(1990).unwrap(),
4962 FixedOffset::east_opt(5 * 3600 + 60).unwrap()
4963 )
4964 )
4965 );
4966 }
4967
4968 #[test]
4969 fn primitive_value_to_date_range() {
4970 // converts first value of sequence
4971 assert_eq!(
4972 dicom_value!(Strs, ["20121221-", "1992-", "1990-1992", "1990+0501"])
4973 .to_date_range()
4974 .unwrap(),
4975 DateRange::from_start(NaiveDate::from_ymd_opt(2012, 12, 21).unwrap())
4976 );
4977 }
4978
4979 #[test]
4980 fn primitive_value_to_time_range() {
4981 assert_eq!(
4982 dicom_value!(Str, "-153012.123").to_time_range().unwrap(),
4983 TimeRange::from_end(NaiveTime::from_hms_micro_opt(15, 30, 12, 123_999).unwrap())
4984 );
4985 assert_eq!(
4986 PrimitiveValue::from(&b"1015-"[..]).to_time_range().unwrap(),
4987 TimeRange::from_start(NaiveTime::from_hms_opt(10, 15, 0).unwrap())
4988 );
4989 }
4990
4991 #[test]
4992 fn primitive_value_to_datetime_range() {
4993 assert_eq!(
4994 dicom_value!(Str, "202002-20210228153012.123")
4995 .to_datetime_range()
4996 .unwrap(),
4997 DateTimeRange::from_start_to_end(
4998 NaiveDateTime::new(
4999 NaiveDate::from_ymd_opt(2020, 2, 1).unwrap(),
5000 NaiveTime::from_hms_opt(0, 0, 0).unwrap()
5001 ),
5002 NaiveDateTime::new(
5003 NaiveDate::from_ymd_opt(2021, 2, 28).unwrap(),
5004 NaiveTime::from_hms_micro_opt(15, 30, 12, 123_999).unwrap()
5005 )
5006 )
5007 .unwrap()
5008 );
5009 // East UTC offset gets parsed and the missing lower bound time-zone
5010 // will be the local clock time-zone offset
5011 assert_eq!(
5012 PrimitiveValue::from(&b"2020-2030+0800"[..])
5013 .to_datetime_range()
5014 .unwrap(),
5015 DateTimeRange::TimeZone {
5016 start: Some(
5017 Local::now()
5018 .offset()
5019 .from_local_datetime(&NaiveDateTime::new(
5020 NaiveDate::from_ymd_opt(2020, 1, 1).unwrap(),
5021 NaiveTime::from_hms_opt(0, 0, 0).unwrap()
5022 ))
5023 .unwrap()
5024 ),
5025 end: Some(
5026 FixedOffset::east_opt(8 * 3600)
5027 .unwrap()
5028 .from_local_datetime(&NaiveDateTime::new(
5029 NaiveDate::from_ymd_opt(2030, 12, 31).unwrap(),
5030 NaiveTime::from_hms_micro_opt(23, 59, 59, 999_999).unwrap()
5031 ))
5032 .unwrap()
5033 )
5034 }
5035 );
5036 }
5037
5038 #[test]
5039 fn calculate_byte_len() {
5040 // single even string
5041 // b"ABCD"
5042 let val = dicom_value!("ABCD");
5043 assert_eq!(val.calculate_byte_len(), 4);
5044
5045 // multi string, no padding
5046 // b"ABCD\\EFG"
5047 let val = dicom_value!(Strs, ["ABCD", "EFG"]);
5048 assert_eq!(val.calculate_byte_len(), 8);
5049
5050 // multi string with padding
5051 // b"ABCD\\EFGH "
5052 let val = dicom_value!(Strs, ["ABCD", "EFGH"]);
5053 assert_eq!(val.calculate_byte_len(), 10);
5054
5055 // multi date, no padding
5056 // b"20141012\\202009\\20180101"
5057 let val = dicom_value!(
5058 Date,
5059 [
5060 DicomDate::from_ymd(2014, 10, 12).unwrap(),
5061 DicomDate::from_ym(2020, 9).unwrap(),
5062 DicomDate::from_ymd(2018, 1, 1).unwrap()
5063 ]
5064 );
5065 assert_eq!(val.calculate_byte_len(), 24);
5066
5067 // multi date with padding
5068 // b"20141012\\2020 "
5069 let val = dicom_value!(
5070 Date,
5071 [
5072 DicomDate::from_ymd(2014, 10, 12).unwrap(),
5073 DicomDate::from_y(2020).unwrap()
5074 ]
5075 );
5076 assert_eq!(val.calculate_byte_len(), 14);
5077
5078 // single time with second fragment - full precision
5079 // b"185530.475600 "
5080 let val = dicom_value!(DicomTime::from_hms_micro(18, 55, 30, 475_600).unwrap());
5081 assert_eq!(val.calculate_byte_len(), 14);
5082
5083 // multi time with padding
5084 // b"185530\\185530 "
5085 let val = dicom_value!(
5086 Time,
5087 [
5088 DicomTime::from_hms(18, 55, 30).unwrap(),
5089 DicomTime::from_hms(18, 55, 30).unwrap()
5090 ]
5091 );
5092 assert_eq!(val.calculate_byte_len(), 14);
5093
5094 // single date-time with time zone, no second fragment
5095 // b"20121221093001+0100 "
5096 let offset = FixedOffset::east_opt(3600).unwrap();
5097 let val = PrimitiveValue::from(
5098 DicomDateTime::from_date_and_time_with_time_zone(
5099 DicomDate::from_ymd(2012, 12, 21).unwrap(),
5100 DicomTime::from_hms(9, 30, 1).unwrap(),
5101 offset,
5102 )
5103 .unwrap(),
5104 );
5105 assert_eq!(val.calculate_byte_len(), 20);
5106
5107 // single date-time without time zone, no second fragment
5108 // b"20121221093001 "
5109 let val = PrimitiveValue::from(
5110 DicomDateTime::from_date_and_time(
5111 DicomDate::from_ymd(2012, 12, 21).unwrap(),
5112 DicomTime::from_hms(9, 30, 1).unwrap(),
5113 )
5114 .unwrap(),
5115 );
5116 assert_eq!(val.calculate_byte_len(), 14);
5117
5118 // very precise date time, 0 microseconds
5119 let dicom_date_time = DicomDateTime::from_date_and_time_with_time_zone(
5120 DicomDate::from_ymd(2024, 8, 26).unwrap(),
5121 DicomTime::from_hms_micro(19, 41, 38, 0).unwrap(),
5122 FixedOffset::west_opt(0).unwrap(),
5123 )
5124 .unwrap();
5125 let val = PrimitiveValue::from(dicom_date_time);
5126 assert_eq!(val.calculate_byte_len(), 26);
5127 }
5128
5129 #[test]
5130 fn primitive_value_get() {
5131 assert_eq!(
5132 dicom_value!(Strs, ["Smith^John"]).string().unwrap(),
5133 "Smith^John"
5134 );
5135
5136 assert_eq!(
5137 dicom_value!(Strs, ["Smith^John"]).strings().unwrap(),
5138 &["Smith^John"]
5139 );
5140
5141 assert_eq!(dicom_value!(I32, [1, 2, 5]).int32().unwrap(), 1,);
5142
5143 assert_eq!(
5144 dicom_value!(I32, [1, 2, 5]).int32_slice().unwrap(),
5145 &[1, 2, 5],
5146 );
5147
5148 assert!(matches!(
5149 dicom_value!(I32, [1, 2, 5]).uint32(),
5150 Err(CastValueError {
5151 requested: "uint32",
5152 got: ValueType::I32,
5153 ..
5154 })
5155 ));
5156
5157 assert!(matches!(
5158 dicom_value!(I32, [1, 2, 5]).strings(),
5159 Err(CastValueError {
5160 requested: "strings",
5161 got: ValueType::I32,
5162 ..
5163 })
5164 ));
5165
5166 assert_eq!(
5167 PrimitiveValue::Date(smallvec![DicomDate::from_ymd(2014, 10, 12).unwrap()])
5168 .date()
5169 .unwrap(),
5170 DicomDate::from_ymd(2014, 10, 12).unwrap(),
5171 );
5172
5173 assert!(matches!(
5174 PrimitiveValue::Date(smallvec![DicomDate::from_ymd(2014, 10, 12).unwrap()]).time(),
5175 Err(CastValueError {
5176 requested: "time",
5177 got: ValueType::Date,
5178 ..
5179 })
5180 ));
5181 }
5182
5183 /// Expect Str to be comparable to 1-element Strs.
5184 #[test]
5185 fn eq_ignores_multi_variants() {
5186 assert_eq!(dicom_value!(Str, "abc123"), dicom_value!(Strs, ["abc123"]),);
5187
5188 assert_eq!(dicom_value!(Strs, ["abc123"]), dicom_value!(Str, "abc123"),);
5189
5190 assert_eq!(dicom_value!(Str, "ABC123"), PrimitiveValue::from("ABC123"),);
5191
5192 assert_eq!(dicom_value!(Str, ""), PrimitiveValue::from(""),);
5193 }
5194
5195 #[test]
5196 fn eq_str() {
5197 assert_eq!(PrimitiveValue::from("Doe^John"), "Doe^John");
5198 assert_eq!(dicom_value!(Strs, ["Doe^John"]), "Doe^John");
5199 assert_eq!(PrimitiveValue::from("Doe^John"), &*"Doe^John".to_owned());
5200
5201 assert_ne!(dicom_value!(Strs, ["Doe^John", "Silva^João"]), "Doe^John");
5202 }
5203}