1#[cfg(test)]
2mod uri_test;
3
4use shared::error::*;
5
6use std::fmt;
7
8pub const SCHEME: &str = "stun";
12pub const SCHEME_SECURE: &str = "stuns";
14
15#[derive(PartialEq, Eq, Debug)]
17pub struct Uri {
19 pub scheme: String,
21 pub host: String,
23 pub port: Option<u16>,
25}
26
27impl fmt::Display for Uri {
28 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29 let host = if self.host.contains("::") {
30 format!("[{}]", self.host)
31 } else {
32 self.host.clone()
33 };
34
35 if let Some(port) = self.port {
36 write!(f, "{}:{}:{}", self.scheme, host, port)
37 } else {
38 write!(f, "{}:{}", self.scheme, host)
39 }
40 }
41}
42
43impl Uri {
44 pub fn parse_uri(raw: &str) -> Result<Self> {
46 if raw.contains("//") {
48 return Err(Error::ErrInvalidUrl);
49 }
50
51 let mut s = raw.to_string();
52 let pos = raw.find(':');
53 let p = pos.ok_or(Error::ErrSchemeType)?;
54 s.replace_range(p..p + 1, "://");
55
56 let raw_parts = url::Url::parse(&s)?;
57
58 let scheme = raw_parts.scheme().into();
59 if scheme != SCHEME && scheme != SCHEME_SECURE {
60 return Err(Error::ErrSchemeType);
61 }
62
63 let host = raw_parts
64 .host_str()
65 .ok_or(Error::ErrHost)?
66 .trim()
67 .trim_start_matches('[')
68 .trim_end_matches(']')
69 .to_owned();
70
71 let port = raw_parts.port();
72
73 Ok(Uri { scheme, host, port })
74 }
75}