Skip to main content

freeswitch_types/conference_info/
types.rs

1use super::common::State;
2use super::media::MediaStatus;
3
4/// Conference metadata (RFC 4575 Section 5.1).
5#[derive(Debug, Clone, PartialEq, Eq, Default)]
6#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
7#[non_exhaustive]
8pub struct ConferenceDescription {
9    /// Conference display name.
10    #[cfg_attr(
11        feature = "serde",
12        serde(
13            rename = "display-text",
14            default,
15            skip_serializing_if = "Option::is_none"
16        )
17    )]
18    pub display_text: Option<String>,
19    /// Conference subject/topic.
20    #[cfg_attr(
21        feature = "serde",
22        serde(default, skip_serializing_if = "Option::is_none")
23    )]
24    pub subject: Option<String>,
25    /// Free-form text description.
26    #[cfg_attr(
27        feature = "serde",
28        serde(rename = "free-text", default, skip_serializing_if = "Option::is_none")
29    )]
30    pub free_text: Option<String>,
31    /// Space-separated keywords.
32    #[cfg_attr(
33        feature = "serde",
34        serde(default, skip_serializing_if = "Option::is_none")
35    )]
36    pub keywords: Option<String>,
37    /// Access and participation URIs.
38    #[cfg_attr(
39        feature = "serde",
40        serde(rename = "conf-uris", default, skip_serializing_if = "Option::is_none")
41    )]
42    pub conf_uris: Option<Uris>,
43    /// Auxiliary service URIs (web page, streaming, recording).
44    #[cfg_attr(
45        feature = "serde",
46        serde(
47            rename = "service-uris",
48            default,
49            skip_serializing_if = "Option::is_none"
50        )
51    )]
52    pub service_uris: Option<Uris>,
53    /// Maximum number of participants allowed.
54    #[cfg_attr(
55        feature = "serde",
56        serde(
57            rename = "maximum-user-count",
58            default,
59            skip_serializing_if = "Option::is_none"
60        )
61    )]
62    pub maximum_user_count: Option<u32>,
63    /// Available media types for this conference.
64    #[cfg_attr(
65        feature = "serde",
66        serde(
67            rename = "available-media",
68            default,
69            skip_serializing_if = "Option::is_none"
70        )
71    )]
72    pub available_media: Option<AvailableMedia>,
73}
74
75/// Container for available media descriptions in a conference.
76#[derive(Debug, Clone, PartialEq, Eq, Default)]
77#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
78#[non_exhaustive]
79pub struct AvailableMedia {
80    /// Individual media type entries.
81    #[cfg_attr(
82        feature = "serde",
83        serde(rename = "entry", default, skip_serializing_if = "Vec::is_empty")
84    )]
85    pub entries: Vec<MediaDescription>,
86}
87
88/// A media type available in the conference (part of conference-description).
89#[derive(Debug, Clone, PartialEq, Eq)]
90#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
91#[non_exhaustive]
92pub struct MediaDescription {
93    /// Media label attribute.
94    #[cfg_attr(feature = "serde", serde(rename = "@label", default))]
95    pub label: String,
96    /// Human-readable description.
97    #[cfg_attr(
98        feature = "serde",
99        serde(
100            rename = "display-text",
101            default,
102            skip_serializing_if = "Option::is_none"
103        )
104    )]
105    pub display_text: Option<String>,
106    /// SDP media type (audio, video, text).
107    #[cfg_attr(
108        feature = "serde",
109        serde(rename = "type", default, skip_serializing_if = "Option::is_none")
110    )]
111    pub media_type: Option<String>,
112    /// Directionality/status.
113    #[cfg_attr(
114        feature = "serde",
115        serde(default, skip_serializing_if = "Option::is_none")
116    )]
117    pub status: Option<MediaStatus>,
118}
119
120impl MediaDescription {
121    /// Create a media description with the given label.
122    pub fn new(label: impl Into<String>) -> Self {
123        Self {
124            label: label.into(),
125            display_text: None,
126            media_type: None,
127            status: None,
128        }
129    }
130}
131
132/// Conference host information (RFC 4575 Section 5.2).
133#[derive(Debug, Clone, PartialEq, Eq, Default)]
134#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
135#[non_exhaustive]
136pub struct HostInfo {
137    /// Host display name.
138    #[cfg_attr(
139        feature = "serde",
140        serde(
141            rename = "display-text",
142            default,
143            skip_serializing_if = "Option::is_none"
144        )
145    )]
146    pub display_text: Option<String>,
147    /// Host web page URL.
148    #[cfg_attr(
149        feature = "serde",
150        serde(rename = "web-page", default, skip_serializing_if = "Option::is_none")
151    )]
152    pub web_page: Option<String>,
153    /// Host contact URIs.
154    #[cfg_attr(
155        feature = "serde",
156        serde(default, skip_serializing_if = "Option::is_none")
157    )]
158    pub uris: Option<Uris>,
159}
160
161/// Aggregate conference state (RFC 4575 Section 5.3).
162#[derive(Debug, Clone, PartialEq, Eq, Default)]
163#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
164#[non_exhaustive]
165pub struct ConferenceState {
166    /// Number of participants currently in the conference.
167    #[cfg_attr(
168        feature = "serde",
169        serde(
170            rename = "user-count",
171            default,
172            skip_serializing_if = "Option::is_none"
173        )
174    )]
175    pub user_count: Option<u32>,
176    /// Whether the conference is actively in session.
177    #[cfg_attr(
178        feature = "serde",
179        serde(default, skip_serializing_if = "Option::is_none")
180    )]
181    pub active: Option<bool>,
182    /// Whether the conference is locked for new participants.
183    #[cfg_attr(
184        feature = "serde",
185        serde(default, skip_serializing_if = "Option::is_none")
186    )]
187    pub locked: Option<bool>,
188}
189
190/// Container for URI entries, used by host-info, conf-uris, service-uris,
191/// associated-aors, and sidebars-by-ref.
192#[derive(Debug, Clone, PartialEq, Eq, Default)]
193#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
194#[non_exhaustive]
195pub struct Uris {
196    /// Partial notification state.
197    #[cfg_attr(
198        feature = "serde",
199        serde(rename = "@state", default, skip_serializing_if = "Option::is_none")
200    )]
201    pub state: Option<State>,
202    /// Individual URI entries.
203    #[cfg_attr(
204        feature = "serde",
205        serde(rename = "entry", default, skip_serializing_if = "Vec::is_empty")
206    )]
207    pub entries: Vec<UriEntry>,
208}
209
210/// A single URI entry with optional metadata.
211#[derive(Debug, Clone, PartialEq, Eq)]
212#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
213#[non_exhaustive]
214pub struct UriEntry {
215    /// The URI value.
216    #[cfg_attr(feature = "serde", serde(default))]
217    pub uri: String,
218    /// Human-readable label.
219    #[cfg_attr(
220        feature = "serde",
221        serde(
222            rename = "display-text",
223            default,
224            skip_serializing_if = "Option::is_none"
225        )
226    )]
227    pub display_text: Option<String>,
228    /// Entry purpose (e.g. "participation", "streaming", "web-page").
229    #[cfg_attr(
230        feature = "serde",
231        serde(default, skip_serializing_if = "Option::is_none")
232    )]
233    pub purpose: Option<String>,
234    /// Last modification timestamp.
235    #[cfg_attr(
236        feature = "serde",
237        serde(default, skip_serializing_if = "Option::is_none")
238    )]
239    pub modified: Option<String>,
240}
241
242impl UriEntry {
243    /// Create a URI entry.
244    pub fn new(uri: impl Into<String>) -> Self {
245        Self {
246            uri: uri.into(),
247            display_text: None,
248            purpose: None,
249            modified: None,
250        }
251    }
252}
253
254/// Container for inline sidebar conferences (RFC 4575 Section 5.8).
255///
256/// Each entry is a full [`ConferenceInfo`](super::ConferenceInfo) document,
257/// allowing recursive nesting.
258#[derive(Debug, Clone, PartialEq, Eq, Default)]
259#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
260#[non_exhaustive]
261pub struct SidebarsByVal {
262    /// Partial notification state.
263    #[cfg_attr(
264        feature = "serde",
265        serde(rename = "@state", default, skip_serializing_if = "Option::is_none")
266    )]
267    pub state: Option<State>,
268    /// Inline conference-info entries.
269    #[cfg_attr(
270        feature = "serde",
271        serde(rename = "entry", default, skip_serializing_if = "Vec::is_empty")
272    )]
273    pub entries: Vec<super::ConferenceInfo>,
274}
275
276#[cfg(test)]
277mod tests {
278    use super::*;
279
280    #[test]
281    fn uri_entry_new() {
282        let entry = UriEntry::new("sip:conf@example.com");
283        assert_eq!(entry.uri, "sip:conf@example.com");
284        assert!(entry
285            .display_text
286            .is_none());
287    }
288
289    #[test]
290    fn conference_state_default() {
291        let state = ConferenceState::default();
292        assert!(state
293            .user_count
294            .is_none());
295        assert!(state
296            .active
297            .is_none());
298        assert!(state
299            .locked
300            .is_none());
301    }
302}