1use ntex_bytes::ByteString;
2
3use crate::authority::Authority;
4use crate::error::{ErrorKind, InvalidUrl, InvalidUrlParts};
5use crate::{parse, path::PathAndQuery, query::Fragment, scheme::Scheme, url::Url};
6
7#[derive(Debug, Default, Clone)]
32pub struct Builder {
33 base: Option<Url>,
34 scheme: Option<String>,
35 authority: Option<String>,
36 host: Option<String>,
37 userinfo: Option<(String, Option<String>)>,
38 port: Option<u16>,
39 path: Option<String>,
40 query: Option<String>,
41 pairs: Vec<(String, String)>,
42 fragment: Option<String>,
43}
44
45impl Builder {
46 pub fn new() -> Self {
48 Self::default()
49 }
50
51 #[must_use]
53 pub fn scheme(mut self, scheme: &str) -> Self {
54 self.scheme = Some(scheme.to_string());
55 self
56 }
57
58 #[must_use]
62 pub fn authority(mut self, authority: &str) -> Self {
63 self.authority = Some(authority.to_string());
64 self
65 }
66
67 #[must_use]
69 pub fn host(mut self, host: &str) -> Self {
70 self.host = Some(host.to_string());
71 self
72 }
73
74 #[must_use]
76 pub fn userinfo(mut self, username: &str, password: Option<&str>) -> Self {
77 self.userinfo = Some((username.to_string(), password.map(str::to_string)));
78 self
79 }
80
81 #[must_use]
83 pub fn port(mut self, port: u16) -> Self {
84 self.port = Some(port);
85 self
86 }
87
88 #[must_use]
90 pub fn path(mut self, path: &str) -> Self {
91 self.path = Some(path.to_string());
92 self
93 }
94
95 #[must_use]
97 pub fn query(mut self, query: &str) -> Self {
98 self.query = Some(query.to_string());
99 self
100 }
101
102 #[must_use]
104 pub fn query_pair(mut self, key: &str, value: &str) -> Self {
105 self.pairs.push((key.to_string(), value.to_string()));
106 self
107 }
108
109 #[must_use]
111 pub fn query_pairs<I, K, V>(mut self, pairs: I) -> Self
112 where
113 I: IntoIterator<Item = (K, V)>,
114 K: AsRef<str>,
115 V: AsRef<str>,
116 {
117 self.pairs.extend(
118 pairs
119 .into_iter()
120 .map(|(k, v)| (k.as_ref().to_string(), v.as_ref().to_string())),
121 );
122 self
123 }
124
125 #[must_use]
127 pub fn fragment(mut self, fragment: &str) -> Self {
128 self.fragment = Some(fragment.to_string());
129 self
130 }
131
132 pub fn build(self) -> Result<Url, InvalidUrl> {
134 let mut url = self.base.unwrap_or_else(Url::empty);
135 if let Some(scheme) = self.scheme {
136 url.set_scheme(&scheme)?;
137 }
138 if let Some(authority) = self.authority {
139 url.set_authority(Some(&authority))?;
140 }
141 if let Some(host) = self.host {
142 url.set_host(&host)?;
143 }
144 if let Some((user, password)) = self.userinfo {
145 url.set_userinfo(Some(&user), password.as_deref())?;
146 }
147 if self.port.is_some() {
148 url.set_port(self.port)?;
149 }
150 if let Some(path) = self.path {
151 url.set_path(&path);
152 }
153 if let Some(query) = self.query {
154 url.set_query(Some(&query));
155 }
156 if !self.pairs.is_empty() {
157 url.extend_query_pairs(self.pairs);
158 }
159 if let Some(fragment) = self.fragment {
160 url.set_fragment(Some(&fragment));
161 }
162 if url.as_str().is_empty() {
163 Err(InvalidUrl::new(ErrorKind::Empty))
164 } else {
165 Ok(url)
166 }
167 }
168}
169
170impl From<Url> for Builder {
171 fn from(url: Url) -> Self {
172 Builder {
173 base: Some(url),
174 ..Default::default()
175 }
176 }
177}
178
179#[derive(Debug, Default, Clone)]
184pub struct Parts {
185 pub scheme: Option<ByteString>,
187 pub authority: Option<ByteString>,
189 pub path_and_query: ByteString,
191 pub fragment: Option<ByteString>,
193}
194
195impl Url {
196 pub fn from_parts(parts: Parts) -> Result<Url, InvalidUrlParts> {
211 let invalid_path = || InvalidUrlParts::from(InvalidUrl::new(ErrorKind::InvalidPath));
212
213 let pq = PathAndQuery::new(&parts.path_and_query)?;
214 let path = pq.path().as_str();
215 if let Some(scheme) = &parts.scheme {
216 Scheme::new(scheme)?;
217 }
218 if let Some(authority) = &parts.authority {
219 Authority::new(authority)?;
220 if !path.is_empty() && !path.starts_with('/') {
221 return Err(invalid_path());
222 }
223 } else if parts.scheme.is_none() && path.split('/').next().unwrap_or("").contains(':') {
224 return Err(invalid_path());
225 }
226 if let Some(fragment) = &parts.fragment {
227 Fragment::new(fragment)?;
228 }
229
230 let mut s = String::new();
231 if let Some(scheme) = &parts.scheme {
232 s.push_str(scheme);
233 s.push(':');
234 }
235 if let Some(authority) = &parts.authority {
236 s.push_str("//");
237 s.push_str(authority);
238 } else if path.starts_with("//") {
239 s.push_str("/.");
240 }
241 s.push_str(&parts.path_and_query);
242 if let Some(fragment) = &parts.fragment {
243 s.push('#');
244 s.push_str(fragment);
245 }
246 if s.is_empty() {
247 return Err(InvalidUrl::new(ErrorKind::Empty).into());
248 }
249 Ok(parse::parse(&s, None)?)
250 }
251}