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