sdp_rs/lines/attribute/
mod.rs

1//! Types related to the attribute line (`a=`).
2//!
3//! If a (common) attribute that has clear syntax
4//! (either through an ABNF syntax or through something else) is missing from here, feel free to
5//! open an issue. With nom it should be easy to create a typed form of it instead of dealing with
6//! the `Attribute::Other` variant.
7
8mod 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
18/// The attribute line (`a=`) tokenizer. This is low level stuff and you shouldn't interact directly
19/// with it, unless you know what you are doing.
20pub use crate::tokenizers::key_optvalue::Tokenizer;
21use std::convert::TryFrom;
22
23/// foo bar
24#[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
46//TODO: add warning log on errors behind a feature flag
47impl<'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}