1mod conference_type;
9mod fmtp;
10mod orientation;
11mod rtpmap;
12
13pub use conference_type::ConferenceType;
14pub use fmtp::Fmtp;
15pub use orientation::Orientation;
16pub use rtpmap::Rtpmap;
17
18pub use crate::tokenizers::key_optvalue::Tokenizer;
21use std::convert::TryFrom;
22
23#[derive(Debug, PartialEq, PartialOrd, Clone)]
25pub enum Attribute {
26 Cat(String),
27 Keywds(String),
28 Tool(String),
29 Ptime(f32),
30 Maxptime(f32),
31 Rtpmap(Rtpmap),
32 Recvonly,
33 Sendrecv,
34 Sendonly,
35 Inactive,
36 Orient(Orientation),
37 Type(ConferenceType),
38 Charset(String),
39 Sdplang(String),
40 Lang(String),
41 Framerate(f32),
42 Quality(i32),
43 Other(String, Option<String>),
44}
45
46impl<'a> TryFrom<Tokenizer<'a, 'a'>> for Attribute {
48 type Error = crate::Error;
49
50 fn try_from(tokenizer: Tokenizer<'a, 'a'>) -> Result<Self, Self::Error> {
51 Ok(match (tokenizer.key, tokenizer.value) {
52 (key, Some(value)) if key.eq("cat") => Self::Cat(value.into()),
53 (key, Some(value)) if key.eq("keywds") => Self::Keywds(value.into()),
54 (key, Some(value)) if key.eq("tool") => Self::Tool(value.into()),
55 (key, value) if key.eq("ptime") => match value {
56 Some(value) => value
57 .parse()
58 .map(Self::Ptime)
59 .map_err(|e| crate::Error::parser_with_error("ptime attribute", value, e))?,
60 None => {
61 return Err(crate::Error::parser_with_error(
62 "ptime attribute",
63 "",
64 "missing value",
65 ))
66 }
67 },
68 (key, value) if key.eq("maxptime") => match value {
69 Some(value) => value
70 .parse()
71 .map(Self::Maxptime)
72 .map_err(|e| crate::Error::parser_with_error("maxptime attribute", value, e))?,
73 None => {
74 return Err(crate::Error::parser_with_error(
75 "maxptime attribute",
76 "",
77 "missing value",
78 ))
79 }
80 },
81 (key, value) if key.eq("rtpmap") => {
82 match value {
83 Some(value) => Self::Rtpmap(Rtpmap::try_from(value).map_err(|e| {
84 crate::Error::parser_with_error("rtpmap attribute", value, e)
85 })?),
86 None => {
87 return Err(crate::Error::parser_with_error(
88 "rtpmap attribute",
89 "",
90 "missing value",
91 ))
92 }
93 }
94 }
95 (key, None) if key.eq("recvonly") => Self::Recvonly,
96 (key, None) if key.eq("sendrecv") => Self::Sendrecv,
97 (key, None) if key.eq("sendonly") => Self::Sendonly,
98 (key, None) if key.eq("inacive") => Self::Inactive,
99 (key, value) if key.eq("orient") => match value {
100 Some(value) => Orientation::try_from(value)
101 .map(Self::Orient)
102 .map_err(|e| crate::Error::parser_with_error("orient attribute", value, e))?,
103 None => {
104 return Err(crate::Error::parser_with_error(
105 "orient attribute",
106 "",
107 "missing value",
108 ))
109 }
110 },
111 (key, value) if key.eq("type") => match value {
112 Some(value) => ConferenceType::try_from(value)
113 .map(Self::Type)
114 .map_err(|e| crate::Error::parser_with_error("type attribute", value, e))?,
115 None => return Err(crate::Error::parser_with_error("type", "", "missing value")),
116 },
117 (key, Some(value)) if key.eq("charset") => Self::Charset(value.into()),
118 (key, Some(value)) if key.eq("sdplang") => Self::Sdplang(value.into()),
119 (key, Some(value)) if key.eq("lang") => Self::Lang(value.into()),
120 (key, value) if key.eq("framerate") => match value {
121 Some(value) => value.parse().map(Self::Framerate).map_err(|e| {
122 crate::Error::parser_with_error("framerate attribute", value, e)
123 })?,
124 None => {
125 return Err(crate::Error::parser_with_error(
126 "framerate attribute",
127 "",
128 "missing value",
129 ))
130 }
131 },
132 (key, value) if key.eq("quality") => match value {
133 Some(value) => value
134 .parse()
135 .map(Self::Quality)
136 .map_err(|e| crate::Error::parser_with_error("quality attribute", value, e))?,
137 None => {
138 return Err(crate::Error::parser_with_error(
139 "quality attribute",
140 "",
141 "missing value",
142 ))
143 }
144 },
145 (key, value) => Self::Other(key.into(), value.map(Into::into)),
146 })
147 }
148}
149
150impl std::fmt::Display for Attribute {
151 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
152 match self {
153 Self::Cat(cat) => write!(f, "a=cat:{}", cat),
154 Self::Keywds(keywds) => write!(f, "a=keywds:{}", keywds),
155 Self::Tool(tool) => write!(f, "a=tool:{}", tool),
156 Self::Ptime(ptime) => write!(f, "a=ptime:{}", ptime),
157 Self::Maxptime(maxptime) => write!(f, "a=maxptime:{}", maxptime),
158 Self::Rtpmap(rtpmap) => write!(f, "a=rtpmap:{}", rtpmap),
159 Self::Recvonly => write!(f, "a=recvonly"),
160 Self::Sendrecv => write!(f, "a=sendrecv"),
161 Self::Sendonly => write!(f, "a=sendonly"),
162 Self::Inactive => write!(f, "a=inactive"),
163 Self::Orient(orientation) => write!(f, "a=orient:{}", orientation),
164 Self::Type(conference_type) => write!(f, "a=type:{}", conference_type),
165 Self::Charset(charset) => write!(f, "a=charset:{}", charset),
166 Self::Sdplang(sdplang) => write!(f, "a=sdplang:{}", sdplang),
167 Self::Lang(lang) => write!(f, "a=lang:{}", lang),
168 Self::Framerate(framerate) => write!(f, "a=framerate:{}", framerate),
169 Self::Quality(quality) => write!(f, "a=quality:{}", quality),
170 Self::Other(key, Some(value)) => write!(f, "a={}:{}", key, value),
171 Self::Other(key, None) => write!(f, "a={}", key),
172 }
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179
180 #[test]
181 fn from_tokenizer1() {
182 let tokenizer: Tokenizer<'a'> = ("key", Some("something")).into();
183
184 assert_eq!(
185 Attribute::try_from(tokenizer).unwrap(),
186 Attribute::Other("key".into(), Some("something".into()))
187 );
188 }
189
190 #[test]
191 fn from_tokenizer2() {
192 let tokenizer: Tokenizer<'a'> = ("orient", None).into();
193
194 assert!(Attribute::try_from(tokenizer).is_err(),);
195 }
196
197 #[test]
198 fn from_tokenizer3() {
199 let tokenizer: Tokenizer<'a'> = ("orient", Some("Portrait")).into();
200
201 assert!(Attribute::try_from(tokenizer).is_err(),);
202 }
203
204 #[test]
205 fn from_tokenizer4() {
206 let tokenizer: Tokenizer<'a'> = ("orient", Some("portrait")).into();
207
208 assert_eq!(
209 Attribute::try_from(tokenizer).unwrap(),
210 Attribute::Orient(Orientation::Portrait)
211 );
212 }
213
214 #[test]
215 fn display1() {
216 assert_eq!(
217 Attribute::Cat("foo.bar".to_string()).to_string(),
218 "a=cat:foo.bar"
219 );
220 }
221
222 #[test]
223 fn display2() {
224 assert_eq!(Attribute::Ptime(20.0).to_string(), "a=ptime:20");
225 }
226
227 #[test]
228 fn display3() {
229 assert_eq!(
230 Attribute::Orient(Orientation::Portrait).to_string(),
231 "a=orient:portrait"
232 );
233 }
234
235 #[test]
236 fn display4() {
237 assert_eq!(Attribute::Recvonly.to_string(), "a=recvonly");
238 }
239
240 #[test]
241 fn display5() {
242 assert_eq!(
243 Attribute::Other("foo".into(), Some("bar".into())).to_string(),
244 "a=foo:bar"
245 );
246 }
247
248 #[test]
249 fn display6() {
250 assert_eq!(Attribute::Other("foo".into(), None).to_string(), "a=foo");
251 }
252}