mirror of
https://github.com/signalapp/libsignal.git
synced 2024-09-19 19:42:19 +02:00
backup: Make message backup types serializable
This commit is contained in:
parent
07801c8153
commit
784164d4bb
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -2171,6 +2171,7 @@ dependencies = [
|
||||
"protobuf",
|
||||
"protobuf-codegen",
|
||||
"protobuf-json-mapping",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"signal-crypto",
|
||||
@ -4518,6 +4519,7 @@ version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"sha1_smol",
|
||||
]
|
||||
|
||||
|
@ -40,7 +40,7 @@ derive-where = "1.2.5"
|
||||
displaydoc = "0.2.5"
|
||||
env_logger = "0.10.0"
|
||||
futures = "0.3.29"
|
||||
hex = "0.4.3"
|
||||
hex = { version = "0.4.3", features = ["serde"] }
|
||||
hkdf = "0.12"
|
||||
hmac = "0.12"
|
||||
itertools = "0.12.0"
|
||||
@ -50,6 +50,7 @@ mediasan-common = "0.5.0"
|
||||
num_enum = "0.6.1"
|
||||
protobuf = "3.3.0"
|
||||
protobuf-json-mapping = { version = "3.3.0", optional = true }
|
||||
serde = { version = "1.0", features = ["derive", "rc"] }
|
||||
serde_json = { version = "1.0", optional = true }
|
||||
sha2 = "0.10"
|
||||
smallvec = "1.13.2"
|
||||
@ -60,7 +61,7 @@ strum = { version = "0.26", features = ["derive"] }
|
||||
strum_macros = { version = "0.26.4" }
|
||||
subtle = "2.5.0"
|
||||
thiserror = "1.0.50"
|
||||
uuid = "1.1.2"
|
||||
uuid = { version = "1.1.2", features = ["serde"] }
|
||||
|
||||
[dev-dependencies]
|
||||
libsignal-message-backup = { path = "./", features = ["json"] }
|
||||
|
@ -31,6 +31,7 @@ mod file;
|
||||
mod frame;
|
||||
pub(crate) mod method;
|
||||
mod recipient;
|
||||
mod serialize;
|
||||
mod sticker;
|
||||
mod time;
|
||||
|
||||
@ -38,12 +39,12 @@ pub trait ReferencedTypes {
|
||||
/// Recorded information from a [`proto::Recipient`].
|
||||
type RecipientData: Debug + AsRef<DestinationKind>;
|
||||
/// Resolved data for a [`RecipientId`] in a non-`proto::Recipient` message.
|
||||
type RecipientReference: Clone + Debug;
|
||||
type RecipientReference: Clone + Debug + serde::Serialize;
|
||||
|
||||
/// Recorded information from a [`proto::chat_style::CustomChatColor`].
|
||||
type CustomColorData: Debug + From<CustomChatColor>;
|
||||
type CustomColorData: Debug + From<CustomChatColor> + serde::Serialize;
|
||||
/// Resolved data for a [`CustomColorId`] in a non-`CustomChatColor` message.
|
||||
type CustomColorReference: Clone + Debug;
|
||||
type CustomColorReference: Clone + Debug + serde::Serialize;
|
||||
|
||||
fn color_reference<'a>(
|
||||
id: &'a CustomColorId,
|
||||
@ -98,7 +99,7 @@ struct ChatsData<M: Method + ReferencedTypes> {
|
||||
pub chat_items_count: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
pub struct Backup {
|
||||
pub meta: BackupMeta,
|
||||
pub account_data: AccountData<Store>,
|
||||
@ -108,7 +109,7 @@ pub struct Backup {
|
||||
pub sticker_packs: HashMap<StickerPackId, StickerPack<Store>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
pub struct BackupMeta {
|
||||
/// The version of the backup format being parsed.
|
||||
pub version: u64,
|
||||
@ -128,6 +129,7 @@ pub struct BackupMeta {
|
||||
strum::EnumString,
|
||||
strum::Display,
|
||||
strum::IntoStaticStr,
|
||||
serde::Serialize,
|
||||
)]
|
||||
pub enum Purpose {
|
||||
/// Intended for immediate transfer from one device to another.
|
||||
|
@ -15,10 +15,11 @@ use zkgroup::ProfileKeyBytes;
|
||||
use crate::backup::chat::chat_style::{ChatStyle, ChatStyleError, CustomColorMap};
|
||||
use crate::backup::method::Method;
|
||||
use crate::backup::time::Duration;
|
||||
use crate::backup::{ReferencedTypes, TryIntoWith as _};
|
||||
use crate::backup::{serialize, ReferencedTypes, TryIntoWith as _};
|
||||
use crate::proto::backup as proto;
|
||||
|
||||
#[derive_where(Debug)]
|
||||
#[derive(serde::Serialize)]
|
||||
#[cfg_attr(test, derive_where(PartialEq;
|
||||
M::Value<ProfileKeyBytes>: PartialEq,
|
||||
M::Value<Option<UsernameData>>: PartialEq,
|
||||
@ -27,6 +28,10 @@ use crate::proto::backup as proto;
|
||||
AccountSettings<M>: PartialEq,
|
||||
))]
|
||||
pub struct AccountData<M: Method + ReferencedTypes> {
|
||||
#[serde(
|
||||
with = "hex",
|
||||
bound(serialize = "M::Value<ProfileKeyBytes>: AsRef<[u8]>")
|
||||
)]
|
||||
pub profile_key: M::Value<ProfileKeyBytes>,
|
||||
pub username: M::Value<Option<UsernameData>>,
|
||||
pub given_name: M::Value<String>,
|
||||
@ -37,24 +42,29 @@ pub struct AccountData<M: Method + ReferencedTypes> {
|
||||
pub backup_subscription: M::Value<Option<Subscription>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct UsernameData {
|
||||
#[serde(serialize_with = "serialize::to_string")]
|
||||
pub username: Username,
|
||||
pub link: Option<UsernameLink>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct UsernameLink {
|
||||
#[serde(serialize_with = "serialize::enum_as_string")]
|
||||
pub color: crate::proto::backup::account_data::username_link::Color,
|
||||
#[serde(serialize_with = "hex::serialize")]
|
||||
pub entropy: [u8; USERNAME_LINK_ENTROPY_SIZE],
|
||||
#[serde(serialize_with = "serialize::to_string")]
|
||||
pub server_id: Uuid,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct Subscription {
|
||||
#[serde(with = "hex")]
|
||||
pub subscriber_id: SubscriberId,
|
||||
pub currency_code: String,
|
||||
pub manually_canceled: bool,
|
||||
@ -64,6 +74,7 @@ const SUBSCRIBER_ID_LENGTH: usize = 32;
|
||||
type SubscriberId = [u8; SUBSCRIBER_ID_LENGTH];
|
||||
|
||||
#[derive_where(Debug)]
|
||||
#[derive(serde::Serialize)]
|
||||
#[cfg_attr(test, derive_where(PartialEq;
|
||||
M::Value<PhoneSharing>: PartialEq,
|
||||
M::Value<Option<bool>>: PartialEq,
|
||||
@ -95,7 +106,7 @@ pub struct AccountSettings<M: Method + ReferencedTypes> {
|
||||
pub custom_chat_colors: CustomColorMap<M>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, serde::Serialize)]
|
||||
pub enum PhoneSharing {
|
||||
WithEverybody,
|
||||
WithNobody,
|
||||
|
@ -11,7 +11,7 @@ use crate::backup::TryFromWith;
|
||||
use crate::proto::backup as proto;
|
||||
|
||||
/// Validated version of [`proto::AdHocCall`].
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct AdHocCall<Recipient> {
|
||||
pub id: CallId,
|
||||
@ -20,7 +20,7 @@ pub struct AdHocCall<Recipient> {
|
||||
}
|
||||
|
||||
/// Validated version of [`proto::IndividualCall`].
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct IndividualCall {
|
||||
pub id: Option<CallId>,
|
||||
@ -31,7 +31,7 @@ pub struct IndividualCall {
|
||||
}
|
||||
|
||||
/// Validated version of [`proto::GroupCall`].
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct GroupCall<Recipient> {
|
||||
pub id: Option<CallId>,
|
||||
@ -45,7 +45,7 @@ pub struct GroupCall<Recipient> {
|
||||
///
|
||||
/// This is not referenced as a foreign key from elsewhere in a backup, but
|
||||
/// corresponds to shared state across conversation members for a given call.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize)]
|
||||
pub struct CallId(u64);
|
||||
|
||||
#[derive(Debug, displaydoc::Display, thiserror::Error)]
|
||||
@ -78,14 +78,14 @@ pub enum CallLinkError {
|
||||
InvalidRootKey(usize),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub enum CallType {
|
||||
Audio,
|
||||
Video,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub enum IndividualCallState {
|
||||
Accepted,
|
||||
@ -94,7 +94,7 @@ pub enum IndividualCallState {
|
||||
Missed,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub enum GroupCallState {
|
||||
/// No ring
|
||||
@ -122,7 +122,7 @@ const CALL_LINK_ROOT_KEY_LEN: usize = 16;
|
||||
type CallLinkRootKey = [u8; CALL_LINK_ROOT_KEY_LEN];
|
||||
|
||||
/// Validated version of [`proto::CallLink`].
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct CallLink {
|
||||
pub admin_approval: bool,
|
||||
|
@ -139,6 +139,7 @@ pub struct InvalidExpiration {
|
||||
|
||||
/// Validated version of [`proto::Chat`].
|
||||
#[derive_where(Debug)]
|
||||
#[derive(serde::Serialize)]
|
||||
#[cfg_attr(test, derive_where(PartialEq;
|
||||
M::List<ChatItemData<M>>: PartialEq,
|
||||
M::RecipientReference: PartialEq,
|
||||
@ -146,6 +147,7 @@ pub struct InvalidExpiration {
|
||||
))]
|
||||
pub struct ChatData<M: Method + ReferencedTypes> {
|
||||
pub recipient: M::RecipientReference,
|
||||
#[serde(bound(serialize = "M::List<ChatItemData<M>>: serde::Serialize"))]
|
||||
pub items: M::List<ChatItemData<M>>,
|
||||
pub expiration_timer: Option<Duration>,
|
||||
pub mute_until: Option<Timestamp>,
|
||||
@ -156,18 +158,21 @@ pub struct ChatData<M: Method + ReferencedTypes> {
|
||||
pub archived: bool,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, serde::Serialize)]
|
||||
pub struct PinOrder(NonZeroU32);
|
||||
|
||||
/// Validated version of [`proto::ChatItem`].
|
||||
#[derive_where(Debug)]
|
||||
#[derive(serde::Serialize)]
|
||||
#[cfg_attr(test, derive_where(PartialEq;
|
||||
ChatItemMessage<M>: PartialEq,
|
||||
M::RecipientReference: PartialEq
|
||||
))]
|
||||
pub struct ChatItemData<M: Method + ReferencedTypes> {
|
||||
pub author: M::RecipientReference,
|
||||
#[serde(bound(serialize = "ChatItemMessage<M>: serde::Serialize"))]
|
||||
pub message: ChatItemMessage<M>,
|
||||
// This could be Self: Serialize but that just confuses the compiler.
|
||||
pub revisions: Vec<ChatItemData<M>>,
|
||||
pub direction: Direction,
|
||||
pub expire_start: Option<Timestamp>,
|
||||
@ -182,6 +187,7 @@ pub struct ChatItemData<M: Method + ReferencedTypes> {
|
||||
|
||||
/// Validated version of [`proto::chat_item::Item`].
|
||||
#[derive_where(Debug)]
|
||||
#[derive(serde::Serialize)]
|
||||
#[cfg_attr(test, derive_where(PartialEq;
|
||||
M::BoxedValue<GiftBadge>: PartialEq,
|
||||
M::RecipientReference: PartialEq
|
||||
@ -194,11 +200,12 @@ pub enum ChatItemMessage<M: Method + ReferencedTypes> {
|
||||
RemoteDeleted,
|
||||
Update(UpdateMessage<M::RecipientReference>),
|
||||
PaymentNotification(PaymentNotification),
|
||||
#[serde(bound(serialize = "M::BoxedValue<GiftBadge>: serde::Serialize"))]
|
||||
GiftBadge(M::BoxedValue<GiftBadge>),
|
||||
}
|
||||
|
||||
/// Validated version of [`proto::Reaction`].
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct Reaction {
|
||||
pub emoji: String,
|
||||
@ -218,7 +225,7 @@ pub enum ReactionError {
|
||||
EmptyEmoji,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub enum Direction {
|
||||
Incoming {
|
||||
@ -231,7 +238,7 @@ pub enum Direction {
|
||||
Directionless,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct OutgoingSend {
|
||||
pub recipient: RecipientId,
|
||||
@ -242,7 +249,7 @@ pub struct OutgoingSend {
|
||||
pub sealed_sender: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub enum DeliveryStatus {
|
||||
Failed,
|
||||
|
@ -8,9 +8,10 @@ use derive_where::derive_where;
|
||||
use itertools::Itertools as _;
|
||||
|
||||
use crate::backup::method::{Contains, Lookup};
|
||||
use crate::backup::{ReferencedTypes, TryFromWith, TryIntoWith as _};
|
||||
use crate::backup::{serialize, ReferencedTypes, TryFromWith, TryIntoWith as _};
|
||||
use crate::proto::backup as proto;
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
#[derive_where(Debug)]
|
||||
#[cfg_attr(test, derive_where(PartialEq; M::CustomColorReference: PartialEq))]
|
||||
pub struct ChatStyle<M: ReferencedTypes> {
|
||||
@ -18,26 +19,27 @@ pub struct ChatStyle<M: ReferencedTypes> {
|
||||
pub bubble_color: BubbleColor<M::CustomColorReference>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub enum Wallpaper {
|
||||
Preset(WallpaperPreset),
|
||||
Photo,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct Color(#[allow(unused)] u32);
|
||||
#[serde(transparent)]
|
||||
pub struct Color(u32);
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct WallpaperPreset {
|
||||
/// Guaranteed to not be [`proto::chat_style::WallpaperPreset::UNKNOWN_WALLPAPER_PRESET`].
|
||||
#[allow(unused)]
|
||||
#[serde(serialize_with = "serialize::enum_as_string")]
|
||||
enum_value: proto::chat_style::WallpaperPreset,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub enum BubbleColor<CustomColor> {
|
||||
Preset(BubbleColorPreset),
|
||||
@ -45,7 +47,7 @@ pub enum BubbleColor<CustomColor> {
|
||||
Auto,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct BubbleGradientColor {
|
||||
pub color: Color,
|
||||
@ -54,18 +56,19 @@ pub struct BubbleGradientColor {
|
||||
position: f32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct BubbleColorPreset {
|
||||
/// Guaranteed to not be [`proto::chat_style::BubbleColorPreset::UNKNOWN_BUBBLE_COLOR_PRESET`].
|
||||
#[allow(unused)]
|
||||
#[serde(serialize_with = "serialize::enum_as_string")]
|
||||
enum_value: proto::chat_style::BubbleColorPreset,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, serde::Serialize)]
|
||||
pub struct CustomColorId(pub(crate) u32);
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub enum CustomChatColor {
|
||||
Gradient {
|
||||
@ -81,6 +84,7 @@ pub enum CustomChatColor {
|
||||
///
|
||||
/// Uses a `Vec` internally since the list is expected to be small.
|
||||
#[derive_where(Debug, Default)]
|
||||
#[derive(serde::Serialize)]
|
||||
#[cfg_attr(test, derive_where(PartialEq; M::CustomColorData: PartialEq))]
|
||||
pub struct CustomColorMap<M: ReferencedTypes>(Vec<(CustomColorId, M::CustomColorData)>);
|
||||
|
||||
|
@ -11,7 +11,7 @@ use crate::backup::{TryFromWith, TryIntoWith as _};
|
||||
use crate::proto::backup as proto;
|
||||
|
||||
/// Validated version of [`proto::ContactMessage`].
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct ContactMessage {
|
||||
pub contacts: Vec<ContactAttachment>,
|
||||
@ -20,7 +20,7 @@ pub struct ContactMessage {
|
||||
}
|
||||
|
||||
/// Validated version of [`proto::ContactAttachment`].
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct ContactAttachment {
|
||||
pub name: Option<proto::contact_attachment::Name>,
|
||||
@ -28,6 +28,7 @@ pub struct ContactAttachment {
|
||||
pub email: Vec<proto::contact_attachment::Email>,
|
||||
pub address: Vec<proto::contact_attachment::PostalAddress>,
|
||||
pub organization: Option<String>,
|
||||
#[serde(skip)]
|
||||
_limit_construction_to_module: (),
|
||||
}
|
||||
|
||||
|
@ -6,10 +6,13 @@
|
||||
use zkgroup::receipts::ReceiptCredentialPresentation;
|
||||
use zkgroup::ZkGroupDeserializationFailure;
|
||||
|
||||
use crate::backup::serialize;
|
||||
use crate::proto::backup as proto;
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
pub struct GiftBadge {
|
||||
receipt_credential_presentation: ReceiptCredentialPresentation,
|
||||
#[serde(serialize_with = "serialize::enum_as_string")]
|
||||
state: proto::gift_badge::State,
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@ use macro_rules_attribute::macro_rules_derive;
|
||||
use protobuf::{EnumOrUnknown, Message};
|
||||
|
||||
use crate::backup::time::Duration;
|
||||
use crate::backup::uuid_bytes_to_aci;
|
||||
use crate::backup::{serialize, uuid_bytes_to_aci};
|
||||
use crate::proto::backup::{
|
||||
self as proto, group_invitation_revoked_update, GenericGroupUpdate, GroupAdminStatusUpdate,
|
||||
GroupAnnouncementOnlyChangeUpdate, GroupAttributesAccessLevelChangeUpdate, GroupAvatarUpdate,
|
||||
@ -45,7 +45,10 @@ macro_rules! TryFromProto {
|
||||
($( #[$attrs:meta] )*
|
||||
pub enum GroupChatUpdate { $(
|
||||
$VariantName:ident $({
|
||||
$($field:ident : $typ:ty,)*
|
||||
$(
|
||||
$(#[$_attr: meta])*
|
||||
$field:ident : $typ:ty,
|
||||
)*
|
||||
})?,
|
||||
)* }) => {
|
||||
// Expand using the next match for each enum variant.
|
||||
@ -82,117 +85,152 @@ macro_rules! TryFromProto {
|
||||
|
||||
/// Validated version of [`proto::group_change_chat_update::update::Update`].
|
||||
#[allow(clippy::enum_variant_names, non_snake_case)] // names taken from proto message.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[macro_rules_derive(TryFromProto)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub enum GroupChatUpdate {
|
||||
GenericGroupUpdate {
|
||||
#[serde(serialize_with = "serialize::optional_service_id_as_string")]
|
||||
updaterAci: Option<Aci>,
|
||||
},
|
||||
GroupCreationUpdate {
|
||||
#[serde(serialize_with = "serialize::optional_service_id_as_string")]
|
||||
updaterAci: Option<Aci>,
|
||||
},
|
||||
GroupNameUpdate {
|
||||
#[serde(serialize_with = "serialize::optional_service_id_as_string")]
|
||||
updaterAci: Option<Aci>,
|
||||
newGroupName: NoValidation<Option<String>>,
|
||||
},
|
||||
GroupAvatarUpdate {
|
||||
#[serde(serialize_with = "serialize::optional_service_id_as_string")]
|
||||
updaterAci: Option<Aci>,
|
||||
wasRemoved: NoValidation<bool>,
|
||||
},
|
||||
GroupDescriptionUpdate {
|
||||
#[serde(serialize_with = "serialize::optional_service_id_as_string")]
|
||||
updaterAci: Option<Aci>,
|
||||
newDescription: NoValidation<Option<String>>,
|
||||
},
|
||||
GroupMembershipAccessLevelChangeUpdate {
|
||||
#[serde(serialize_with = "serialize::optional_service_id_as_string")]
|
||||
updaterAci: Option<Aci>,
|
||||
accessLevel: AccessLevel,
|
||||
},
|
||||
GroupAttributesAccessLevelChangeUpdate {
|
||||
#[serde(serialize_with = "serialize::optional_service_id_as_string")]
|
||||
updaterAci: Option<Aci>,
|
||||
accessLevel: AccessLevel,
|
||||
},
|
||||
GroupAnnouncementOnlyChangeUpdate {
|
||||
#[serde(serialize_with = "serialize::optional_service_id_as_string")]
|
||||
updaterAci: Option<Aci>,
|
||||
isAnnouncementOnly: NoValidation<bool>,
|
||||
},
|
||||
GroupAdminStatusUpdate {
|
||||
#[serde(serialize_with = "serialize::optional_service_id_as_string")]
|
||||
updaterAci: Option<Aci>,
|
||||
#[serde(serialize_with = "serialize::service_id_as_string")]
|
||||
memberAci: Aci,
|
||||
wasAdminStatusGranted: NoValidation<bool>,
|
||||
},
|
||||
GroupMemberLeftUpdate {
|
||||
#[serde(serialize_with = "serialize::service_id_as_string")]
|
||||
aci: Aci,
|
||||
},
|
||||
GroupMemberRemovedUpdate {
|
||||
#[serde(serialize_with = "serialize::optional_service_id_as_string")]
|
||||
removerAci: Option<Aci>,
|
||||
#[serde(serialize_with = "serialize::service_id_as_string")]
|
||||
removedAci: Aci,
|
||||
},
|
||||
SelfInvitedToGroupUpdate {
|
||||
#[serde(serialize_with = "serialize::optional_service_id_as_string")]
|
||||
inviterAci: Option<Aci>,
|
||||
},
|
||||
SelfInvitedOtherUserToGroupUpdate {
|
||||
#[serde(serialize_with = "serialize::service_id_as_string")]
|
||||
inviteeServiceId: ServiceId,
|
||||
},
|
||||
GroupUnknownInviteeUpdate {
|
||||
#[serde(serialize_with = "serialize::optional_service_id_as_string")]
|
||||
inviterAci: Option<Aci>,
|
||||
inviteeCount: NoValidation<u32>,
|
||||
},
|
||||
GroupInvitationAcceptedUpdate {
|
||||
#[serde(serialize_with = "serialize::optional_service_id_as_string")]
|
||||
inviterAci: Option<Aci>,
|
||||
#[serde(serialize_with = "serialize::service_id_as_string")]
|
||||
newMemberAci: Aci,
|
||||
},
|
||||
GroupInvitationDeclinedUpdate {
|
||||
#[serde(serialize_with = "serialize::optional_service_id_as_string")]
|
||||
inviterAci: Option<Aci>,
|
||||
#[serde(serialize_with = "serialize::optional_service_id_as_string")]
|
||||
inviteeAci: Option<Aci>,
|
||||
},
|
||||
GroupMemberJoinedUpdate {
|
||||
#[serde(serialize_with = "serialize::service_id_as_string")]
|
||||
newMemberAci: Aci,
|
||||
},
|
||||
GroupMemberAddedUpdate {
|
||||
#[serde(serialize_with = "serialize::optional_service_id_as_string")]
|
||||
updaterAci: Option<Aci>,
|
||||
#[serde(serialize_with = "serialize::service_id_as_string")]
|
||||
newMemberAci: Aci,
|
||||
#[serde(serialize_with = "serialize::optional_service_id_as_string")]
|
||||
inviterAci: Option<Aci>,
|
||||
// TODO check that this field doesn't affect the validity of other fields in
|
||||
// the message.
|
||||
hadOpenInvitation: NoValidation<bool>,
|
||||
},
|
||||
GroupSelfInvitationRevokedUpdate {
|
||||
#[serde(serialize_with = "serialize::optional_service_id_as_string")]
|
||||
revokerAci: Option<Aci>,
|
||||
},
|
||||
|
||||
GroupInvitationRevokedUpdate {
|
||||
#[serde(serialize_with = "serialize::optional_service_id_as_string")]
|
||||
updaterAci: Option<Aci>,
|
||||
invitees: Vec<Invitee>,
|
||||
},
|
||||
GroupJoinRequestUpdate {
|
||||
#[serde(serialize_with = "serialize::service_id_as_string")]
|
||||
requestorAci: Aci,
|
||||
},
|
||||
GroupJoinRequestApprovalUpdate {
|
||||
#[serde(serialize_with = "serialize::service_id_as_string")]
|
||||
requestorAci: Aci,
|
||||
#[serde(serialize_with = "serialize::optional_service_id_as_string")]
|
||||
updaterAci: Option<Aci>,
|
||||
wasApproved: NoValidation<bool>,
|
||||
},
|
||||
GroupJoinRequestCanceledUpdate {
|
||||
#[serde(serialize_with = "serialize::service_id_as_string")]
|
||||
requestorAci: Aci,
|
||||
},
|
||||
GroupInviteLinkResetUpdate {
|
||||
#[serde(serialize_with = "serialize::optional_service_id_as_string")]
|
||||
updaterAci: Option<Aci>,
|
||||
},
|
||||
|
||||
GroupInviteLinkEnabledUpdate {
|
||||
#[serde(serialize_with = "serialize::optional_service_id_as_string")]
|
||||
updaterAci: Option<Aci>,
|
||||
linkRequiresAdminApproval: NoValidation<bool>,
|
||||
},
|
||||
|
||||
GroupInviteLinkAdminApprovalUpdate {
|
||||
#[serde(serialize_with = "serialize::optional_service_id_as_string")]
|
||||
updaterAci: Option<Aci>,
|
||||
linkRequiresAdminApproval: NoValidation<bool>,
|
||||
},
|
||||
GroupInviteLinkDisabledUpdate {
|
||||
#[serde(serialize_with = "serialize::optional_service_id_as_string")]
|
||||
updaterAci: Option<Aci>,
|
||||
},
|
||||
GroupMemberJoinedByLinkUpdate {
|
||||
#[serde(serialize_with = "serialize::service_id_as_string")]
|
||||
newMemberAci: Aci,
|
||||
},
|
||||
GroupV2MigrationUpdate,
|
||||
@ -204,16 +242,18 @@ pub enum GroupChatUpdate {
|
||||
droppedMembersCount: NoValidation<u32>,
|
||||
},
|
||||
GroupSequenceOfRequestsAndCancelsUpdate {
|
||||
#[serde(serialize_with = "serialize::service_id_as_string")]
|
||||
requestorAci: Aci,
|
||||
count: NoValidation<u32>,
|
||||
},
|
||||
GroupExpirationTimerUpdate {
|
||||
#[serde(serialize_with = "serialize::optional_service_id_as_string")]
|
||||
updaterAci: Option<Aci>,
|
||||
expiresInMs: Duration,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub enum AccessLevel {
|
||||
Any,
|
||||
@ -221,11 +261,14 @@ pub enum AccessLevel {
|
||||
Administrator,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct Invitee {
|
||||
#[serde(serialize_with = "serialize::optional_service_id_as_string")]
|
||||
pub inviter: Option<Aci>,
|
||||
#[serde(serialize_with = "serialize::optional_service_id_as_string")]
|
||||
pub invitee_aci: Option<Aci>,
|
||||
#[serde(serialize_with = "serialize::optional_service_id_as_string")]
|
||||
pub invitee_pni: Option<Pni>,
|
||||
}
|
||||
|
||||
@ -238,8 +281,9 @@ pub struct GroupUpdateError {
|
||||
pub field_error: GroupUpdateFieldError,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[derive(Copy, Clone, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
#[serde(transparent)]
|
||||
pub struct NoValidation<T>(T);
|
||||
|
||||
#[derive(Debug, thiserror::Error, displaydoc::Display)]
|
||||
|
@ -7,7 +7,7 @@ use crate::backup::time::Timestamp;
|
||||
|
||||
use crate::proto::backup as proto;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct LinkPreview {
|
||||
pub url: String,
|
||||
|
@ -5,10 +5,11 @@
|
||||
|
||||
use std::fmt::Display;
|
||||
|
||||
use crate::backup::serialize;
|
||||
use crate::backup::time::Timestamp;
|
||||
use crate::proto::backup as proto;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct PaymentNotification {
|
||||
pub amount: Option<MobAmount>,
|
||||
@ -17,16 +18,17 @@ pub struct PaymentNotification {
|
||||
pub details: Option<TransactionDetails>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub enum TransactionDetails {
|
||||
Transaction(Transaction),
|
||||
FailedTransaction(FailedTransaction),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct Transaction {
|
||||
#[serde(serialize_with = "serialize::enum_as_string")]
|
||||
pub status: proto::payment_notification::transaction_details::transaction::Status,
|
||||
pub identification: Option<Identification>,
|
||||
pub timestamp: Option<Timestamp>,
|
||||
@ -37,20 +39,23 @@ pub struct Transaction {
|
||||
}
|
||||
|
||||
/// Wrapper around an arbitrary-precision decimal number
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
#[serde(transparent)]
|
||||
pub struct MobAmount(String);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub enum Identification {
|
||||
Sent { key_images: Vec<Vec<u8>> },
|
||||
Received { public_keys: Vec<Vec<u8>> },
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
#[serde(transparent)]
|
||||
pub struct FailedTransaction {
|
||||
#[serde(serialize_with = "serialize::enum_as_string")]
|
||||
pub reason: proto::payment_notification::transaction_details::failed_transaction::FailureReason,
|
||||
}
|
||||
|
||||
|
@ -11,17 +11,18 @@ use crate::backup::TryFromWith;
|
||||
use crate::proto::backup as proto;
|
||||
|
||||
/// Validated version of [`proto::Quote`]
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct Quote {
|
||||
pub author: RecipientId,
|
||||
pub quote_type: QuoteType,
|
||||
pub target_sent_timestamp: Option<Timestamp>,
|
||||
pub text: Option<MessageText>,
|
||||
#[serde(skip)]
|
||||
_limit_construction_to_module: (),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub enum QuoteType {
|
||||
Normal,
|
||||
|
@ -12,7 +12,7 @@ use crate::backup::{TryFromWith, TryIntoWith as _};
|
||||
use crate::proto::backup as proto;
|
||||
|
||||
/// Validated version of [`proto::StandardMessage`].
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct StandardMessage {
|
||||
pub text: Option<MessageText>,
|
||||
|
@ -10,7 +10,7 @@ use crate::backup::{TryFromWith, TryIntoWith as _};
|
||||
use crate::proto::backup as proto;
|
||||
|
||||
/// Validated version of [`proto::StickerMessage`].
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct StickerMessage {
|
||||
pub reactions: Vec<Reaction>,
|
||||
|
@ -5,18 +5,18 @@
|
||||
|
||||
use libsignal_protocol::Aci;
|
||||
|
||||
use crate::backup::uuid_bytes_to_aci;
|
||||
use crate::backup::{serialize, uuid_bytes_to_aci};
|
||||
use crate::proto::backup as proto;
|
||||
|
||||
/// Validated version of [`proto::Text`].
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct MessageText {
|
||||
pub text: String,
|
||||
pub ranges: Vec<TextRange>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct TextRange {
|
||||
pub start: Option<u32>,
|
||||
@ -24,11 +24,11 @@ pub struct TextRange {
|
||||
pub effect: TextEffect,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub enum TextEffect {
|
||||
MentionAci(Aci),
|
||||
Style(proto::body_range::Style),
|
||||
MentionAci(#[serde(serialize_with = "serialize::service_id_as_string")] Aci),
|
||||
Style(#[serde(serialize_with = "serialize::enum_as_string")] proto::body_range::Style),
|
||||
}
|
||||
|
||||
#[derive(Debug, displaydoc::Display, thiserror::Error)]
|
||||
|
@ -12,7 +12,7 @@ use crate::backup::{TryFromWith, TryIntoWith as _};
|
||||
use crate::proto::backup as proto;
|
||||
|
||||
/// Validated version of [`proto::chat_update_message::Update`].
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub enum UpdateMessage<Recipient> {
|
||||
Simple(SimpleChatUpdate),
|
||||
@ -27,7 +27,7 @@ pub enum UpdateMessage<Recipient> {
|
||||
}
|
||||
|
||||
/// Validated version of [`proto::simple_chat_update::Type`].
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub enum SimpleChatUpdate {
|
||||
JoinedSignal,
|
||||
|
@ -11,7 +11,7 @@ use crate::backup::{TryFromWith, TryIntoWith as _};
|
||||
use crate::proto::backup as proto;
|
||||
|
||||
/// Validated version of a voice message [`proto::StandardMessage`].
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct VoiceMessage {
|
||||
pub quote: Option<Quote>,
|
||||
|
@ -12,10 +12,11 @@ use uuid::Uuid;
|
||||
|
||||
use crate::proto::backup::{self as proto, FilePointer};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(Default, PartialEq))]
|
||||
pub struct VoiceMessageAttachment {
|
||||
pub client_uuid: Option<Uuid>,
|
||||
#[serde(skip)]
|
||||
_limit_construction_to_module: (),
|
||||
}
|
||||
|
||||
|
@ -6,14 +6,14 @@
|
||||
use crate::backup::WithId;
|
||||
use crate::proto::backup::{Chat, Recipient};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, serde::Serialize)]
|
||||
pub struct RecipientId(pub(super) u64);
|
||||
|
||||
/// Foreign key
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, serde::Serialize)]
|
||||
pub struct ChatId(pub(super) u64);
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, serde::Serialize)]
|
||||
pub struct RingerRecipientId(pub(super) RecipientId);
|
||||
|
||||
impl From<RingerRecipientId> for RecipientId {
|
||||
|
@ -86,16 +86,23 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Method {
|
||||
type Value<T: Debug>: Debug;
|
||||
// The `serde::Serialize` supertrait isn't needed for anything but it simplifies
|
||||
// using the `serde::Serialize` derive macro on types that are parameterized
|
||||
// over `M: Method`. The macro's heuristic assumes that all type parameters must
|
||||
// implement `serde::Serialize`. That's not correct in this case but it's
|
||||
// simpler to just roll with it since the no-op implementations can be trivially
|
||||
// derived.
|
||||
pub trait Method: serde::Serialize {
|
||||
type Value<T: Debug + serde::Serialize>: Debug + serde::Serialize;
|
||||
type BoxedValue<T: Debug>: Debug;
|
||||
type Map<K: Eq + Hash + Debug, V: Debug>: Map<K, V> + Debug;
|
||||
type List<T: Debug>: Extend<T> + Default + Debug;
|
||||
|
||||
fn value<T: Debug>(value: T) -> Self::Value<T>;
|
||||
fn value<T: Debug + serde::Serialize>(value: T) -> Self::Value<T>;
|
||||
fn boxed_value<T: Debug>(value: T) -> Self::BoxedValue<T>;
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
pub enum ValidateOnly {}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
@ -108,24 +115,25 @@ impl<T> Extend<T> for ValidateOnlyList {
|
||||
}
|
||||
|
||||
impl Method for ValidateOnly {
|
||||
type Value<T: Debug> = ();
|
||||
type Value<T: Debug + serde::Serialize> = ();
|
||||
type BoxedValue<T: Debug> = ();
|
||||
type Map<K: Eq + Hash + Debug, V: Debug> = HashSet<K>;
|
||||
type List<T: Debug> = ValidateOnlyList;
|
||||
|
||||
fn value<T: Debug>(_value: T) -> Self::Value<T> {}
|
||||
fn value<T: Debug + serde::Serialize>(_value: T) -> Self::Value<T> {}
|
||||
fn boxed_value<T: Debug>(_value: T) -> Self::BoxedValue<T> {}
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
pub enum Store {}
|
||||
|
||||
impl Method for Store {
|
||||
type Value<T: Debug> = T;
|
||||
type Value<T: Debug + serde::Serialize> = T;
|
||||
type BoxedValue<T: Debug> = Box<T>;
|
||||
type Map<K: Eq + Hash + Debug, V: Debug> = HashMap<K, V>;
|
||||
type List<T: Debug> = Vec<T>;
|
||||
|
||||
fn value<T: Debug>(value: T) -> Self::Value<T> {
|
||||
fn value<T: Debug + serde::Serialize>(value: T) -> Self::Value<T> {
|
||||
value
|
||||
}
|
||||
fn boxed_value<T: Debug>(value: T) -> Self::BoxedValue<T> {
|
||||
|
@ -17,7 +17,7 @@ use crate::backup::call::{CallLink, CallLinkError};
|
||||
use crate::backup::frame::RecipientId;
|
||||
use crate::backup::method::{LookupPair, Method, Store, ValidateOnly};
|
||||
use crate::backup::time::Timestamp;
|
||||
use crate::backup::{ReferencedTypes, TryFromWith, TryIntoWith};
|
||||
use crate::backup::{serialize, ReferencedTypes, TryFromWith, TryIntoWith};
|
||||
use crate::proto::backup as proto;
|
||||
use crate::proto::backup::recipient::Destination as RecipientDestination;
|
||||
|
||||
@ -66,7 +66,7 @@ pub struct MinimalRecipientData(DestinationKind);
|
||||
///
|
||||
/// This keeps the full data in memory behind a [`Arc`] so it can be cheaply
|
||||
/// cloned when referenced by later frames.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, serde::Serialize)]
|
||||
pub struct FullRecipientData(Arc<Destination<Store>>);
|
||||
|
||||
#[derive_where(Debug)]
|
||||
@ -78,7 +78,7 @@ pub struct FullRecipientData(Arc<Destination<Store>>);
|
||||
M::Value<CallLink>: PartialEq
|
||||
)
|
||||
)]
|
||||
#[derive(strum::EnumDiscriminants)]
|
||||
#[derive(serde::Serialize, strum::EnumDiscriminants)]
|
||||
#[strum_discriminants(name(DestinationKind))]
|
||||
pub enum Destination<M: Method + ReferencedTypes> {
|
||||
Contact(M::Value<ContactData>),
|
||||
@ -95,16 +95,19 @@ impl AsRef<DestinationKind> for DestinationKind {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct ContactData {
|
||||
#[serde(serialize_with = "serialize::optional_service_id_as_string")]
|
||||
pub aci: Option<Aci>,
|
||||
#[serde(serialize_with = "serialize::optional_service_id_as_string")]
|
||||
pub pni: Option<Pni>,
|
||||
pub profile_key: Option<ProfileKeyBytes>,
|
||||
pub username: Option<String>,
|
||||
pub registration: Registration,
|
||||
pub e164: Option<u64>,
|
||||
pub blocked: bool,
|
||||
#[serde(serialize_with = "serialize::enum_as_string")]
|
||||
pub visibility: proto::contact::Visibility,
|
||||
pub profile_sharing: bool,
|
||||
pub profile_given_name: Option<String>,
|
||||
@ -112,17 +115,19 @@ pub struct ContactData {
|
||||
pub hide_story: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct GroupData {
|
||||
pub master_key: GroupMasterKeyBytes,
|
||||
pub whitelisted: bool,
|
||||
pub hide_story: bool,
|
||||
#[serde(serialize_with = "serialize::enum_as_string")]
|
||||
pub story_send_mode: proto::group::StorySendMode,
|
||||
#[serde(serialize_with = "serialize::optional_proto_message_as_bytes")]
|
||||
pub snapshot: Option<Box<proto::group::GroupSnapshot>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub enum DistributionListItem<Recipient> {
|
||||
Deleted {
|
||||
@ -138,14 +143,14 @@ pub enum DistributionListItem<Recipient> {
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub enum Registration {
|
||||
NotRegistered { unregistered_at: Option<Timestamp> },
|
||||
Registered,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub enum PrivacyMode {
|
||||
OnlyWith,
|
||||
|
182
rust/message-backup/src/backup/serialize.rs
Normal file
182
rust/message-backup/src/backup/serialize.rs
Normal file
@ -0,0 +1,182 @@
|
||||
//
|
||||
// Copyright (C) 2024 Signal Messenger, LLC.
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
use libsignal_protocol::ServiceId;
|
||||
use serde::ser::{SerializeStruct as _, SerializeTupleVariant as _};
|
||||
use serde::{Serialize, Serializer};
|
||||
|
||||
use crate::proto::backup as proto;
|
||||
|
||||
/// Serializes using [`ToString`].
|
||||
pub(crate) fn to_string<S: Serializer>(t: &impl ToString, s: S) -> Result<S::Ok, S::Error> {
|
||||
t.to_string().serialize(s)
|
||||
}
|
||||
|
||||
/// Serializes using [`ServiceId::service_id_string`].
|
||||
pub(crate) fn service_id_as_string<S: Serializer>(
|
||||
id: &(impl Copy + Into<ServiceId>),
|
||||
serializer: S,
|
||||
) -> Result<S::Ok, S::Error> {
|
||||
(*id).into().service_id_string().serialize(serializer)
|
||||
}
|
||||
|
||||
/// Serializes using [`ServiceId::service_id_string`].
|
||||
pub(crate) fn optional_service_id_as_string<S: Serializer>(
|
||||
id: &Option<(impl Copy + Into<ServiceId>)>,
|
||||
serializer: S,
|
||||
) -> Result<S::Ok, S::Error> {
|
||||
(*id)
|
||||
.map(|id| id.into().service_id_string())
|
||||
.serialize(serializer)
|
||||
}
|
||||
|
||||
/// Serializes [`protobuf::Enum`] types as strings.
|
||||
pub(crate) fn enum_as_string<S: Serializer>(
|
||||
source: &impl protobuf::Enum,
|
||||
serializer: S,
|
||||
) -> Result<S::Ok, S::Error> {
|
||||
format!("{source:?}").serialize(serializer)
|
||||
}
|
||||
|
||||
/// Serializes [`protobuf::Message`] types as hex-encoded protobuf wire format.
|
||||
pub(crate) fn optional_proto_message_as_bytes<S: Serializer, M: protobuf::Message>(
|
||||
message: &Option<impl std::ops::Deref<Target = M>>,
|
||||
serializer: S,
|
||||
) -> Result<S::Ok, S::Error> {
|
||||
struct MessageAsHexBytes<T>(T);
|
||||
impl<T: protobuf::Message> Serialize for MessageAsHexBytes<&'_ T> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut bytes = Vec::new();
|
||||
self.0
|
||||
.write_to_vec(&mut bytes)
|
||||
.map_err(<S::Error as serde::ser::Error>::custom)?;
|
||||
|
||||
hex::serialize(bytes, serializer)
|
||||
}
|
||||
}
|
||||
|
||||
message
|
||||
.as_deref()
|
||||
.map(MessageAsHexBytes)
|
||||
.serialize(serializer)
|
||||
}
|
||||
|
||||
impl serde::Serialize for proto::contact_attachment::Name {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let Self {
|
||||
givenName,
|
||||
familyName,
|
||||
prefix,
|
||||
suffix,
|
||||
middleName,
|
||||
displayName,
|
||||
special_fields: _,
|
||||
} = self;
|
||||
let mut ser = serializer.serialize_struct("Name", 6)?;
|
||||
ser.serialize_field("givenName", givenName)?;
|
||||
ser.serialize_field("familyName", familyName)?;
|
||||
ser.serialize_field("prefix", prefix)?;
|
||||
ser.serialize_field("suffix", suffix)?;
|
||||
ser.serialize_field("middleName", middleName)?;
|
||||
ser.serialize_field("displayName", displayName)?;
|
||||
ser.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::Serialize for proto::contact_attachment::Phone {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let Self {
|
||||
value,
|
||||
type_,
|
||||
label,
|
||||
special_fields: _,
|
||||
} = self;
|
||||
let mut ser = serializer.serialize_struct("Phone", 3)?;
|
||||
ser.serialize_field("value", value)?;
|
||||
ser.serialize_field("type_", &format!("{:?}", type_))?;
|
||||
ser.serialize_field("label", label)?;
|
||||
ser.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::Serialize for proto::contact_attachment::Email {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let Self {
|
||||
value,
|
||||
type_,
|
||||
label,
|
||||
special_fields: _,
|
||||
} = self;
|
||||
let mut ser = serializer.serialize_struct("Email", 3)?;
|
||||
ser.serialize_field("value", value)?;
|
||||
ser.serialize_field("type_", &format!("{:?}", type_))?;
|
||||
ser.serialize_field("label", label)?;
|
||||
ser.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::Serialize for proto::contact_attachment::PostalAddress {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let Self {
|
||||
type_,
|
||||
label,
|
||||
street,
|
||||
pobox,
|
||||
neighborhood,
|
||||
city,
|
||||
region,
|
||||
postcode,
|
||||
country,
|
||||
special_fields: _,
|
||||
} = self;
|
||||
let mut ser = serializer.serialize_struct("PostalAddress", 9)?;
|
||||
ser.serialize_field("type_", &format!("{:?}", type_))?;
|
||||
ser.serialize_field("label", label)?;
|
||||
ser.serialize_field("street", street)?;
|
||||
ser.serialize_field("pobox", pobox)?;
|
||||
ser.serialize_field("neighborhood", neighborhood)?;
|
||||
ser.serialize_field("city", city)?;
|
||||
ser.serialize_field("region", region)?;
|
||||
ser.serialize_field("postcode", postcode)?;
|
||||
ser.serialize_field("country", country)?;
|
||||
ser.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::Serialize for proto::learned_profile_chat_update::PreviousName {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match self {
|
||||
proto::learned_profile_chat_update::PreviousName::E164(e164) => {
|
||||
let mut tv = serializer.serialize_tuple_variant("PreviousName", 0, "E164", 1)?;
|
||||
tv.serialize_field(e164)?;
|
||||
tv.end()
|
||||
}
|
||||
proto::learned_profile_chat_update::PreviousName::Username(username) => {
|
||||
let mut tv =
|
||||
serializer.serialize_tuple_variant("PreviousName", 1, "Username", 1)?;
|
||||
tv.serialize_field(username)?;
|
||||
tv.end()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@ use crate::proto::backup as proto;
|
||||
|
||||
/// Validated version of [`proto::StickerPack`].
|
||||
#[derive_where(Debug)]
|
||||
#[derive(serde::Serialize)]
|
||||
#[cfg_attr(test, derive_where(PartialEq;
|
||||
M::Value<Key>: PartialEq,
|
||||
))]
|
||||
@ -23,7 +24,7 @@ pub struct StickerPack<M: Method> {
|
||||
_limit_construction_to_module: (),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, serde::Serialize)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub struct MessageSticker {
|
||||
pub pack_id: PackId,
|
||||
@ -32,10 +33,10 @@ pub struct MessageSticker {
|
||||
_limit_construction_to_module: (),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, serde::Serialize)]
|
||||
pub struct PackId([u8; 16]);
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, serde::Serialize)]
|
||||
pub struct Key([u8; 32]);
|
||||
|
||||
impl<'a> TryFrom<&'a [u8]> for PackId {
|
||||
|
@ -6,10 +6,10 @@
|
||||
use std::ops::Add;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, serde::Serialize)]
|
||||
pub struct Timestamp(SystemTime);
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, serde::Serialize)]
|
||||
pub struct Duration(std::time::Duration);
|
||||
|
||||
impl Timestamp {
|
||||
|
@ -46,6 +46,25 @@ fn is_valid_binary_proto(input: Fixture<Vec<u8>>) {
|
||||
validate_proto(input.content())
|
||||
}
|
||||
|
||||
#[dir_test(
|
||||
dir: "$CARGO_MANIFEST_DIR/tests/res/test-cases",
|
||||
glob: "valid/*.binproto",
|
||||
postfix: "serialize"
|
||||
loader: read_file
|
||||
)]
|
||||
fn can_serialize_binary_proto(input: Fixture<Vec<u8>>) {
|
||||
let input = Cursor::new(input.content());
|
||||
let reader = BackupReader::new_unencrypted(input, BACKUP_PURPOSE);
|
||||
let result = futures::executor::block_on(reader.read_all())
|
||||
.result
|
||||
.expect("valid backup");
|
||||
// This should not crash.
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&result).expect("can serialize")
|
||||
)
|
||||
}
|
||||
|
||||
const ENCRYPTED_SOURCE_SUFFIX: &str = ".source.jsonproto";
|
||||
#[dir_test(
|
||||
dir: "$CARGO_MANIFEST_DIR/tests/res/test-cases",
|
||||
|
Loading…
Reference in New Issue
Block a user