oddity-rtsp-protocol 0.2.0

RTSP protocol implementation for oddity-rtsp-server.
Documentation
use std::fmt;
use std::str::FromStr;

use super::Error;

#[derive(Debug, Clone, PartialEq)]
pub struct Range {
    pub start: Option<NptTime>,
    pub end: Option<NptTime>,
}

impl Range {
    const SUPPORTED_UNITS: [&'static str; 1] = ["npt"];

    pub fn new(start: NptTime, end: NptTime) -> Range {
        Range {
            start: Some(start),
            end: Some(end),
        }
    }

    pub fn new_for_live() -> Range {
        Range {
            start: Some(NptTime::Now),
            end: None,
        }
    }
}

impl fmt::Display for Range {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "npt=")?;
        match (self.start.as_ref(), self.end.as_ref()) {
            (Some(start), Some(end)) => write!(f, "{}-{}", start, end),
            (Some(start), None) => write!(f, "{}-", start),
            (None, Some(end)) => write!(f, "-{}", end),
            (None, None) => write!(f, "-"),
        }
    }
}

impl FromStr for Range {
    type Err = Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.split_once(';') {
            None => {
                if let Some((unit, value)) = s.split_once('=') {
                    if Self::SUPPORTED_UNITS.contains(&unit) {
                        if let Some((start, end)) = value.split_once('-') {
                            let start = if !start.is_empty() {
                                Some(start.parse()?)
                            } else {
                                None
                            };
                            let end = if !end.is_empty() {
                                Some(end.parse()?)
                            } else {
                                None
                            };
                            Ok(Range { start, end })
                        } else {
                            Err(Error::RangeMalformed {
                                value: s.to_string(),
                            })
                        }
                    } else {
                        Err(Error::RangeUnitNotSupported {
                            value: s.to_string(),
                        })
                    }
                } else {
                    Err(Error::RangeMalformed {
                        value: s.to_string(),
                    })
                }
            }
            Some((_, time)) => {
                if time.starts_with("time=") {
                    Err(Error::RangeTimeNotSupported {
                        value: s.to_string(),
                    })
                } else {
                    Err(Error::RangeMalformed {
                        value: s.to_string(),
                    })
                }
            }
        }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub enum NptTime {
    Now,
    Time(f64),
}

impl fmt::Display for NptTime {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            NptTime::Now => write!(f, "now"),
            NptTime::Time(seconds) => write!(f, "{:.3}", seconds),
        }
    }
}

impl FromStr for NptTime {
    type Err = Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "now" => Ok(NptTime::Now),
            s => match *s.split(':').collect::<Vec<_>>().as_slice() {
                [npt_time] => {
                    let npt_time =
                        npt_time
                            .parse::<f64>()
                            .map_err(|_| Error::RangeNptTimeMalfored {
                                value: s.to_string(),
                            })?;
                    Ok(NptTime::Time(npt_time))
                }
                [npt_hh, npt_mm, npt_ss] => {
                    let npt_hh = npt_hh.parse::<u32>();
                    let npt_mm = npt_mm.parse::<u32>();
                    let npt_secs = npt_ss.parse::<f32>();
                    match (npt_hh, npt_mm, npt_secs) {
                        (Ok(hh), Ok(mm), Ok(secs)) => {
                            let npt_time =
                                ((hh * 3600) as f64) + ((mm * 60) as f64) + (secs as f64);
                            Ok(NptTime::Time(npt_time))
                        }
                        _ => Err(Error::RangeNptTimeMalfored {
                            value: s.to_string(),
                        }),
                    }
                }
                _ => Err(Error::RangeNptTimeMalfored {
                    value: s.to_string(),
                }),
            },
        }
    }
}