Skip to content

Commit 290e30d

Browse files
committed
feat: Make struct types non-exhaustive
This is better to avoid breaking changes when fields are added.
1 parent 80bf57e commit 290e30d

File tree

8 files changed

+136
-2
lines changed

8 files changed

+136
-2
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ Breaking changes:
88
* `RoomSortOrder` and `RoomDirection` in `rooms::list_rooms::v1` are now non-
99
exhaustive. Their `PartialOrd` and `Ord` implementations now use their string
1010
representation instead of the order in which they are defined in the enum.
11+
* `UserDetails`, `ExternalId`, `CurrentUpdate`, `ExperimentalFeatures`,
12+
`RoomDetails` and `UserMinorDetails` are now non-exhaustive. To keep using
13+
them as if they were exhaustive, use the `ruma_unstable_exhaustive_types`
14+
compile-time `cfg` setting.
1115

1216
Improvement:
1317

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ dbg_macro = "warn"
4343
disallowed_types = "warn"
4444
empty_line_after_outer_attr = "warn"
4545
exhaustive_enums = "warn"
46-
# exhaustive_structs = "warn"
46+
exhaustive_structs = "warn"
4747
inefficient_to_string = "warn"
4848
macro_use_imports = "warn"
4949
map_flatten = "warn"

src/background_updates/status/v1.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,12 @@ pub struct Response {
2828
pub current_updates: HashMap<String, CurrentUpdate>,
2929
}
3030

