1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
//!A common general purpose library for SDP. It can parse and generate all SDP
//!structures. Supports both [RFC8866](https://www.rfc-editor.org/rfc/rfc8866.html) and
//![RFC4566](https://www.rfc-editor.org/rfc/rfc4566.html).
//!
//!Like [rsip](https://docs.rs/rsip/latest/rsip/), this crate is a general purpose library for
//!common types found when working with the SDP protocol.
//!You will find high level types like the [SessionDescription], [MediaDescription] and [Time]
//!but you will also find line-level types like [lines::Connection] or even types found inside a
//!line, like [lines::bandwidth::Bwtype] etc.
//!
//!`sdp-rs` is capable of parsing messages from &str or String using
//![nom](https://github.com/Geal/nom)
//!parser and can also generate SDP messages using the main `SessionDescription` struct. Each type
//!(high level type, line type or sub-line type) can be parsed or be displayed as it would,
//!so you can work with part of an SDP message, if that's useful for you.
//!
//!If you need parsing raw bytes (`&[u8]`) ping us. It is possible but we avoided doing that in the
//!first place because a) it requires tons of traits/generics which will increase complexity and
//!compile times b) SDP specification highly recommends that the input is UTF-8 c) performance of
//!converting the bytes to UTF-8 should be negligible.
//!
//!## Features
//!* This thing is _fast_, uses nom for basic message parsing.
//!* Strong (new)types in most cases. Whenever for a type there is a strict specification, we opt
//!  for a strict (newtype) definition.
//!* Very simple code structure make it super easy to extend and add new SDP lines and attributes
//!  As long as you can do [nom](https://github.com/Geal/nom) stuff, it's straightforward.
//!  If you find dealing with nom difficult, you can always open an issue for the desired (missing)
//!  type. The goal is to add as many typed SDP attributes as possible.
//!
//!## Architecture
//!Each type in `sdp-rs` has a related tokenizer.
//!This is not enforced by the type system yet, however very soon this will be the case.
//!In brief, for every `sdp-rs` type we have:
//!* Tokenizing: in the lowest level we have the `Tokenizer` which is capable of tokenizing the
//!  input. All common tokenizers accept the `&str` input. You shouldn't have to work directly with
//!  the tokenizers, these are being used indirectly in the parsing level.
//!* Parsing: once the input has been tokenized into tokens, then there are `TryFrom` impls from the
//!  relevant type tokenizer to the actual type. This is the parsing step where tokens (in the form
//!  of `&str`) are transformed to integers, strings or `sdp-rs` types.
//!* each `sdp-rs` type implements the `Display` trait and hence has a representation.

mod error;
pub mod lines;
mod media_description;
mod session_description;
mod time;
#[doc(hidden)]
pub mod tokenizers;

pub use error::Error;
pub(crate) use error::TokenizerError;
pub use media_description::MediaDescription;
pub use session_description::SessionDescription;
pub use time::Time;

pub(crate) type TResult<'a, T> = Result<(&'a str, T), nom::Err<TokenizerError>>;
pub(crate) type SResult<'a> = Result<(&'a str, &'a str), nom::Err<TokenizerError>>;

//TODO: add tests
pub(crate) mod parser_utils {
    use crate::SResult;

    pub fn until_stopbreak_of<'a>(stopbreak: &'a str) -> impl FnMut(&'a str) -> SResult<'a> {
        use nom::{
            bytes::complete::{tag, take_until},
            sequence::terminated,
        };

        terminated(take_until(stopbreak), tag(stopbreak))
    }

    pub fn until_space(part: &str) -> SResult {
        use nom::{
            bytes::complete::{tag, take_until},
            sequence::terminated,
        };

        terminated(take_until(" "), tag(" "))(part)
    }

    pub fn until_newline(part: &str) -> SResult {
        use nom::branch::alt;

        alt((until_crlf, until_cr, until_lf))(part)
    }

    fn until_crlf(part: &str) -> SResult {
        use nom::{
            bytes::complete::{tag, take_until},
            sequence::terminated,
        };

        terminated(take_until("\r\n"), tag("\r\n"))(part)
    }

    fn until_cr(part: &str) -> SResult {
        use nom::{
            bytes::complete::{tag, take_until},
            sequence::terminated,
        };

        terminated(take_until("\r"), tag("\r"))(part)
    }

    fn until_lf(part: &str) -> SResult {
        use nom::{
            bytes::complete::{tag, take_until},
            sequence::terminated,
        };

        terminated(take_until("\n"), tag("\n"))(part)
    }
}