libsignal_service/
models.rs1use bytes::Bytes;
2use phonenumber::PhoneNumber;
3use serde::{Deserialize, Serialize};
4use thiserror::Error;
5use uuid::Uuid;
6
7#[derive(Debug, Serialize, Deserialize)]
9pub struct Attachment<R> {
10 pub content_type: String,
11 pub reader: R,
12}
13
14#[derive(Debug, Serialize, Deserialize)]
18pub struct Contact {
19 pub uuid: Uuid,
20 pub phone_number: Option<PhoneNumber>,
21 pub name: String,
22 pub expire_timer: u32,
23 pub expire_timer_version: u32,
24 pub inbox_position: u32,
25 #[serde(skip)]
26 pub avatar: Option<Attachment<Bytes>>,
27}
28
29#[derive(Error, Debug)]
30pub enum ParseContactError {
31 #[error(transparent)]
32 Protobuf(#[from] prost::DecodeError),
33 #[error(transparent)]
34 Uuid(#[from] uuid::Error),
35 #[error("missing UUID")]
36 MissingUuid,
37 #[error("missing profile key")]
38 MissingProfileKey,
39 #[error("missing avatar content-type")]
40 MissingAvatarContentType,
41}
42
43impl Contact {
44 pub fn from_proto(
45 contact_details: crate::proto::ContactDetails,
46 avatar_data: Option<Bytes>,
47 ) -> Result<Self, ParseContactError> {
48 Ok(Self {
49 uuid: contact_details
50 .aci
51 .as_ref()
52 .ok_or(ParseContactError::MissingUuid)?
53 .parse()?,
54 phone_number: contact_details
55 .number
56 .as_ref()
57 .and_then(|n| phonenumber::parse(None, n).ok()),
58 name: contact_details.name().into(),
59 expire_timer: contact_details.expire_timer(),
60 expire_timer_version: contact_details.expire_timer_version(),
61 inbox_position: contact_details.inbox_position(),
62 avatar: contact_details.avatar.and_then(|avatar| {
63 if let (Some(content_type), Some(avatar_data)) =
64 (avatar.content_type, avatar_data)
65 {
66 Some(Attachment {
67 content_type,
68 reader: avatar_data,
69 })
70 } else {
71 tracing::warn!("missing avatar content-type, skipping.");
72 None
73 }
74 }),
75 })
76 }
77}