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