libsignal_service/
profile_name.rs

1use std::fmt::Display;
2
3use serde::{Deserialize, Serialize};
4
5#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
6pub struct ProfileName<S> {
7    pub given_name: S,
8    pub family_name: Option<S>,
9}
10
11// Implements ProfileName::getJoinedName
12impl<S: AsRef<str>> Display for ProfileName<S> {
13    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
14        let given_name = self.given_name.as_ref();
15        match (
16            self.given_name.as_ref().is_empty(),
17            self.family_name.as_ref().map(AsRef::as_ref),
18        ) {
19            (true, None) => Ok(()),
20            (false, None) => write!(f, "{}", given_name),
21            (true, Some(family_name)) => write!(f, "{}", family_name),
22            (false, Some(family_name)) => {
23                if self.is_cjkv() {
24                    write!(f, "{} {}", family_name, given_name)
25                } else {
26                    write!(f, "{} {}", given_name, family_name)
27                }
28            },
29        }
30    }
31}
32
33impl<S: AsRef<str>> ProfileName<S> {
34    pub fn as_ref(&self) -> ProfileName<&str> {
35        ProfileName {
36            given_name: self.given_name.as_ref(),
37            family_name: self.family_name.as_ref().map(|x| x.as_ref()),
38        }
39    }
40
41    pub fn is_cjkv(&self) -> bool {
42        // XXX insert unicode magic here
43        false
44    }
45
46    pub fn serialize(&self) -> Vec<u8> {
47        if let Some(family_name) = self.family_name.as_ref() {
48            self.given_name
49                .as_ref()
50                .as_bytes()
51                .iter()
52                .chain(std::iter::once(&0u8))
53                .chain(family_name.as_ref().as_bytes())
54                .copied()
55                .collect()
56        } else {
57            self.given_name.as_ref().as_bytes().into()
58        }
59    }
60
61    pub fn is_empty(&self) -> bool {
62        self.given_name.as_ref() == "" && self.family_name.is_none()
63    }
64}
65
66impl ProfileName<String> {
67    /// Copying deserialization of a ProfileName.
68    pub fn deserialize(
69        data: &[u8],
70    ) -> Result<Option<Self>, std::str::Utf8Error> {
71        let parts: Vec<&[u8]> = data.split(|x| *x == 0).collect();
72        match parts.len() {
73            0 => Ok(None),
74            1 => Ok(Some(Self {
75                given_name: std::str::from_utf8(parts[0])?.to_string(),
76                family_name: None,
77            })),
78            _ => Ok(Some(Self {
79                given_name: std::str::from_utf8(parts[0])?.to_string(),
80                family_name: Some(std::str::from_utf8(parts[1])?.to_string()),
81            })),
82        }
83    }
84}
85
86impl<'de> ProfileName<&'de str> {
87    pub fn empty() -> Self {
88        ProfileName {
89            given_name: "",
90            family_name: None,
91        }
92    }
93
94    /// Zero-copy deserialization of a ProfileName.
95    pub fn deserialize<'inp: 'de>(
96        data: &'inp [u8],
97    ) -> Result<Option<Self>, std::str::Utf8Error> {
98        let parts: Vec<&[u8]> = data.split(|x| *x == 0).collect();
99        match parts.len() {
100            0 => Ok(None),
101            1 => Ok(Some(Self {
102                given_name: std::str::from_utf8(parts[0])?,
103                family_name: None,
104            })),
105            _ => Ok(Some(Self {
106                given_name: std::str::from_utf8(parts[0])?,
107                family_name: Some(std::str::from_utf8(parts[1])?),
108            })),
109        }
110    }
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116
117    #[test]
118    fn roundtrip_name() {
119        let names = [
120            ("foo", Some("bar")),
121            ("foo", None),
122            ("", None),
123            ("", Some("bar")),
124        ];
125
126        for &(given_name, family_name) in &names {
127            let uut_name = ProfileName::<&str> {
128                given_name,
129                family_name,
130            };
131            let ser = uut_name.serialize();
132            let deserialized =
133                ProfileName::<&str>::deserialize(&ser).expect("utf8");
134            assert_eq!(Some(uut_name), deserialized);
135        }
136    }
137}