31+
/// Information about a current update.
32+
///
33+
/// To create an instance of this type, first create a `CurrentUpdateInit` and convert it via
34+
/// `CurrentUpdate::from` / `.into()`.
3135
#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)]
36+
#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
3237
pub struct CurrentUpdate {
3338
/// Name of the update.
3439
pub name: String,
@@ -43,6 +48,33 @@ pub struct CurrentUpdate {
4348
pub average_items_per_ms: f64,
4449
}
4550

51+
/// Initial set of fields of [`CurrentUpdate`].
52+
///
53+
/// This struct will not be updated even if additional fields are added to `CurrentUpdate`.
54+
#[derive(Debug)]
55+
#[allow(clippy::exhaustive_structs)]
56+
pub struct CurrentUpdateInit {
57+
/// Name of the update.
58+
pub name: String,
59+
60+
/// Total number of processed "items".
61+
pub total_item_count: u64,
62+
63+
/// Runtime of background process, not including sleeping time.
64+
pub total_duration_ms: f64,
65+
66+
/// Items processed per millisecond based on an exponential average.
67+
pub average_items_per_ms: f64,
68+
}
69+
70+
impl From<CurrentUpdateInit> for CurrentUpdate {
71+
fn from(value: CurrentUpdateInit) -> Self {
72+
let CurrentUpdateInit { name, total_item_count, total_duration_ms, average_items_per_ms } =
73+
value;
74+
Self { name, total_item_count, total_duration_ms, average_items_per_ms }
75+
}
76+
}
77+
4678
impl Request {
4779
/// Creates an empty `Request`.
4880
pub fn new() -> Self {

src/experimental_features/enable_features/v1.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ pub struct Request {
2626
}
2727

2828
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, Default)]
29+
#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
2930
pub struct ExperimentalFeatures {
3031
/// Whether busy presence state is enabled.
3132
#[serde(skip_serializing_if = "Option::is_none")]
@@ -38,6 +39,13 @@ pub struct ExperimentalFeatures {
3839
pub msc3967: Option<bool>,
3940
}
4041

42+
impl ExperimentalFeatures {
43+
/// Construct an empty `ExperimentalFeatures`.
44+
pub fn new() -> Self {
45+
Self::default()
46+
}
47+
}
48+
4149
#[response]
4250
#[derive(Default)]
4351
pub struct Response {}

src/lib.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
11
//! Serializable types for the requests and responses for each endpoint in the
22
//! [synapse admin API][api].
33
//!
4+
//! # Compile-time `cfg` settings
5+
//!
6+
//! These settings are accepted at compile time to configure the generated code. They can be set as
7+
//! `--cfg={key}={value}` using `RUSTFLAGS` or `.cargo/config.toml` (under `[build]` -> `rustflags =
8+
//! ["..."]`).
9+
//!
10+
//! * `ruma_identifiers_storage` -- Choose the inner representation of `Owned*` wrapper types for
11+
//! identifiers. By default they use [`Box`], setting the value to `Arc` makes them use
12+
//! [`Arc`](std::sync::Arc).
13+
//! * `ruma_unstable_exhaustive_types` -- Most types in synapse-admin-api are marked as
14+
//! non-exhaustive to avoid breaking changes when new fields are added in the API. This setting
15+
//! compiles all types as exhaustive. By enabling this feature you opt out of all semver
16+
//! guarantees synapse-admin-api otherwise provides.
17+
//!
418
//! [api]: https://github.com/matrix-org/synapse/tree/master/docs/admin_api
519
620
// FIXME: don't allow dead code, warn on missing docs

src/rooms/list_rooms/v1.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use ruma::{
55
room::RoomType,
66
serde::{OrdAsRefStr, PartialEqAsRefStr, PartialOrdAsRefStr, StringEnum},
77
space::SpaceRoomJoinRule,
8-
OwnedRoomAliasId, OwnedRoomId, OwnedUserId, UInt,
8+
uint, OwnedRoomAliasId, OwnedRoomId, OwnedUserId, UInt,
99
};
1010
use serde::{Deserialize, Serialize};
1111

@@ -147,6 +147,7 @@ pub enum SortDirection {
147147

148148
/// Structure for all the room details.
149149
#[derive(Serialize, Deserialize, Clone, Debug)]
150+
#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
150151
pub struct RoomDetails {
151152
/// Room ID
152153
pub room_id: OwnedRoomId,
@@ -196,3 +197,27 @@ pub struct RoomDetails {
196197
/// Room type of the room.
197198
pub room_type: Option<RoomType>,
198199
}
200+
201+
impl RoomDetails {
202+
/// Construct `RoomDetails` with the given room ID and all the other fields at their default
203+
/// value.
204+
pub fn new(room_id: OwnedRoomId) -> Self {
205+
Self {
206+
room_id,
207+
name: None,
208+
canonical_alias: None,
209+
joined_members: uint!(0),
210+
joined_local_members: uint!(0),
211+
version: None,
212+
creator: None,
213+
encryption: None,
214+
federatable: false,
215+
public: false,
216+
join_rules: None,
217+
guest_access: None,
218+
history_visibility: None,
219+
state_events: uint!(0),
220+
room_type: None,
221+
}
222+
}
223+
}

src/users.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use serde::{Deserialize, Serialize};
1212

1313
/// User details
1414
#[derive(Serialize, Deserialize, Clone, Debug)]
15+
#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
1516
pub struct UserDetails {
1617
/// The user's name.
1718
pub name: String,
@@ -71,12 +72,44 @@ pub struct UserDetails {
7172
pub locked: bool,
7273
}
7374

75+
impl UserDetails {
76+
/// Construct a `UserDetails` with the given user name and all the other fields set to their
77+
/// default value.
78+
pub fn new(name: String) -> Self {
79+
Self {
80+
name,
81+
password_hash: None,
82+
is_guest: false,
83+
admin: false,
84+
consent_version: None,
85+
consent_server_notice_sent: None,
86+
appservice_id: None,
87+
creation_ts: None,
88+
user_type: None,
89+
deactivated: false,
90+
displayname: String::new(),
91+
avatar_url: None,
92+
threepids: Vec::new(),
93+
external_ids: Vec::new(),
94+
locked: false,
95+
}
96+
}
97+
}
98+
7499
/// An external ID associated with a user
75100
#[derive(Clone, Debug, Deserialize, Serialize)]
101+
#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
76102
pub struct ExternalId {
77103
/// The authentication provider to which the user is associated.
78104
pub auth_provider: String,
79105

80106
/// The ID known to the auth provider associated with this user.
81107
pub external_id: String,
82108
}
109+
110+
impl ExternalId {
111+
/// Construct an `ExternalId` with the given authentication provider and ID.
112+
pub fn new(auth_provider: String, external_id: String) -> Self {
113+
Self { auth_provider, external_id }
114+
}
115+
}

src/users/list_users/v2.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ impl Response {
9797

9898
/// A minor set of user details.
9999
#[derive(Serialize, Deserialize, Clone, Debug)]
100+
#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
100101
pub struct UserMinorDetails {
101102
/// The user's name.
102103
pub name: String,
@@ -128,3 +129,20 @@ pub struct UserMinorDetails {
128129
#[serde(default, deserialize_with = "crate::serde::bool_or_uint")]
129130
pub locked: bool,
130131
}
132+
133+
impl UserMinorDetails {
134+
/// Construct a `UserMinorDetails` with the given user name and all the other fields set to
135+
/// their default value.
136+
pub fn new(name: String) -> Self {
137+
Self {
138+
name,
139+
is_guest: false,
140+
admin: false,
141+
user_type: None,
142+
deactivated: false,
143+
displayname: String::new(),
144+
avatar_url: None,
145+
locked: false,
146+
}
147+
}
148+
}

0 commit comments

Comments
 (0)