ftth_rsip/common/
version.rs

1use crate::Error;
2#[doc(hidden)]
3pub use tokenizer::Tokenizer;
4
5use std::convert::{TryFrom, TryInto};
6
7/// Simple enum that holds the SIP version. Defaults to `Version::V2`.
8#[derive(Debug, PartialEq, Eq, Clone)]
9pub enum Version {
10    V1,
11    V2,
12    //Custom(String)
13}
14
15impl Default for Version {
16    fn default() -> Self {
17        Self::V2
18    }
19}
20
21impl<'a> TryFrom<&'a [u8]> for Version {
22    type Error = crate::Error;
23
24    fn try_from(from: &'a [u8]) -> Result<Self, Self::Error> {
25        Tokenizer::tokenize(from)?.1.try_into()
26    }
27}
28
29impl std::fmt::Display for Version {
30    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31        match self {
32            Self::V1 => write!(f, "SIP/1.0"),
33            Self::V2 => write!(f, "SIP/2.0"),
34        }
35    }
36}
37
38impl<'a> std::convert::TryFrom<tokenizer::Tokenizer<'a, &'a str, char>> for Version {
39    type Error = Error;
40
41    fn try_from(tokenizer: tokenizer::Tokenizer<'a, &'a str, char>) -> Result<Self, Self::Error> {
42        match tokenizer.major {
43            "1" => Ok(Version::V1),
44            "2" => Ok(Version::V2),
45            _ => Err(Self::Error::ParseError("Unrecognized SIP version".into())),
46        }
47    }
48}
49
50impl<'a> std::convert::TryFrom<tokenizer::Tokenizer<'a, &'a [u8], u8>> for Version {
51    type Error = Error;
52
53    fn try_from(tokenizer: tokenizer::Tokenizer<'a, &'a [u8], u8>) -> Result<Self, Self::Error> {
54        match tokenizer.major {
55            b"1" => Ok(Version::V1),
56            b"2" => Ok(Version::V2),
57            _ => Err(Self::Error::ParseError("Unrecognized SIP version".into())),
58        }
59    }
60}
61
62#[doc(hidden)]
63mod tokenizer {
64    use crate::{AbstractInput, AbstractInputItem, GResult, GenericNomError, TokenizerError};
65    use std::marker::PhantomData;
66
67    #[derive(Debug, PartialEq, Eq, Clone)]
68    pub struct Tokenizer<'a, T, I>
69    where
70        T: AbstractInput<'a, I>,
71        I: AbstractInputItem<I>,
72    {
73        pub major: T,
74        pub minor: T,
75        phantom1: PhantomData<&'a T>,
76        phantom2: PhantomData<I>,
77    }
78
79    impl<'a, T, I> From<(T, T)> for Tokenizer<'a, T, I>
80    where
81        T: AbstractInput<'a, I>,
82        I: AbstractInputItem<I>,
83    {
84        fn from(from: (T, T)) -> Self {
85            Self {
86                major: from.0,
87                minor: from.1,
88                phantom1: PhantomData,
89                phantom2: PhantomData,
90            }
91        }
92    }
93
94    impl<'a, T, I> Tokenizer<'a, T, I>
95    where
96        T: AbstractInput<'a, I>,
97        I: AbstractInputItem<I>,
98    {
99        pub fn tokenize(part: T) -> GResult<T, Self> {
100            use nom::{bytes::complete::tag, character::complete::digit1, sequence::tuple};
101
102            let (rem, (_, major, _, minor)) = tuple((tag("SIP/"), digit1, tag("."), digit1))(part)
103                .map_err(|_: GenericNomError<'a, T>| {
104                    TokenizerError::from(("version", part)).into()
105                })?;
106
107            Ok((rem, (major, minor).into()))
108        }
109    }
110}
111
112#[cfg(feature = "test-utils")]
113impl testing_utils::Randomize for Version {
114    fn random() -> Self {
115        //is anyone using V1 ?!
116        Self::V2
117    }
118}