Skip to main content

xmtp_proto/types/
server_configuration.rs

1//! Turning one `GetConfigurationResponse` into the snapshot the client holds.
2//!
3//! Zero or empty on the wire means "not provided", so every such field falls
4//! back to the compiled `BACKEND_DEFAULT_*` constant (CFG-031). The two
5//! exceptions the ยง5.2 table names are the identifier, which is rejected when
6//! empty, and `commit_log_enabled`, which keeps `None` distinct from
7//! `Some(false)`.
8
9use xmtp_configuration::{
10    AuthConfiguration, LimitsConfiguration, MlsConfiguration, RetentionConfiguration,
11    ServerConfiguration, SigningKeyDescription,
12};
13
14use crate::backend_v1;
15
16/// Take the published value, or the compiled default when it is zero.
17fn or_default<T, W>(published: W, default: T) -> T
18where
19    T: Copy + PartialEq + Default + TryFrom<W>,
20    W: Copy,
21{
22    T::try_from(published)
23        .ok()
24        .filter(|value| *value != T::default())
25        .unwrap_or(default)
26}
27
28impl From<backend_v1::AuthConfiguration> for AuthConfiguration {
29    fn from(auth: backend_v1::AuthConfiguration) -> Self {
30        Self {
31            enabled: auth.enabled,
32            keys: auth
33                .keys
34                .into_iter()
35                .map(|key| SigningKeyDescription {
36                    kid: key.kid,
37                    alg: key.alg,
38                })
39                .collect(),
40            audiences: auth.audiences,
41            issuers: auth.issuers,
42            required_scopes: auth.required_scopes,
43        }
44    }
45}
46
47impl From<backend_v1::RetentionConfiguration> for RetentionConfiguration {
48    fn from(retention: backend_v1::RetentionConfiguration) -> Self {
49        let default = Self::default();
50        Self {
51            group_message_seconds: or_default(
52                retention.group_message_seconds,
53                default.group_message_seconds,
54            ),
55            welcome_seconds: or_default(retention.welcome_seconds, default.welcome_seconds),
56            key_package_seconds: or_default(
57                retention.key_package_seconds,
58                default.key_package_seconds,
59            ),
60        }
61    }
62}
63
64impl From<backend_v1::LimitsConfiguration> for LimitsConfiguration {
65    fn from(limits: backend_v1::LimitsConfiguration) -> Self {
66        let default = Self::default();
67        Self {
68            max_envelope_bytes: or_default(limits.max_envelope_bytes, default.max_envelope_bytes),
69            max_request_bytes: or_default(limits.max_request_bytes, default.max_request_bytes),
70            max_response_bytes: or_default(limits.max_response_bytes, default.max_response_bytes),
71            max_publish_topics: or_default(limits.max_publish_topics, default.max_publish_topics),
72            max_query_topics: or_default(limits.max_query_topics, default.max_query_topics),
73            max_query_limit: or_default(limits.max_query_limit, default.max_query_limit),
74            default_query_limit: or_default(
75                limits.default_query_limit,
76                default.default_query_limit,
77            ),
78            max_newest_metadata_topics: or_default(
79                limits.max_newest_metadata_topics,
80                default.max_newest_metadata_topics,
81            ),
82            max_newest_full_topics: or_default(
83                limits.max_newest_full_topics,
84                default.max_newest_full_topics,
85            ),
86            max_update_adds: or_default(limits.max_update_adds, default.max_update_adds),
87            max_update_removes: or_default(limits.max_update_removes, default.max_update_removes),
88            max_stream_topics: or_default(limits.max_stream_topics, default.max_stream_topics),
89            max_static_topics: or_default(limits.max_static_topics, default.max_static_topics),
90            max_lookup_identifiers: or_default(
91                limits.max_lookup_identifiers,
92                default.max_lookup_identifiers,
93            ),
94            max_scw_signatures: or_default(limits.max_scw_signatures, default.max_scw_signatures),
95            max_identity_entries: or_default(
96                limits.max_identity_entries,
97                default.max_identity_entries,
98            ),
99            max_update_frames_per_second: or_default(
100                limits.max_update_frames_per_second,
101                default.max_update_frames_per_second,
102            ),
103            max_update_burst: or_default(limits.max_update_burst, default.max_update_burst),
104            max_ping_frames_per_second: or_default(
105                limits.max_ping_frames_per_second,
106                default.max_ping_frames_per_second,
107            ),
108            max_ping_burst: or_default(limits.max_ping_burst, default.max_ping_burst),
109        }
110    }
111}
112
113impl From<backend_v1::MlsConfiguration> for MlsConfiguration {
114    fn from(mls: backend_v1::MlsConfiguration) -> Self {
115        let default = Self::default();
116        Self {
117            max_group_members: or_default(mls.max_group_members, default.max_group_members),
118            max_installations_per_inbox: or_default(
119                mls.max_installations_per_inbox,
120                default.max_installations_per_inbox,
121            ),
122            // Absent is not the same as false: an operator may switch the
123            // commit log off explicitly.
124            commit_log_enabled: mls.commit_log_enabled,
125        }
126    }
127}
128
129impl From<backend_v1::GetConfigurationResponse> for ServerConfiguration {
130    fn from(response: backend_v1::GetConfigurationResponse) -> Self {
131        Self {
132            identifier: response.identifier,
133            server_version: response.server_version,
134            min_libxmtp_version: response.min_libxmtp_version,
135            auth: response.auth.unwrap_or_default().into(),
136            retention: response.retention.unwrap_or_default().into(),
137            limits: response.limits.unwrap_or_default().into(),
138            mls: response.mls.unwrap_or_default().into(),
139            smart_contract_wallet_chains: response.smart_contract_wallet_chains,
140        }
141    }
142}
143
144#[cfg(test)]
145mod tests;