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