1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#[cfg(feature = "read_write")]
use crate::read_write::component::Component;
#[cfg(feature = "read_write")]
use crate::FromComponentError;

/// The components correspond to the sex (biological) and gender identity.
#[derive(Debug, Clone, PartialEq, PartialOrd, Default)]
pub struct Gender {
    /// The biological sex of the contact.
    pub sex: Option<Sex>,
    /// The gender identity of the contact. Free-form as
    /// to allow it to be set to whatever gender the
    /// contact prefers.
    ///
    /// As a side not sorry for using the term identity,
    /// I know its not correct but I am trying to keep
    /// both backwards compatability and compatability with
    /// the standard. It should be `gender` but oh well.
    pub identity: Option<String>,
}

/// A sex component, representing a biological sex.
#[derive(Debug, Clone, PartialEq, PartialOrd, Copy)]
pub enum Sex {
    /// Male sex, XY chromosomes
    Male,
    /// Female sex, XX chromosomes
    Female,
    /// Intersex - ambigouous sex
    Other,
    /// Unnknown, probably should just be represented
    /// by a `None` value on [`Gender`] anyway.
    Unknown,
}

impl From<Sex> for char {
    fn from(s: Sex) -> Self {
        match s {
            Sex::Male => 'M',
            Sex::Female => 'F',
            Sex::Other => 'O',
            Sex::Unknown => 'U',
        }
    }
}

#[cfg(feature = "read_write")]
impl From<Gender> for Component {
    fn from(gender: Gender) -> Self {
        Self {
            name: "GENDER".to_string(),
            values: {
                let mut vec = Vec::with_capacity(2);
                vec.push(
                    vec![
                        gender.sex.map_or('N', char::from).to_string(),
                    ],
                );
                if let Some(identity) = gender.identity {
                    vec.push(vec![identity]);
                }
                vec
            },
            ..Self::default()
        }
    }
}

#[cfg(feature = "read_write")]
impl TryFrom<Component> for Gender {
    type Error = FromComponentError;

    fn try_from(mut comp: Component) -> Result<Self, Self::Error> {
        Ok(Self {
            sex: match comp
                    .values
                    .get_mut(0)
                    .and_then(Vec::pop).as_deref() {
                        None | Some("N") => None,
                        Some("M") => Some(Sex::Male),
                        Some("F") => Some(Sex::Female),
                        Some("O") => Some(Sex::Other),
                        Some("U") => Some(Sex::Unknown),
                        _ => return Err(FromComponentError::InvalidRegex),
                    },
            identity: comp
                .values
                .get_mut(1)
                .and_then(Vec::pop),
        })
    }
}