rsip/common/uri/param/
mod.rs

1#[doc(hidden)]
2pub use tokenizer::Tokenizer;
3
4pub mod branch;
5pub mod expires;
6pub mod maddr;
7pub mod q;
8pub mod received;
9pub mod tag;
10pub mod ttl;
11pub mod user;
12
13pub use branch::Branch;
14pub use expires::Expires;
15pub use maddr::Maddr;
16pub use q::Q;
17pub use received::Received;
18pub use tag::Tag;
19pub use ttl::Ttl;
20pub use user::User;
21
22use crate::{Error, Method, Transport};
23use rsip_derives::NewType;
24use std::convert::TryInto;
25
26/// This enum holds all the possible parameters found in SIP(S) URIs, and headers like `From`,
27/// `To`, `Contact`, `Via` etc. For better safety, we should probably define different param
28/// enums for each of those cases since, for instance, a `branch` parameter should not appear
29/// in a `Contact` header, however we have it in the same enum for simplicity for now and delegate
30/// this safety to the user.
31//TODO: move out Via/From/etc params from here, but keep the same tokenizer
32#[derive(Debug, PartialEq, Eq, Clone)]
33pub enum Param {
34    Transport(Transport),
35    User(User),
36    Method(Method),
37    Ttl(Ttl),
38    Maddr(Maddr),
39    Lr,
40    Branch(Branch),     //param belonging to Via header but added here for simplicity
41    Received(Received), //param belonging to Via header but added here for simplicity
42    Tag(Tag),           //param belonging to From header but added here for simplicity
43    Expires(Expires),   //param belonging to Contact header but added here for simplicity
44    Q(Q),               //param belonging to Contact header but added here for simplicity
45    Other(OtherParam, Option<OtherParamValue>),
46}
47
48#[derive(NewType, Debug, PartialEq, Eq, Clone)]
49pub struct OtherParam(String);
50#[derive(NewType, Debug, PartialEq, Eq, Clone)]
51pub struct OtherParamValue(String);
52
53impl std::fmt::Display for Param {
54    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55        match &self {
56            Self::Transport(transport) => write!(f, ";transport={}", transport),
57            Self::User(user) => write!(f, ";user={}", user),
58            Self::Method(method) => write!(f, ";method={}", method),
59            Self::Ttl(ttl) => write!(f, ";ttl={}", ttl),
60            Self::Maddr(maddr) => write!(f, ";maddr={}", maddr),
61            Self::Lr => write!(f, ";lr"),
62            Self::Branch(branch) => write!(f, ";branch={}", branch),
63            Self::Received(received) => write!(f, ";received={}", received),
64            Self::Tag(tag) => write!(f, ";tag={}", tag),
65            Self::Expires(expires) => write!(f, ";expires={}", expires),
66            Self::Q(q) => write!(f, ";q={}", q),
67            Self::Other(name, Some(value)) => write!(f, ";{}={}", name, value),
68            Self::Other(name, None) => write!(f, ";{}", name),
69        }
70    }
71}
72
73impl<'a> std::convert::TryFrom<tokenizer::Tokenizer<'a, &'a str, char>> for Param {
74    type Error = Error;
75
76    fn try_from(tokenizer: tokenizer::Tokenizer<'a, &'a str, char>) -> Result<Self, Self::Error> {
77        (tokenizer.name, tokenizer.value).try_into()
78    }
79}
80
81impl<'a> std::convert::TryFrom<tokenizer::Tokenizer<'a, &'a [u8], u8>> for Param {
82    type Error = Error;
83
84    fn try_from(tokenizer: tokenizer::Tokenizer<'a, &'a [u8], u8>) -> Result<Self, Self::Error> {
85        use std::str::from_utf8;
86
87        Self::try_from(Tokenizer::from((
88            from_utf8(tokenizer.name)?,
89            tokenizer.value.map(from_utf8).transpose()?,
90        )))
91    }
92}
93
94impl<'a> std::convert::TryFrom<(&'a str, Option<&'a str>)> for Param {
95    type Error = Error;
96
97    fn try_from(from: (&'a str, Option<&'a str>)) -> Result<Self, Self::Error> {
98        use std::str::FromStr;
99
100        match (from.0, from.1) {
101            (s, Some(v)) if s.eq_ignore_ascii_case("transport") => {
102                Ok(Param::Transport(Transport::from_str(v)?))
103            }
104            (s, Some(v)) if s.eq_ignore_ascii_case("user") => Ok(Param::User(User::new(v))),
105            (s, Some(v)) if s.eq_ignore_ascii_case("method") => {
106                Ok(Param::Method(Method::from_str(v)?))
107            }
108            (s, Some(v)) if s.eq_ignore_ascii_case("ttl") => Ok(Param::Ttl(Ttl::new(v))),
109            (s, Some(v)) if s.eq_ignore_ascii_case("maddr") => Ok(Param::Maddr(Maddr::new(v))),
110            (s, Some(v)) if s.eq_ignore_ascii_case("branch") => Ok(Param::Branch(Branch::new(v))),
111            (s, Some(v)) if s.eq_ignore_ascii_case("received") => {
112                Ok(Param::Received(Received::new(v)))
113            }
114            (s, Some(v)) if s.eq_ignore_ascii_case("tag") => Ok(Param::Tag(Tag::new(v))),
115            (s, Some(v)) if s.eq_ignore_ascii_case("expires") => {
116                Ok(Param::Expires(Expires::new(v)))
117            }
118            (s, Some(v)) if s.eq_ignore_ascii_case("q") => Ok(Param::Q(Q::new(v))),
119            (s, None) if s.eq_ignore_ascii_case("lr") => Ok(Param::Lr),
120            (s, v) => Ok(Param::Other(s.into(), v.map(Into::into))),
121        }
122    }
123}
124
125#[doc(hidden)]
126pub mod tokenizer {
127    use crate::{AbstractInput, AbstractInputItem, GResult, GenericNomError, TokenizerError};
128    use std::marker::PhantomData;
129
130    #[derive(Debug, PartialEq, Eq, Clone)]
131    pub struct Tokenizer<'a, T, I>
132    where
133        T: AbstractInput<'a, I>,
134        I: AbstractInputItem<I>,
135    {
136        pub name: T,
137        pub value: Option<T>,
138        phantom1: PhantomData<&'a T>,
139        phantom2: PhantomData<I>,
140    }
141
142    impl<'a, T, I> From<(T, Option<T>)> for Tokenizer<'a, T, I>
143    where
144        T: AbstractInput<'a, I>,
145        I: AbstractInputItem<I>,
146    {
147        fn from(from: (T, Option<T>)) -> Self {
148            Self {
149                name: from.0,
150                value: from.1,
151                phantom1: PhantomData,
152                phantom2: PhantomData,
153            }
154        }
155    }
156
157    impl<'a, T, I> Tokenizer<'a, T, I>
158    where
159        T: AbstractInput<'a, I>,
160        I: AbstractInputItem<I>,
161    {
162        pub fn tokenize(part: T) -> GResult<T, Self> {
163            use nom::{
164                bytes::complete::{tag, take_while},
165                combinator::{map, opt},
166                sequence::tuple,
167            };
168
169            let (rem, (_, name, value)) = tuple((
170                tag(";"),
171                take_while(I::is_token), //rfc3261 includes other chars as well, needs fixing..
172                opt(map(tuple((tag("="), take_while(I::is_token))), |t| t.1)),
173            ))(part)
174            .map_err(|_: GenericNomError<'a, T>| {
175                TokenizerError::from(("uri param", part)).into()
176            })?;
177
178            Ok((rem, (name, value).into()))
179        }
180    }
181}
182
183#[cfg(feature = "test-utils")]
184impl testing_utils::Randomize for Param {
185    fn random() -> Self {
186        use testing_utils::{rand_str_of, sample, Randomize};
187        sample(&[
188            Param::Transport(Randomize::random()),
189            Param::Method(Randomize::random()),
190            Param::User(Randomize::random()),
191            Param::Ttl(Randomize::random()),
192            Param::Maddr(Randomize::random()),
193            Param::Lr,
194            Param::Branch(Randomize::random()),
195            Param::Received(Randomize::random()),
196            Param::Tag(Randomize::random()),
197            Param::Expires(Randomize::random()),
198            Param::Q(Randomize::random()),
199            Param::Other(
200                rand_str_of(3).into(),
201                sample(&[None, Some(rand_str_of(5).into())]),
202            ),
203        ])
204    }
205}