1use derive_into_owned::IntoOwned;
6use nom::{
7 branch::alt,
8 bytes::complete::tag,
9 combinator::{map, opt},
10 sequence::{preceded, tuple},
11 IResult,
12};
13
14use std::{borrow::Cow, net::IpAddr};
15
16use crate::parsers::{attribute, cowify, read_addr, read_number, read_string, wsf};
17
18#[derive(Copy, Clone, PartialEq, Eq)]
19#[cfg_attr(feature = "debug", derive(Debug))]
20#[cfg_attr(
21 feature = "serde",
22 derive(serde::Serialize, serde::Deserialize),
23 serde(rename_all = "camelCase")
24)]
25pub enum CandidateComponent {
26 Rtp,
27 Rtcp,
28}
29
30#[derive(Copy, Clone, PartialEq, Eq)]
31#[cfg_attr(feature = "debug", derive(Debug))]
32#[cfg_attr(
33 feature = "serde",
34 derive(serde::Serialize, serde::Deserialize),
35 serde(rename_all = "camelCase")
36)]
37#[non_exhaustive]
38pub enum CandidateProtocol {
39 Tcp,
40 Udp,
41 Dccp,
42}
43
44#[derive(Copy, Clone, PartialEq, Eq)]
45#[cfg_attr(feature = "debug", derive(Debug))]
46#[cfg_attr(
47 feature = "serde",
48 derive(serde::Serialize, serde::Deserialize),
49 serde(rename_all = "camelCase")
50)]
51#[non_exhaustive]
52pub enum CandidateType {
53 Host,
54 Relay,
55 Srflx,
56 Prflx,
57}
58
59#[derive(Clone, IntoOwned, PartialEq, Eq)]
69#[cfg_attr(feature = "debug", derive(Debug))]
70#[cfg_attr(
71 feature = "serde",
72 derive(serde::Serialize, serde::Deserialize),
73 serde(rename_all = "camelCase")
74)]
75pub struct Candidate<'a> {
76 pub foundation: u32,
77 pub component: CandidateComponent,
78 pub protocol: CandidateProtocol,
79 pub priority: u32, pub addr: IpAddr, pub port: u32, pub r#type: CandidateType, pub raddr: Option<IpAddr>, pub rport: Option<u32>, pub tcptype: Option<Cow<'a, str>>,
86 pub generation: Option<u32>,
87 pub network_id: Option<u32>,
88}
89
90pub fn candidate(input: &str) -> IResult<&str, Candidate> {
91 map(
92 tuple((
93 wsf(read_number), wsf(alt((
96 map(tag("1"), |_| CandidateComponent::Rtp),
97 map(tag("2"), |_| CandidateComponent::Rtcp),
98 ))),
99 wsf(alt((
101 map(alt((tag("UDP"), tag("udp"))), |_| CandidateProtocol::Udp),
102 map(alt((tag("TCP"), tag("tcp"))), |_| CandidateProtocol::Tcp),
103 map(alt((tag("DCCP"), tag("dccp"))), |_| CandidateProtocol::Dccp),
104 ))),
105 wsf(read_number), wsf(read_addr), wsf(read_number), preceded(
109 tag("typ"),
110 wsf(alt((
112 map(tag("host"), |_| CandidateType::Host),
113 map(tag("relay"), |_| CandidateType::Relay),
114 map(tag("srflx"), |_| CandidateType::Srflx),
115 map(tag("prflx"), |_| CandidateType::Prflx),
116 ))),
117 ),
118 opt(preceded(wsf(tag("raddr")), read_addr)), opt(preceded(wsf(tag("rport")), read_number)), opt(preceded(wsf(tag("tcptype")), cowify(read_string))), opt(preceded(wsf(tag("generation")), read_number)), opt(preceded(wsf(tag("network-id")), read_number)), )),
124 |(
125 foundation,
126 component,
127 protocol,
128 priority,
129 addr,
130 port,
131 r#type,
132 raddr,
133 rport,
134 tcptype,
135 generation,
136 network_id,
137 )| Candidate {
138 foundation,
139 component,
140 protocol,
141 priority,
142 addr,
143 port,
144 r#type,
145 raddr,
146 rport,
147 tcptype,
148 generation,
149 network_id,
150 },
151 )(input)
152}
153
154pub fn candidate_line(input: &str) -> IResult<&str, Candidate> {
156 attribute("candidate", candidate)(input)
157}
158
159#[cfg(test)]
160#[rustfmt::skip]
161mod tests {
162 use std::net::Ipv4Addr;
163
164 use crate::{assert_line, assert_line_print};
165
166 use super::*;
167
168 #[test]
169 fn parses_candidate_line() {
170 assert_line_print!(candidate_line, "a=candidate:3348148302 1 udp 2113937151 192.0.2.1 56500 typ host");
171 assert_line_print!(candidate_line, "a=candidate:3348148302 1 tcp 2113937151 192.0.2.1 56500 typ srflx");
172 assert_line_print!(candidate_line, "a=candidate:3348148302 2 tcp 2113937151 192.0.2.1 56500 typ srflx");
173 assert_line!(candidate_line, "a=candidate:1 1 TCP 2128609279 10.0.1.1 9 typ host tcptype active", Candidate {
174 foundation: 1, component: CandidateComponent::Rtp, protocol: CandidateProtocol::Tcp, priority: 2128609279, addr: Ipv4Addr::new(10,0,1,1).into(), port: 9,
175 r#type: CandidateType::Host, raddr: None, rport: None, tcptype: Some( Cow::from("active"),), generation: None, network_id: None, });
176 assert_line_print!(candidate_line, "a=candidate:2 1 tcp 2124414975 10.0.1.1 8998 typ host tcptype passive");
177 assert_line_print!(candidate_line, "a=candidate:3 1 tcp 2120220671 10.0.1.1 8999 typ host tcptype so");
178 assert_line_print!(candidate_line, "a=candidate:4 1 tcp 1688207359 192.0.2.3 9 typ srflx raddr 10.0.1.1 rport 9 tcptype active");
179 assert_line_print!(candidate_line, "a=candidate:5 1 tcp 1684013055 192.0.2.3 45664 typ srflx raddr 10.0.1.1 rport 8998 tcptype passive generation 5");
180 assert_line_print!(candidate_line, "a=candidate:6 1 tcp 1692401663 192.0.2.3 45687 typ srflx raddr 10.0.1.1 rport 8999 tcptype so");
181 assert_line!(candidate_line, "a=candidate:3348148302 1 UDP 2113937151 192.0.2.1 56500 typ relay");
182 assert_line!(candidate_line, "a=candidate:3348148302 1 UDP 2113937151 192.0.2.1 56500 typ srflx");
183 assert_line_print!(candidate_line, "a=candidate:2791055836 1 udp 2122262783 2001:9e8:b0b:8400:c5e3:8776:82fc:7704 58605 typ host generation 0 network-id 2");
185 }
186
187 #[test]
188 fn audio_lines() {
189
190 let lines =[
191 "a=candidate:1467250027 1 udp 2122260223 192.168.0.196 46243 typ host generation 0",
192 "a=candidate:1467250027 2 udp 2122260222 192.168.0.196 56280 typ host generation 0",
193 "a=candidate:435653019 1 tcp 1845501695 192.168.0.196 0 typ host tcptype active generation 0",
194 "a=candidate:435653019 2 tcp 1845501695 192.168.0.196 0 typ host tcptype active generation 0",
195 "a=candidate:1853887674 1 udp 1518280447 47.61.61.61 36768 typ srflx raddr 192.168.0.196 rport 36768 generation 0",
196 "a=candidate:1853887674 2 udp 1518280447 47.61.61.61 36768 typ srflx raddr 192.168.0.196 rport 36768 generation 0",
197 "a=candidate:750991856 2 udp 25108222 237.30.30.30 51472 typ relay raddr 47.61.61.61 rport 54763 generation 0",
198 "a=candidate:750991856 1 udp 25108223 237.30.30.30 58779 typ relay raddr 47.61.61.61 rport 54761 generation 0",
199 ];
200 for line in &lines {
201 assert_line!(*line, candidate_line);
202 }
203
204 }
205
206 #[test]
207 #[should_panic]
208 #[ignore]
209 fn fails_on_bad_ip() {
210 candidate("candidate:3348148302 1 udp 2113937151 293.0.2.1 56500 typ host\n").unwrap();
211 }
212}