1use address::IPAddress;
2
3#[derive(Clone, Debug)]
12pub struct WebUrl {
13 pub(in crate::url) url: String,
14 pub(in crate::url) scheme_len: u32,
15 pub(in crate::url) host_end: u32,
16 pub(in crate::url) ip: Option<IPAddress>,
17 pub(in crate::url) port_end: u32,
18 pub(in crate::url) port: Option<u16>,
19 pub(in crate::url) path_end: u32,
20 pub(in crate::url) query_end: u32,
21}
22
23impl WebUrl {
24 #[allow(clippy::too_many_arguments)]
31 pub unsafe fn new<S>(
32 url: S,
33 scheme_len: u32,
34 host_end: u32,
35 ip: Option<IPAddress>,
36 port_end: u32,
37 port: Option<u16>,
38 path_end: u32,
39 query_end: u32,
40 ) -> Self
41 where
42 S: Into<String>,
43 {
44 Self {
45 url: url.into(),
46 scheme_len,
47 host_end,
48 ip,
49 port_end,
50 port,
51 path_end,
52 query_end,
53 }
54 }
55}
56
57impl WebUrl {
58 pub fn len(&self) -> usize {
62 self.url.len()
63 }
64
65 pub fn is_empty(&self) -> bool {
67 false
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use crate::parse::Error::*;
74 use crate::WebUrl;
75 use std::str::FromStr;
76
77 #[test]
78 fn len() {
79 let url = WebUrl::from_str("https://example.com/path").unwrap();
80 assert_eq!(url.len(), "https://example.com/path".len());
81 }
82
83 #[test]
84 fn is_empty() {
85 let url = WebUrl::from_str("https://example.com").unwrap();
86 assert!(!url.is_empty());
87 }
88
89 #[test]
90 fn parse_full_url() {
91 let url = WebUrl::from_str("https://example.com:8080/path?key=value#section").unwrap();
92 assert_eq!(url.scheme().as_str(), "https");
93 assert_eq!(url.port(), Some(8080));
94 assert_eq!(url.path().as_str(), "/path");
95 assert_eq!(url.query().unwrap().as_str(), "?key=value");
96 assert_eq!(url.fragment().unwrap().as_str(), "#section");
97 }
98
99 #[test]
100 fn parse_no_path_appends_slash() {
101 let url = WebUrl::from_str("https://example.com").unwrap();
102 assert_eq!(url.path().as_str(), "/");
103 assert_eq!(url.as_str(), "https://example.com/");
104 }
105
106 #[test]
107 fn parse_uppercase_lowercased() {
108 let url = WebUrl::from_str("HTTPS://EXAMPLE.COM/Path").unwrap();
109 assert_eq!(url.scheme().as_str(), "https");
110 assert_eq!(url.as_str(), "https://example.com/Path");
111 }
112
113 #[test]
114 fn parse_invalid_scheme() {
115 assert_eq!(WebUrl::from_str("").unwrap_err(), InvalidScheme);
116 assert_eq!(WebUrl::from_str("://host").unwrap_err(), InvalidScheme);
117 assert_eq!(WebUrl::from_str("0http://host").unwrap_err(), InvalidScheme);
118 }
119
120 #[test]
121 fn parse_invalid_host() {
122 assert_eq!(WebUrl::from_str("http://").unwrap_err(), InvalidHost);
123 assert_eq!(WebUrl::from_str("http://ho!st").unwrap_err(), InvalidHost);
124 }
125
126 #[test]
127 fn parse_invalid_port() {
128 assert_eq!(
129 WebUrl::from_str("http://host:bad").unwrap_err(),
130 InvalidPort
131 );
132 assert_eq!(
133 WebUrl::from_str("http://host:99999").unwrap_err(),
134 InvalidPort
135 );
136 }
137
138 #[test]
139 fn parse_invalid_path() {
140 assert_eq!(
141 WebUrl::from_str("http://host/path with space").unwrap_err(),
142 InvalidPath
143 );
144 }
145
146 #[test]
147 fn parse_invalid_query() {
148 assert_eq!(
149 WebUrl::from_str("http://host/path?query with space").unwrap_err(),
150 InvalidQuery
151 );
152 }
153
154 #[test]
155 fn parse_invalid_fragment() {
156 assert_eq!(
157 WebUrl::from_str("http://host/path# bad").unwrap_err(),
158 InvalidFragment
159 );
160 }
161
162 #[test]
163 fn try_from_string_ok() {
164 let input = String::from("https://example.com/path");
165 let url = WebUrl::try_from(input).unwrap();
166 assert_eq!(url.as_str(), "https://example.com/path");
167 }
168
169 #[test]
170 fn try_from_string_err_returns_string() {
171 let input = String::from("not a url");
172 let (error, returned) = WebUrl::try_from(input).unwrap_err();
173 assert_eq!(error, InvalidScheme);
174 assert_eq!(returned, "not a url");
175 }
176
177 #[test]
178 fn try_from_string_no_path() {
179 let input = String::from("https://example.com");
180 let url = WebUrl::try_from(input).unwrap();
181 assert_eq!(url.as_str(), "https://example.com/");
182 }
183}