use std::net::Ipv4Addr; use log::error; use std::fmt::{Display, Formatter, Write}; use crate::prudp::station_url::Type::{PRUDP, PRUDPS, UDP}; use crate::prudp::station_url::UrlOptions::{Address, ConnectionID, NatFiltering, NatMapping, NatType, Platform, PMP, Port, PrincipalID, RVConnectionID, StreamID, StreamType, UPNP}; pub enum Type{ UDP, PRUDP, PRUDPS } pub mod nat_types{ pub const BEHIND_NAT: u8 = 1; pub const PUBLIC: u8 = 2; } pub enum UrlOptions { Address(Ipv4Addr), Port(u16), StreamType(u8), StreamID(u8), ConnectionID(u8), PrincipalID(u32), NatType(u8), NatMapping(u8), NatFiltering(u8), UPNP(u8), RVConnectionID(u32), Platform(u8), PMP(u8), } pub struct StationUrl{ pub url_type: Type, pub options: Vec } impl StationUrl{ fn read_options(options: &str) -> Option>{ let mut options_out = Vec::new(); for option in options.split(';'){ if option == "" { continue; } let mut option_parts = option.split('='); let option_name= option_parts.next()?.to_ascii_lowercase(); let option_value = option_parts.next()?; match option_name.as_ref(){ "address" => { options_out.push(Address(option_value.parse().ok()?)) }, "port" => { options_out.push(Port(option_value.parse().ok()?)) } "natf" => { options_out.push(NatFiltering(option_value.parse().ok()?)) } "natm" => { options_out.push(NatMapping(option_value.parse().ok()?)) } "sid" => { options_out.push(StreamID(option_value.parse().ok()?)) } "upnp" => { options_out.push(UPNP(option_value.parse().ok()?)) } "type" => { options_out.push(NatType(option_value.parse().ok()?)) } "stream" => { options_out.push(StreamType(option_value.parse().ok()?)) } "RVCID" => { options_out.push(RVConnectionID(option_value.parse().ok()?)) } "pl" => { options_out.push(Platform(option_value.parse().ok()?)) } "pmp" => { options_out.push(PMP(option_value.parse().ok()?)) } _ => { error!("unimplemented option type, skipping: {}", option_name); } } } Some(options_out) } } impl TryFrom<&str> for StationUrl{ type Error = (); fn try_from(value: &str) -> Result { let (url_type, options) = value.split_at(value.find(":/").ok_or(())?); let options = &options[2..]; let url_type = match url_type{ "udp" => UDP, "prudp" => PRUDP, "prudps" => PRUDPS, _ => return Err(()) }; let options = Self::read_options(options).ok_or(())?; Ok( Self{ url_type, options } ) } } impl<'a> Into for &'a StationUrl{ fn into(self) -> String { let mut url = match self.url_type{ UDP => "udp:/", PRUDP => "prudp:/", PRUDPS => "prudps:/" }.to_owned(); for option in &self.options{ match option{ Address(v) => write!(url, "address={}", v).expect("failed to write"), Port(v) => write!(url, "port={}", v).expect("failed to write"), StreamType(v) => write!(url, "stream={}", v).expect("failed to write"), StreamID(v) => write!(url, "sid={}", v).expect("failed to write"), ConnectionID(v) => write!(url, "CID={}", v).expect("failed to write"), PrincipalID(v) => write!(url, "PID={}", v).expect("failed to write"), NatType(v) => write!(url, "type={}", v).expect("failed to write"), NatMapping(v) => write!(url, "natm={}", v).expect("failed to write"), NatFiltering(v) => write!(url, "natf={}", v).expect("failed to write"), UPNP(v) => write!(url, "upnp={}", v).expect("failed to write"), RVConnectionID(v) => write!(url, "RVCID={}", v).expect("failed to write"), Platform(v) => write!(url, "pl={}", v).expect("failed to write"), PMP(v) => write!(url, "pmp={}", v).expect("failed to write"), } } url } } impl Display for StationUrl{ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let str: String = self.into(); write!(f, "{}", str) } }