1use serde::ser::SerializeStruct;
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5
6use crate::room_type::RoomType;
7
8#[derive(Clone, Debug, PartialEq, Eq)]
13pub struct Room {
14 pub token: String,
16 pub name: String,
18 pub room_type: RoomType,
20 pub description: Option<String>,
22 pub has_password: bool,
24 pub participant_count: Option<u32>,
26}
27
28impl Room {
29 pub fn new(
31 token: impl Into<String>,
32 name: impl Into<String>,
33 room_type: RoomType,
34 description: Option<String>,
35 has_password: bool,
36 participant_count: Option<u32>,
37 ) -> Self {
38 Self {
39 token: token.into(),
40 name: name.into(),
41 room_type,
42 description,
43 has_password,
44 participant_count,
45 }
46 }
47}
48
49#[derive(Deserialize)]
50#[serde(rename_all = "camelCase")]
51struct RoomWire {
52 token: String,
53 #[serde(default)]
54 name: Option<String>,
55 #[serde(default, alias = "displayName")]
56 display_name: Option<String>,
57 #[serde(default, alias = "roomName")]
58 room_name: Option<String>,
59 #[serde(alias = "room_type", alias = "roomType", alias = "type")]
60 room_type: RoomType,
61 #[serde(default)]
62 description: Option<String>,
63 #[serde(default, alias = "has_password")]
64 has_password: bool,
65 #[serde(
66 default,
67 alias = "numParticipants",
68 alias = "participant_count",
69 deserialize_with = "crate::wire::lossy_opt_u32"
70 )]
71 participant_count: Option<u32>,
72}
73
74impl From<RoomWire> for Room {
75 fn from(wire: RoomWire) -> Self {
76 let name = wire
77 .name
78 .filter(|s| !s.trim().is_empty())
79 .or_else(|| wire.display_name.filter(|s| !s.trim().is_empty()))
80 .or_else(|| wire.room_name.filter(|s| !s.trim().is_empty()))
81 .unwrap_or_default();
82
83 let description = wire.description.filter(|s| !s.trim().is_empty());
84
85 Self {
86 token: wire.token,
87 name,
88 room_type: wire.room_type,
89 description,
90 has_password: wire.has_password,
91 participant_count: wire.participant_count,
92 }
93 }
94}
95
96impl<'de> Deserialize<'de> for Room {
97 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
98 RoomWire::deserialize(deserializer).map(Self::from)
99 }
100}
101
102impl Serialize for Room {
103 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
104 let mut state = serializer.serialize_struct("Room", 6)?;
105 state.serialize_field("token", &self.token)?;
106 state.serialize_field("name", &self.name)?;
107 state.serialize_field("room_type", &self.room_type)?;
108 state.serialize_field("description", &self.description)?;
109 state.serialize_field("has_password", &self.has_password)?;
110 state.serialize_field("participant_count", &self.participant_count)?;
111 state.end()
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn room_deserialization_from_talk_v4_json() {
121 let json = r#"{
122 "token": "d7a8f9b2",
123 "name": "General Chat",
124 "displayName": "General Chat",
125 "type": 2,
126 "description": "Company wide discussion",
127 "hasPassword": false,
128 "participantCount": 15
129 }"#;
130
131 let room: Room = serde_json::from_str(json).expect("valid room json");
132 assert_eq!(room.token, "d7a8f9b2");
133 assert_eq!(room.name, "General Chat");
134 assert_eq!(room.room_type, RoomType::Group);
135 assert_eq!(room.description.as_deref(), Some("Company wide discussion"));
136 assert!(!room.has_password);
137 assert_eq!(room.participant_count, Some(15));
138 }
139
140 #[test]
141 fn room_deserialization_fallback_display_name_and_room_type() {
142 let json = r#"{
143 "token": "x1y2z3",
144 "displayName": "Alice & Bob",
145 "roomType": 1,
146 "hasPassword": true
147 }"#;
148
149 let room: Room = serde_json::from_str(json).expect("valid room json");
150 assert_eq!(room.token, "x1y2z3");
151 assert_eq!(room.name, "Alice & Bob");
152 assert_eq!(room.room_type, RoomType::OneToOne);
153 assert_eq!(room.description, None);
154 assert!(room.has_password);
155 assert_eq!(room.participant_count, None);
156 }
157
158 #[test]
159 fn room_serialization_round_trip() {
160 let room = Room::new(
161 "public123",
162 "Support Room",
163 RoomType::Public,
164 Some("Public assistance".into()),
165 false,
166 Some(42),
167 );
168
169 let json = serde_json::to_string(&room).expect("serialized");
170 let parsed: Room = serde_json::from_str(&json).expect("deserialized");
171 assert_eq!(room, parsed);
172 }
173}