libsignal_service/
models.rs1use std::convert::TryInto;
2
3use crate::proto::Verified;
4
5use bytes::Bytes;
6use phonenumber::PhoneNumber;
7use serde::{Deserialize, Serialize};
8use thiserror::Error;
9use uuid::Uuid;
10use zkgroup::profiles::ProfileKey;
11
12#[derive(Debug, Serialize, Deserialize)]
14pub struct Attachment<R> {
15 pub content_type: String,
16 pub reader: R,
17}
18
19#[derive(Debug, Serialize, Deserialize)]
23pub struct Contact {
24 pub uuid: Uuid,
25 pub phone_number: Option<PhoneNumber>,
26 pub name: String,
27 pub color: Option<String>,
28 #[serde(skip)]
29 pub verified: Verified,
30 pub profile_key: Vec<u8>,
31 pub expire_timer: u32,
32 pub expire_timer_version: u32,
33 pub inbox_position: u32,
34 pub archived: bool,
35 #[serde(skip)]
36 pub avatar: Option<Attachment<Bytes>>,
37}
38
39#[derive(Error, Debug)]
40pub enum ParseContactError {
41 #[error(transparent)]
42 Protobuf(#[from] prost::DecodeError),
43 #[error(transparent)]
44 Uuid(#[from] uuid::Error),
45 #[error("missing UUID")]
46 MissingUuid,
47 #[error("missing profile key")]
48 MissingProfileKey,
49 #[error("missing avatar content-type")]
50 MissingAvatarContentType,
51}
52
53impl Contact {
54 pub fn from_proto(
55 contact_details: crate::proto::ContactDetails,
56 avatar_data: Option<Bytes>,
57 ) -> Result<Self, ParseContactError> {
58 Ok(Self {
59 uuid: contact_details
60 .aci
61 .as_ref()
62 .ok_or(ParseContactError::MissingUuid)?
63 .parse()?,
64 phone_number: contact_details
65 .number
66 .as_ref()
67 .and_then(|n| phonenumber::parse(None, n).ok()),
68 name: contact_details.name().into(),
69 color: contact_details.color.clone(),
70 verified: contact_details.verified.clone().unwrap_or_default(),
71 profile_key: contact_details.profile_key().to_vec(),
72 expire_timer: contact_details.expire_timer(),
73 expire_timer_version: contact_details.expire_timer_version(),
74 inbox_position: contact_details.inbox_position(),
75 archived: contact_details.archived(),
76 avatar: contact_details.avatar.and_then(|avatar| {
77 if let (Some(content_type), Some(avatar_data)) =
78 (avatar.content_type, avatar_data)
79 {
80 Some(Attachment {
81 content_type,
82 reader: avatar_data,
83 })
84 } else {
85 tracing::warn!("missing avatar content-type, skipping.");
86 None
87 }
88 }),
89 })
90 }
91
92 pub fn profile_key(&self) -> Result<ProfileKey, ParseContactError> {
93 Ok(ProfileKey::create(
94 self.profile_key
95 .clone()
96 .try_into()
97 .map_err(|_| ParseContactError::MissingProfileKey)?,
98 ))
99 }
100}