Skip to main content

dicom_core/value/
person_name.rs

1//! Handling of DICOM values with the PN (person name) value representation
2//! as per PS3.5 sect 6.2.
3use std::borrow::Cow;
4use std::fmt::{Display, Formatter};
5use std::mem;
6
7/// A DICOM _Person Name_ (PN value representation).
8///
9/// Values of this type keep
10/// family name, given name, middle name, prefix and suffix
11/// as possibly borrowed values.
12/// All name components are optional.
13///
14/// # Example
15///
16/// A value of type `PersonName` can be obtained
17/// either by parsing a DICOM formatted string via [`from_text`](PersonName::from_text)
18/// or by using the [builder](PersonNameBuilder) API.
19///
20/// ```
21/// # use dicom_core::value::person_name::PersonName;
22/// let dr_seuss: PersonName = PersonName::from_text("Geisel^Theodor^Seuss^Dr.");
23/// assert_eq!(&dr_seuss.to_string(), "Dr. Theodor Seuss Geisel");
24/// assert_eq!(dr_seuss.prefix(), Some("Dr."));
25/// assert_eq!(dr_seuss.given(), Some("Theodor"));
26/// ```
27#[derive(Debug, Clone, Eq, Hash, PartialEq)]
28pub struct PersonName<'a> {
29    prefix: Option<Cow<'a, str>>,
30    family: Option<Cow<'a, str>>,
31    middle: Option<Cow<'a, str>>,
32    given: Option<Cow<'a, str>>,
33    suffix: Option<Cow<'a, str>>,
34}
35
36/// A builder to construct a [`PersonName`] from its components.
37///
38/// # Example
39///
40/// ```
41/// # use dicom_core::value::person_name::{PersonName, PersonNameBuilder};
42/// let ivan: PersonName = PersonNameBuilder::new()
43///     .with_given("Ivan")
44///     .with_family("Levanov")
45///     .build();
46/// assert_eq!(&ivan.to_dicom_string(), "Levanov^Ivan");
47/// ```
48#[derive(Debug, Clone)]
49pub struct PersonNameBuilder<'a> {
50    person_name: PersonName<'a>,
51}
52
53impl Display for PersonName<'_> {
54    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
55        let components = &[
56            &self.prefix,
57            &self.given,
58            &self.middle,
59            &self.family,
60            &self.suffix,
61        ];
62
63        let mut c_iter = components.iter().copied().flatten().peekable();
64
65        while let Some(component) = c_iter.next() {
66            if c_iter.peek().is_some() {
67                write!(f, "{component} ")?
68            } else {
69                write!(f, "{component}")?
70            }
71        }
72        Ok(())
73    }
74}
75
76impl<'a> PersonName<'a> {
77    /// Retrieve PersonName prefix
78    pub fn prefix(&self) -> Option<&str> {
79        self.prefix.as_deref()
80    }
81    /// Retrieve PersonName suffix
82    pub fn suffix(&self) -> Option<&str> {
83        self.suffix.as_deref()
84    }
85    /// Retrieve family name from PersonName
86    pub fn family(&self) -> Option<&str> {
87        self.family.as_deref()
88    }
89    /// Retrieve given name from PersonName
90    pub fn given(&self) -> Option<&str> {
91        self.given.as_deref()
92    }
93    /// Retrieve middle name from PersonName
94    pub fn middle(&self) -> Option<&str> {
95        self.middle.as_deref()
96    }
97    /// Convert the person name into a DICOM formatted string.
98    ///
99    /// Name components are interspersed with a `'^'` separator.
100    /// Leading null components produce a separator,
101    /// while trailing components do not.
102    pub fn to_dicom_string(&self) -> String {
103        let mut name = String::new();
104
105        let components = &[
106            &self.family,
107            &self.given,
108            &self.middle,
109            &self.prefix,
110            &self.suffix,
111        ];
112
113        let mut it = components.iter().rev().peekable();
114        // consume trailing None (null) components
115        while it.next_if(|component| component.is_none()).is_some() {}
116
117        let mut it = it.rev().peekable();
118        while let Some(option) = it.next() {
119            if let Some(component) = option {
120                name.push_str(component);
121            }
122            if it.peek().is_some() {
123                name.push('^');
124            }
125        }
126
127        name
128    }
129
130    /// Obtains a person name by interpreting `slice` as a DICOM formatted string.
131    ///
132    /// The DICOM string representation is split by the `'^'` separator
133    /// into its respective components.
134    /// When passing a text value to this function,
135    /// ensure that it contains a single DICOM formatted name.
136    pub fn from_text(slice: &'a str) -> PersonName<'a> {
137        let mut parts = slice.trim().split('^');
138
139        macro_rules! get_component {
140            () => {
141                parts
142                    .next()
143                    .and_then(|s| if s.is_empty() { None } else { Some(s.into()) })
144            };
145        }
146
147        let family = get_component!();
148        let given = get_component!();
149        let middle = get_component!();
150        let prefix = get_component!();
151        let suffix = get_component!();
152
153        PersonName {
154            prefix,
155            given,
156            family,
157            middle,
158            suffix,
159        }
160    }
161
162    /// Retrieve a builder for a person name.
163    ///
164    /// See [`PersonNameBuilder`] for more information.
165    pub fn builder() -> PersonNameBuilder<'a> {
166        PersonNameBuilder::new()
167    }
168}
169
170impl<'a> PersonNameBuilder<'a> {
171    pub fn new() -> PersonNameBuilder<'a> {
172        PersonNameBuilder {
173            person_name: PersonName {
174                prefix: None,
175                family: None,
176                middle: None,
177                given: None,
178                suffix: None,
179            },
180        }
181    }
182
183    /// Insert or update the family name component.
184    pub fn with_family(&mut self, family_name: impl Into<Cow<'a, str>>) -> &mut Self {
185        self.person_name.family = Some(family_name.into());
186        self
187    }
188
189    /// Insert or update the middle name component.
190    pub fn with_middle(&mut self, middle_name: impl Into<Cow<'a, str>>) -> &mut Self {
191        self.person_name.middle = Some(middle_name.into());
192        self
193    }
194
195    /// Insert or update the given name component.
196    pub fn with_given(&mut self, given_name: impl Into<Cow<'a, str>>) -> &mut Self {
197        self.person_name.given = Some(given_name.into());
198        self
199    }
200
201    /// Insert or update the prefix component.
202    pub fn with_prefix(&mut self, name_prefix: impl Into<Cow<'a, str>>) -> &mut Self {
203        self.person_name.prefix = Some(name_prefix.into());
204        self
205    }
206
207    /// Insert or update the suffix component.
208    pub fn with_suffix(&mut self, name_suffix: impl Into<Cow<'a, str>>) -> &mut Self {
209        self.person_name.suffix = Some(name_suffix.into());
210        self
211    }
212
213    /// Builds the person name with the accumulated components.
214    ///
215    /// This operation consumes the accumulated components, resetting the builder to
216    /// its default state.
217    pub fn build(&mut self) -> PersonName<'a> {
218        mem::take(self).person_name
219    }
220}
221
222impl Default for PersonNameBuilder<'_> {
223    fn default() -> Self {
224        Self::new()
225    }
226}
227
228impl<'a> From<PersonNameBuilder<'a>> for PersonName<'a> {
229    fn from(mut builder: PersonNameBuilder<'a>) -> Self {
230        builder.build()
231    }
232}
233
234impl<'a> From<&mut PersonNameBuilder<'a>> for PersonName<'a> {
235    fn from(builder: &mut PersonNameBuilder<'a>) -> Self {
236        builder.build()
237    }
238}
239
240#[cfg(test)]
241mod tests {
242    use super::*;
243    #[test]
244    fn test_person_name_to_dicom_string() {
245        let p = PersonNameBuilder::new()
246            .with_given("John")
247            .with_family("Adams")
248            .build();
249        assert_eq!(p.to_dicom_string(), "Adams^John".to_string());
250
251        let p: PersonName = PersonNameBuilder::new().with_prefix("Rev.").into();
252        assert_eq!(p.to_dicom_string(), "^^^Rev.".to_string());
253
254        let p = PersonNameBuilder::new().with_suffix("B.A. M.Div.").build();
255        assert_eq!(p.to_dicom_string(), "^^^^B.A. M.Div.".to_string());
256        let p = PersonName {
257            prefix: Some("Rev.".into()),
258            given: Some("John".into()),
259            middle: Some("Robert".into()),
260            family: Some("Adams".into()),
261            suffix: Some("B.A. M.Div.".into()),
262        };
263        assert_eq!(
264            p.to_dicom_string(),
265            "Adams^John^Robert^Rev.^B.A. M.Div.".to_string()
266        );
267        let p = PersonName {
268            prefix: None,
269            given: Some("John".into()),
270            middle: Some("Robert".into()),
271            family: Some("Adams".into()),
272            suffix: Some("B.A. M.Div.".into()),
273        };
274        assert_eq!(
275            p.to_dicom_string(),
276            "Adams^John^Robert^^B.A. M.Div.".to_string()
277        );
278        let p = PersonName {
279            prefix: Some("Rev.".into()),
280            given: Some("John".into()),
281            middle: Some("Robert".into()),
282            family: Some("Adams".into()),
283            suffix: None,
284        };
285        assert_eq!(p.to_dicom_string(), "Adams^John^Robert^Rev.".to_string());
286        let p = PersonName {
287            prefix: None,
288            given: Some("John".into()),
289            middle: Some("Robert".into()),
290            family: Some("Adams".into()),
291            suffix: None,
292        };
293        assert_eq!(p.to_dicom_string(), "Adams^John^Robert".to_string());
294        let p = PersonName::builder().with_middle("Robert").build();
295        assert_eq!(p.to_dicom_string(), "^^Robert".to_string());
296    }
297    #[test]
298    fn test_person_name_to_string() {
299        let p = PersonName::builder()
300            .with_given("John")
301            .with_family("Adams")
302            .build();
303        assert_eq!(p.to_string(), "John Adams".to_string());
304
305        let p = PersonName::builder().with_prefix("Rev.").build();
306        assert_eq!(p.to_string(), "Rev.".to_string());
307
308        let p = PersonName::builder().with_suffix("B.A. M.Div.").build();
309        assert_eq!(p.to_string(), "B.A. M.Div.".to_string());
310        let p = PersonName {
311            prefix: Some("Rev.".into()),
312            given: Some("John".into()),
313            middle: Some("Robert".into()),
314            family: Some("Adams".into()),
315            suffix: Some("B.A. M.Div.".into()),
316        };
317        assert_eq!(
318            p.to_string(),
319            "Rev. John Robert Adams B.A. M.Div.".to_string()
320        );
321        let p = PersonName {
322            prefix: None,
323            given: Some("John".into()),
324            middle: Some("Robert".into()),
325            family: Some("Adams".into()),
326            suffix: Some("B.A. M.Div.".into()),
327        };
328        assert_eq!(p.to_string(), "John Robert Adams B.A. M.Div.".to_string());
329        let p = PersonName {
330            prefix: Some("Rev.".into()),
331            given: Some("John".into()),
332            middle: Some("Robert".into()),
333            family: Some("Adams".into()),
334            suffix: None,
335        };
336        assert_eq!(p.to_string(), "Rev. John Robert Adams".to_string());
337        let p = PersonName {
338            prefix: None,
339            given: Some("John".into()),
340            middle: Some("Robert".into()),
341            family: Some("Adams".into()),
342            suffix: None,
343        };
344        assert_eq!(p.to_string(), "John Robert Adams".to_string());
345        let p = PersonName::builder().with_middle("Robert").build();
346        assert_eq!(p.to_string(), "Robert".to_string());
347    }
348    #[test]
349    fn person_name_from_slice() {
350        assert_eq!(
351            PersonName::from_text("^^Robert"),
352            PersonName::builder().with_middle("Robert").build()
353        );
354        assert_eq!(
355            PersonName::from_text("^^^Rev."),
356            PersonName::builder().with_prefix("Rev.").build()
357        );
358        assert_eq!(
359            PersonName::from_text("^^^^B.A. M.Div."),
360            PersonName::builder().with_suffix("B.A. M.Div.").build()
361        );
362        assert_eq!(
363            PersonName::from_text("^^Robert"),
364            PersonName::builder().with_middle("Robert").build()
365        );
366        assert_eq!(
367            PersonName::from_text("^John"),
368            PersonName::builder().with_given("John").build()
369        );
370        assert_eq!(
371            PersonName::from_text("Adams"),
372            PersonName::builder().with_family("Adams").build()
373        );
374        assert_eq!(
375            PersonName::from_text("Adams^^^^B.A. M.Div."),
376            PersonName::builder()
377                .with_family("Adams")
378                .with_suffix("B.A. M.Div.")
379                .build()
380        );
381        assert_eq!(
382            PersonName::from_text("Adams^^Robert^^B.A. M.Div."),
383            PersonName {
384                prefix: None,
385                given: None,
386                middle: Some("Robert".into()),
387                family: Some("Adams".into()),
388                suffix: Some("B.A. M.Div.".into()),
389            }
390        );
391        assert_eq!(
392            PersonName::from_text("Adams^John^Robert^Rev.^B.A. M.Div."),
393            PersonName {
394                prefix: Some("Rev.".into()),
395                given: Some("John".into()),
396                middle: Some("Robert".into()),
397                family: Some("Adams".into()),
398                suffix: Some("B.A. M.Div.".into()),
399            }
400        );
401        assert_eq!(
402            PersonName::from_text("Adams^ "),
403            PersonName {
404                prefix: None,
405                given: None,
406                middle: None,
407                family: Some("Adams".into()),
408                suffix: None,
409            }
410        );
411    }
412}