/rust/registry/src/index.crates.io-1949cf8c6b5b557f/iri-string-0.7.9/src/build.rs
Line | Count | Source |
1 | | //! URI/IRI builder. |
2 | | //! |
3 | | //! See the documentation of [`Builder`] type. |
4 | | |
5 | | use core::fmt::{self, Display as _, Write as _}; |
6 | | use core::marker::PhantomData; |
7 | | |
8 | | #[cfg(feature = "alloc")] |
9 | | use alloc::collections::TryReserveError; |
10 | | #[cfg(all(feature = "alloc", not(feature = "std")))] |
11 | | use alloc::string::ToString; |
12 | | |
13 | | use crate::format::Censored; |
14 | | #[cfg(feature = "alloc")] |
15 | | use crate::format::{ToDedicatedString, ToStringFallible}; |
16 | | use crate::normalize::{self, NormalizationMode, PathCharacteristic, PctCaseNormalized}; |
17 | | use crate::parser::str::{find_split, prior_byte2}; |
18 | | use crate::parser::validate as parser; |
19 | | use crate::spec::Spec; |
20 | | use crate::types::{RiAbsoluteStr, RiReferenceStr, RiRelativeStr, RiStr}; |
21 | | #[cfg(feature = "alloc")] |
22 | | use crate::types::{RiAbsoluteString, RiReferenceString, RiRelativeString, RiString}; |
23 | | use crate::validate::Error; |
24 | | |
25 | | /// Port builder. |
26 | | /// |
27 | | /// This type is intended to be created by `From` trait implementations, and |
28 | | /// to be passed to [`Builder::port`] method. |
29 | | #[derive(Debug, Clone)] |
30 | | pub struct PortBuilder<'a>(PortBuilderRepr<'a>); |
31 | | |
32 | | impl Default for PortBuilder<'_> { |
33 | | #[inline] |
34 | 0 | fn default() -> Self { |
35 | 0 | Self(PortBuilderRepr::Empty) |
36 | 0 | } |
37 | | } |
38 | | |
39 | | impl From<u8> for PortBuilder<'_> { |
40 | | #[inline] |
41 | 0 | fn from(v: u8) -> Self { |
42 | 0 | Self(PortBuilderRepr::Integer(v.into())) |
43 | 0 | } |
44 | | } |
45 | | |
46 | | impl From<u16> for PortBuilder<'_> { |
47 | | #[inline] |
48 | 0 | fn from(v: u16) -> Self { |
49 | 0 | Self(PortBuilderRepr::Integer(v)) |
50 | 0 | } |
51 | | } |
52 | | |
53 | | impl<'a> From<&'a str> for PortBuilder<'a> { |
54 | | #[inline] |
55 | 0 | fn from(v: &'a str) -> Self { |
56 | 0 | Self(PortBuilderRepr::String(v)) |
57 | 0 | } |
58 | | } |
59 | | |
60 | | #[cfg(feature = "alloc")] |
61 | | impl<'a> From<&'a alloc::string::String> for PortBuilder<'a> { |
62 | | #[inline] |
63 | 0 | fn from(v: &'a alloc::string::String) -> Self { |
64 | 0 | Self(PortBuilderRepr::String(v.as_str())) |
65 | 0 | } |
66 | | } |
67 | | |
68 | | /// Internal representation of a port builder. |
69 | | #[derive(Debug, Clone, Copy)] |
70 | | #[non_exhaustive] |
71 | | enum PortBuilderRepr<'a> { |
72 | | /// Empty port. |
73 | | Empty, |
74 | | /// Port as an integer. |
75 | | /// |
76 | | /// Note that RFC 3986 accepts any number of digits as a port, but |
77 | | /// practically (at least in TCP/IP) `u16` is enough. |
78 | | Integer(u16), |
79 | | /// Port as a string. |
80 | | String(&'a str), |
81 | | } |
82 | | |
83 | | /// Userinfo builder. |
84 | | /// |
85 | | /// This type is intended to be created by `From` trait implementations, and |
86 | | /// to be passed to [`Builder::userinfo`] method. |
87 | | #[derive(Clone)] |
88 | | pub struct UserinfoBuilder<'a>(UserinfoRepr<'a>); |
89 | | |
90 | | impl Default for UserinfoBuilder<'_> { |
91 | | #[inline] |
92 | 0 | fn default() -> Self { |
93 | 0 | Self(UserinfoRepr::None) |
94 | 0 | } |
95 | | } |
96 | | |
97 | | impl fmt::Debug for UserinfoBuilder<'_> { |
98 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
99 | 0 | let mut debug = f.debug_struct("UserinfoBuilder"); |
100 | 0 | if let Some((user, password)) = self.to_user_password() { |
101 | 0 | debug.field("user", &user); |
102 | | // > Applications should not render as clear text any data after |
103 | | // > the first colon (":") character found within a userinfo |
104 | | // > subcomponent unless the data after the colon is the empty |
105 | | // > string (indicating no password). |
106 | 0 | if matches!(password, None | Some("")) { |
107 | 0 | debug.field("password", &password); |
108 | 0 | } else { |
109 | 0 | debug.field("password", &Some(Censored)); |
110 | 0 | } |
111 | 0 | } |
112 | 0 | debug.finish() |
113 | 0 | } |
114 | | } |
115 | | |
116 | | impl<'a> UserinfoBuilder<'a> { |
117 | | /// Decomposes the userinfo into `user` and `password`. |
118 | | #[must_use] |
119 | 0 | fn to_user_password(&self) -> Option<(&'a str, Option<&'a str>)> { |
120 | 0 | match &self.0 { |
121 | 0 | UserinfoRepr::None => None, |
122 | 0 | UserinfoRepr::Direct(s) => match find_split(s, b':') { |
123 | 0 | None => Some((s, None)), |
124 | 0 | Some((user, password)) => Some((user, Some(password))), |
125 | | }, |
126 | 0 | UserinfoRepr::UserPass(user, password) => Some((*user, *password)), |
127 | | } |
128 | 0 | } |
129 | | } |
130 | | |
131 | | impl<'a> From<&'a str> for UserinfoBuilder<'a> { |
132 | | #[inline] |
133 | 0 | fn from(direct: &'a str) -> Self { |
134 | 0 | Self(UserinfoRepr::Direct(direct)) |
135 | 0 | } |
136 | | } |
137 | | |
138 | | impl<'a> From<(&'a str, &'a str)> for UserinfoBuilder<'a> { |
139 | | #[inline] |
140 | 0 | fn from((user, password): (&'a str, &'a str)) -> Self { |
141 | 0 | Self(UserinfoRepr::UserPass(user, Some(password))) |
142 | 0 | } |
143 | | } |
144 | | |
145 | | impl<'a> From<(&'a str, Option<&'a str>)> for UserinfoBuilder<'a> { |
146 | | #[inline] |
147 | 0 | fn from((user, password): (&'a str, Option<&'a str>)) -> Self { |
148 | 0 | Self(UserinfoRepr::UserPass(user, password)) |
149 | 0 | } |
150 | | } |
151 | | |
152 | | #[cfg(feature = "alloc")] |
153 | | impl<'a> From<&'a alloc::string::String> for UserinfoBuilder<'a> { |
154 | | #[inline] |
155 | 0 | fn from(v: &'a alloc::string::String) -> Self { |
156 | 0 | Self::from(v.as_str()) |
157 | 0 | } |
158 | | } |
159 | | |
160 | | /// Internal representation of a userinfo builder. |
161 | | #[derive(Clone, Copy)] |
162 | | enum UserinfoRepr<'a> { |
163 | | /// Not specified (absent). |
164 | | None, |
165 | | /// Direct `userinfo` content. |
166 | | Direct(&'a str), |
167 | | /// User name and password. |
168 | | UserPass(&'a str, Option<&'a str>), |
169 | | } |
170 | | |
171 | | /// URI/IRI authority builder. |
172 | | #[derive(Default, Debug, Clone)] |
173 | | struct AuthorityBuilder<'a> { |
174 | | /// Host. |
175 | | host: HostRepr<'a>, |
176 | | /// Port. |
177 | | port: PortBuilder<'a>, |
178 | | /// Userinfo. |
179 | | userinfo: UserinfoBuilder<'a>, |
180 | | } |
181 | | |
182 | | impl AuthorityBuilder<'_> { |
183 | | /// Writes the authority to the given formatter. |
184 | 0 | fn fmt_write_to<S: Spec>(&self, f: &mut fmt::Formatter<'_>, normalize: bool) -> fmt::Result { |
185 | 0 | match &self.userinfo.0 { |
186 | 0 | UserinfoRepr::None => {} |
187 | 0 | UserinfoRepr::Direct(userinfo) => { |
188 | 0 | if normalize { |
189 | 0 | PctCaseNormalized::<S>::new(userinfo).fmt(f)?; |
190 | | } else { |
191 | 0 | userinfo.fmt(f)?; |
192 | | } |
193 | 0 | f.write_char('@')?; |
194 | | } |
195 | 0 | UserinfoRepr::UserPass(user, password) => { |
196 | 0 | if normalize { |
197 | 0 | PctCaseNormalized::<S>::new(user).fmt(f)?; |
198 | | } else { |
199 | 0 | f.write_str(user)?; |
200 | | } |
201 | 0 | if let Some(password) = password { |
202 | 0 | f.write_char(':')?; |
203 | 0 | if normalize { |
204 | 0 | PctCaseNormalized::<S>::new(password).fmt(f)?; |
205 | | } else { |
206 | 0 | password.fmt(f)?; |
207 | | } |
208 | 0 | } |
209 | 0 | f.write_char('@')?; |
210 | | } |
211 | | } |
212 | | |
213 | 0 | match self.host { |
214 | 0 | HostRepr::String(host) => { |
215 | 0 | if normalize { |
216 | 0 | normalize::normalize_host_port::<S>(f, host)?; |
217 | | } else { |
218 | 0 | f.write_str(host)?; |
219 | | } |
220 | | } |
221 | | #[cfg(feature = "std")] |
222 | 0 | HostRepr::IpAddr(ipaddr) => match ipaddr { |
223 | 0 | std::net::IpAddr::V4(v) => v.fmt(f)?, |
224 | 0 | std::net::IpAddr::V6(v) => write!(f, "[{v}]")?, |
225 | | }, |
226 | | } |
227 | | |
228 | 0 | match self.port.0 { |
229 | 0 | PortBuilderRepr::Empty => {} |
230 | 0 | PortBuilderRepr::Integer(v) => write!(f, ":{v}")?, |
231 | 0 | PortBuilderRepr::String(v) => { |
232 | | // Omit empty port if the normalization is enabled. |
233 | 0 | if !(v.is_empty() && normalize) { |
234 | 0 | write!(f, ":{v}")?; |
235 | 0 | } |
236 | | } |
237 | | } |
238 | | |
239 | 0 | Ok(()) |
240 | 0 | } |
241 | | } |
242 | | |
243 | | /// Host representation. |
244 | | #[derive(Debug, Clone, Copy)] |
245 | | enum HostRepr<'a> { |
246 | | /// Direct string representation. |
247 | | String(&'a str), |
248 | | #[cfg(feature = "std")] |
249 | | /// Dedicated IP address type. |
250 | | IpAddr(std::net::IpAddr), |
251 | | } |
252 | | |
253 | | impl Default for HostRepr<'_> { |
254 | | #[inline] |
255 | 0 | fn default() -> Self { |
256 | 0 | Self::String("") |
257 | 0 | } |
258 | | } |
259 | | |
260 | | /// URI/IRI reference builder. |
261 | | /// |
262 | | /// # Usage |
263 | | /// |
264 | | /// 1. Create builder by [`Builder::new()`][`Self::new`]. |
265 | | /// 2. Set (or unset) components and set normalization mode as you wish. |
266 | | /// 3. Validate by [`Builder::build()`][`Self::build`] and get [`Built`] value. |
267 | | /// 4. Use [`core::fmt::Display`] trait to serialize the resulting [`Built`], |
268 | | /// or use [`From`]/[`Into`] traits to convert into an allocated string types. |
269 | | /// |
270 | | /// ``` |
271 | | /// # use iri_string::validate::Error; |
272 | | /// use iri_string::build::Builder; |
273 | | /// # #[cfg(not(feature = "alloc"))] |
274 | | /// # use iri_string::types::IriStr; |
275 | | /// # #[cfg(feature = "alloc")] |
276 | | /// use iri_string::types::{IriStr, IriString}; |
277 | | /// |
278 | | /// // 1. Create builder. |
279 | | /// let mut builder = Builder::new(); |
280 | | /// |
281 | | /// // 2. Set (or unset) component and normalization mode. |
282 | | /// builder.scheme("http"); |
283 | | /// builder.host("example.com"); |
284 | | /// builder.path("/foo/../"); |
285 | | /// builder.normalize(); |
286 | | /// |
287 | | /// // 3. Validate and create the result. |
288 | | /// let built = builder.build::<IriStr>()?; |
289 | | /// |
290 | | /// # #[cfg(feature = "alloc")] { |
291 | | /// // 4a. Serialize by `Display` trait (or `ToString`). |
292 | | /// let s = built.to_string(); |
293 | | /// assert_eq!(s, "http://example.com/"); |
294 | | /// # } |
295 | | /// |
296 | | /// # #[cfg(feature = "alloc")] { |
297 | | /// // 4b. Convert into an allocated string types. |
298 | | /// // Thanks to pre-validation by `.build::<IriStr>()`, this conversion is infallible! |
299 | | /// let s: IriString = built.into(); |
300 | | /// assert_eq!(s, "http://example.com/"); |
301 | | /// # } |
302 | | /// |
303 | | /// # Ok::<_, Error>(()) |
304 | | /// ``` |
305 | | #[derive(Default, Debug, Clone)] |
306 | | pub struct Builder<'a> { |
307 | | /// Scheme. |
308 | | scheme: Option<&'a str>, |
309 | | /// Authority. |
310 | | authority: Option<AuthorityBuilder<'a>>, |
311 | | /// Path. |
312 | | path: &'a str, |
313 | | /// Query (without the leading `?`). |
314 | | query: Option<&'a str>, |
315 | | /// Fragment (without the leading `#`). |
316 | | fragment: Option<&'a str>, |
317 | | /// Normalization mode. |
318 | | normalize: bool, |
319 | | } |
320 | | |
321 | | impl<'a> Builder<'a> { |
322 | | /// Creates a builder with empty data. |
323 | | /// |
324 | | /// # Examples |
325 | | /// |
326 | | /// ``` |
327 | | /// # use iri_string::validate::Error; |
328 | | /// use iri_string::build::Builder; |
329 | | /// use iri_string::types::IriReferenceStr; |
330 | | /// |
331 | | /// let builder = Builder::new(); |
332 | | /// |
333 | | /// let iri = builder.build::<IriReferenceStr>()?; |
334 | | /// # #[cfg(feature = "alloc")] { |
335 | | /// assert_eq!(iri.to_string(), ""); |
336 | | /// # } |
337 | | /// # Ok::<_, Error>(()) |
338 | | /// ``` |
339 | | #[inline] |
340 | | #[must_use] |
341 | 0 | pub fn new() -> Self { |
342 | 0 | Self::default() |
343 | 0 | } |
344 | | |
345 | | /// Writes the authority to the given formatter. |
346 | | /// |
347 | | /// Don't expose this as public, since this method does not validate. |
348 | | /// |
349 | | /// # Preconditions |
350 | | /// |
351 | | /// The IRI string to be built should be a valid IRI reference. |
352 | | /// Callers are responsible to validate the component values before calling |
353 | | /// this method. |
354 | 0 | fn fmt_write_to<S: Spec>( |
355 | 0 | &self, |
356 | 0 | f: &mut fmt::Formatter<'_>, |
357 | 0 | path_is_absolute: bool, |
358 | 0 | ) -> fmt::Result { |
359 | 0 | if let Some(scheme) = self.scheme { |
360 | | // Write the scheme. |
361 | 0 | if self.normalize { |
362 | 0 | normalize::normalize_scheme(f, scheme)?; |
363 | | } else { |
364 | 0 | f.write_str(scheme)?; |
365 | | } |
366 | 0 | f.write_char(':')?; |
367 | 0 | } |
368 | | |
369 | 0 | if let Some(authority) = &self.authority { |
370 | 0 | f.write_str("//")?; |
371 | 0 | authority.fmt_write_to::<S>(f, self.normalize)?; |
372 | 0 | } |
373 | | |
374 | 0 | if !self.normalize { |
375 | | // No normalization. |
376 | 0 | f.write_str(self.path)?; |
377 | 0 | } else if self.scheme.is_some() || self.authority.is_some() || path_is_absolute { |
378 | | // Apply full syntax-based normalization. |
379 | 0 | let op = normalize::NormalizationOp { |
380 | 0 | mode: NormalizationMode::Default, |
381 | 0 | }; |
382 | 0 | normalize::PathToNormalize::from_single_path(self.path).fmt_write_normalize::<S, _>( |
383 | 0 | f, |
384 | 0 | op, |
385 | 0 | self.authority.is_some(), |
386 | 0 | )?; |
387 | | } else { |
388 | | // The IRI reference starts with `path` component, and the path is relative. |
389 | | // Skip path segment normalization. |
390 | 0 | PctCaseNormalized::<S>::new(self.path).fmt(f)?; |
391 | | } |
392 | | |
393 | 0 | if let Some(query) = self.query { |
394 | 0 | f.write_char('?')?; |
395 | 0 | if self.normalize { |
396 | 0 | normalize::normalize_query::<S>(f, query)?; |
397 | | } else { |
398 | 0 | f.write_str(query)?; |
399 | | } |
400 | 0 | } |
401 | | |
402 | 0 | if let Some(fragment) = self.fragment { |
403 | 0 | f.write_char('#')?; |
404 | 0 | if self.normalize { |
405 | 0 | normalize::normalize_fragment::<S>(f, fragment)?; |
406 | | } else { |
407 | 0 | f.write_str(fragment)?; |
408 | | } |
409 | 0 | } |
410 | | |
411 | 0 | Ok(()) |
412 | 0 | } |
413 | | |
414 | | /// Builds the proxy object that can be converted to the desired IRI string type. |
415 | | /// |
416 | | /// # Examples |
417 | | /// |
418 | | /// ``` |
419 | | /// # use iri_string::validate::Error; |
420 | | /// use iri_string::build::Builder; |
421 | | /// use iri_string::types::IriStr; |
422 | | /// # #[cfg(feature = "alloc")] |
423 | | /// use iri_string::types::IriString; |
424 | | /// |
425 | | /// let mut builder = Builder::new(); |
426 | | /// |
427 | | /// builder.scheme("http"); |
428 | | /// builder.host("example.com"); |
429 | | /// builder.path("/foo/bar"); |
430 | | /// |
431 | | /// let built = builder.build::<IriStr>()?; |
432 | | /// |
433 | | /// # #[cfg(feature = "alloc")] { |
434 | | /// // The returned value implements `core::fmt::Display` and |
435 | | /// // `core::string::ToString`. |
436 | | /// assert_eq!(built.to_string(), "http://example.com/foo/bar"); |
437 | | /// |
438 | | /// // The returned value implements `Into<{iri_owned_string_type}>`. |
439 | | /// let iri = IriString::from(built); |
440 | | /// // `let iri: IriString = built.into();` is also OK. |
441 | | /// # } |
442 | | /// # Ok::<_, Error>(()) |
443 | | /// ``` |
444 | | #[inline] |
445 | 0 | pub fn build<T>(self) -> Result<Built<'a, T>, Error> |
446 | 0 | where |
447 | 0 | T: ?Sized + Buildable<'a>, |
448 | | { |
449 | 0 | <T as private::Sealed<'a>>::validate_builder(self) |
450 | 0 | } |
451 | | } |
452 | | |
453 | | // Setters does not return `&mut Self` or `Self` since it introduces needless |
454 | | // ambiguity for users. |
455 | | // For example, if setters return something and allows method chaining, can you |
456 | | // correctly explain what happens with the code below without reading document? |
457 | | // |
458 | | // ```text |
459 | | // let mut builder = Builder::new().foo("foo").bar("bar"); |
460 | | // let baz = builder.baz("baz").clone().build(); |
461 | | // // Should the result be foo+bar+qux, or foo+bar+baz+qux? |
462 | | // let qux = builder.qux("qux").build(); |
463 | | // ``` |
464 | | impl<'a> Builder<'a> { |
465 | | /// Sets the scheme. |
466 | | /// |
467 | | /// # Examples |
468 | | /// |
469 | | /// ``` |
470 | | /// # use iri_string::validate::Error; |
471 | | /// use iri_string::build::Builder; |
472 | | /// use iri_string::types::IriReferenceStr; |
473 | | /// |
474 | | /// let mut builder = Builder::new(); |
475 | | /// builder.scheme("foo"); |
476 | | /// |
477 | | /// let iri = builder.build::<IriReferenceStr>()?; |
478 | | /// # #[cfg(feature = "alloc")] { |
479 | | /// assert_eq!(iri.to_string(), "foo:"); |
480 | | /// # } |
481 | | /// # Ok::<_, Error>(()) |
482 | | /// ``` |
483 | | #[inline] |
484 | 0 | pub fn scheme(&mut self, v: &'a str) { |
485 | 0 | self.scheme = Some(v); |
486 | 0 | } |
487 | | |
488 | | /// Unsets the scheme. |
489 | | /// |
490 | | /// # Examples |
491 | | /// |
492 | | /// ``` |
493 | | /// # use iri_string::validate::Error; |
494 | | /// use iri_string::build::Builder; |
495 | | /// use iri_string::types::IriReferenceStr; |
496 | | /// |
497 | | /// let mut builder = Builder::new(); |
498 | | /// builder.scheme("foo"); |
499 | | /// builder.unset_scheme(); |
500 | | /// |
501 | | /// let iri = builder.build::<IriReferenceStr>()?; |
502 | | /// # #[cfg(feature = "alloc")] { |
503 | | /// assert_eq!(iri.to_string(), ""); |
504 | | /// # } |
505 | | /// # Ok::<_, Error>(()) |
506 | | /// ``` |
507 | | #[inline] |
508 | 0 | pub fn unset_scheme(&mut self) { |
509 | 0 | self.scheme = None; |
510 | 0 | } |
511 | | |
512 | | /// Sets the path. |
513 | | /// |
514 | | /// Note that no methods are provided to "unset" path since every IRI |
515 | | /// references has a path component (although it can be empty). |
516 | | /// If you want to "unset" the path, just set the empty string. |
517 | | /// |
518 | | /// # Examples |
519 | | /// |
520 | | /// ``` |
521 | | /// # use iri_string::validate::Error; |
522 | | /// use iri_string::build::Builder; |
523 | | /// use iri_string::types::IriReferenceStr; |
524 | | /// |
525 | | /// let mut builder = Builder::new(); |
526 | | /// builder.path("foo/bar"); |
527 | | /// |
528 | | /// let iri = builder.build::<IriReferenceStr>()?; |
529 | | /// # #[cfg(feature = "alloc")] { |
530 | | /// assert_eq!(iri.to_string(), "foo/bar"); |
531 | | /// # } |
532 | | /// # Ok::<_, Error>(()) |
533 | | /// ``` |
534 | | #[inline] |
535 | 0 | pub fn path(&mut self, v: &'a str) { |
536 | 0 | self.path = v; |
537 | 0 | } |
538 | | |
539 | | /// Initializes the authority builder. |
540 | | #[inline] |
541 | 0 | fn authority_builder(&mut self) -> &mut AuthorityBuilder<'a> { |
542 | 0 | self.authority.get_or_insert_with(AuthorityBuilder::default) |
543 | 0 | } |
544 | | |
545 | | /// Unsets the authority. |
546 | | /// |
547 | | /// # Examples |
548 | | /// |
549 | | /// ``` |
550 | | /// # use iri_string::validate::Error; |
551 | | /// use iri_string::build::Builder; |
552 | | /// use iri_string::types::IriReferenceStr; |
553 | | /// |
554 | | /// let mut builder = Builder::new(); |
555 | | /// builder.host("example.com"); |
556 | | /// builder.unset_authority(); |
557 | | /// |
558 | | /// let iri = builder.build::<IriReferenceStr>()?; |
559 | | /// # #[cfg(feature = "alloc")] { |
560 | | /// assert_eq!(iri.to_string(), ""); |
561 | | /// # } |
562 | | /// # Ok::<_, Error>(()) |
563 | | /// ``` |
564 | | #[inline] |
565 | 0 | pub fn unset_authority(&mut self) { |
566 | 0 | self.authority = None; |
567 | 0 | } |
568 | | |
569 | | /// Sets the userinfo. |
570 | | /// |
571 | | /// `userinfo` component always have `user` part (but it can be empty). |
572 | | /// |
573 | | /// Note that `("", None)` is considered as an empty userinfo, rather than |
574 | | /// unset userinfo. |
575 | | /// Also note that the user part cannot have colon characters. |
576 | | /// |
577 | | /// # Examples |
578 | | /// |
579 | | /// ``` |
580 | | /// # use iri_string::validate::Error; |
581 | | /// use iri_string::build::Builder; |
582 | | /// use iri_string::types::IriReferenceStr; |
583 | | /// |
584 | | /// let mut builder = Builder::new(); |
585 | | /// builder.userinfo("user:pass"); |
586 | | /// |
587 | | /// let iri = builder.build::<IriReferenceStr>()?; |
588 | | /// # #[cfg(feature = "alloc")] { |
589 | | /// assert_eq!(iri.to_string(), "//user:pass@"); |
590 | | /// # } |
591 | | /// # Ok::<_, Error>(()) |
592 | | /// ``` |
593 | | /// |
594 | | /// You can specify `(user, password)` pair. |
595 | | /// |
596 | | /// ``` |
597 | | /// # use iri_string::validate::Error; |
598 | | /// use iri_string::build::Builder; |
599 | | /// use iri_string::types::IriReferenceStr; |
600 | | /// |
601 | | /// let mut builder = Builder::new(); |
602 | | /// |
603 | | /// builder.userinfo(("user", Some("pass"))); |
604 | | /// # #[cfg(feature = "alloc")] { |
605 | | /// assert_eq!( |
606 | | /// builder.clone().build::<IriReferenceStr>()?.to_string(), |
607 | | /// "//user:pass@" |
608 | | /// ); |
609 | | /// # } |
610 | | /// # Ok::<_, Error>(()) |
611 | | /// ``` |
612 | | /// |
613 | | /// `("", None)` is considered as an empty userinfo. |
614 | | /// |
615 | | /// ``` |
616 | | /// # use iri_string::validate::Error; |
617 | | /// use iri_string::build::Builder; |
618 | | /// use iri_string::types::IriReferenceStr; |
619 | | /// |
620 | | /// let mut builder = Builder::new(); |
621 | | /// builder.userinfo(("", None)); |
622 | | /// |
623 | | /// let iri = builder.build::<IriReferenceStr>()?; |
624 | | /// # #[cfg(feature = "alloc")] { |
625 | | /// assert_eq!(iri.to_string(), "//@"); |
626 | | /// # } |
627 | | /// # Ok::<_, Error>(()) |
628 | | /// ``` |
629 | | #[inline] |
630 | 0 | pub fn userinfo<T: Into<UserinfoBuilder<'a>>>(&mut self, v: T) { |
631 | 0 | self.authority_builder().userinfo = v.into(); |
632 | 0 | } |
633 | | |
634 | | /// Unsets the port. |
635 | | /// |
636 | | /// # Examples |
637 | | /// |
638 | | /// ``` |
639 | | /// # use iri_string::validate::Error; |
640 | | /// use iri_string::build::Builder; |
641 | | /// use iri_string::types::IriReferenceStr; |
642 | | /// |
643 | | /// let mut builder = Builder::new(); |
644 | | /// builder.userinfo("user:pass"); |
645 | | /// // Note that this does not unset the entire authority. |
646 | | /// // Now empty authority is set. |
647 | | /// builder.unset_userinfo(); |
648 | | /// |
649 | | /// let iri = builder.build::<IriReferenceStr>()?; |
650 | | /// # #[cfg(feature = "alloc")] { |
651 | | /// assert_eq!(iri.to_string(), "//"); |
652 | | /// # } |
653 | | /// # Ok::<_, Error>(()) |
654 | | /// ``` |
655 | | #[inline] |
656 | 0 | pub fn unset_userinfo(&mut self) { |
657 | 0 | self.authority_builder().userinfo = UserinfoBuilder::default(); |
658 | 0 | } |
659 | | |
660 | | /// Sets the reg-name or IP address (i.e. host) without port. |
661 | | /// |
662 | | /// Note that no methods are provided to "unset" host. |
663 | | /// Depending on your situation, set empty string as a reg-name, or unset |
664 | | /// the authority entirely by [`unset_authority`][`Self::unset_authority`] |
665 | | /// method. |
666 | | /// |
667 | | /// # Examples |
668 | | /// |
669 | | /// ``` |
670 | | /// # use iri_string::validate::Error; |
671 | | /// use iri_string::build::Builder; |
672 | | /// use iri_string::types::IriReferenceStr; |
673 | | /// |
674 | | /// let mut builder = Builder::new(); |
675 | | /// builder.host("example.com"); |
676 | | /// |
677 | | /// let iri = builder.build::<IriReferenceStr>()?; |
678 | | /// # #[cfg(feature = "alloc")] { |
679 | | /// assert_eq!(iri.to_string(), "//example.com"); |
680 | | /// # } |
681 | | /// # Ok::<_, Error>(()) |
682 | | /// ``` |
683 | | #[inline] |
684 | 0 | pub fn host(&mut self, v: &'a str) { |
685 | 0 | self.authority_builder().host = HostRepr::String(v); |
686 | 0 | } |
687 | | |
688 | | /// Sets the IP address as a host. |
689 | | /// |
690 | | /// Note that no methods are provided to "unset" host. |
691 | | /// Depending on your situation, set empty string as a reg-name, or unset |
692 | | /// the authority entirely by [`unset_authority`][`Self::unset_authority`] |
693 | | /// method. |
694 | | /// |
695 | | /// # Examples |
696 | | /// |
697 | | /// ``` |
698 | | /// # use iri_string::validate::Error; |
699 | | /// # #[cfg(feature = "std")] { |
700 | | /// use iri_string::build::Builder; |
701 | | /// use iri_string::types::IriReferenceStr; |
702 | | /// |
703 | | /// let mut builder = Builder::new(); |
704 | | /// builder.ip_address(std::net::Ipv4Addr::new(192, 0, 2, 0)); |
705 | | /// |
706 | | /// let iri = builder.build::<IriReferenceStr>()?; |
707 | | /// # #[cfg(feature = "alloc")] { |
708 | | /// assert_eq!(iri.to_string(), "//192.0.2.0"); |
709 | | /// # } |
710 | | /// # } |
711 | | /// # Ok::<_, Error>(()) |
712 | | /// ``` |
713 | | #[cfg(feature = "std")] |
714 | | #[inline] |
715 | 0 | pub fn ip_address<T: Into<std::net::IpAddr>>(&mut self, addr: T) { |
716 | 0 | self.authority_builder().host = HostRepr::IpAddr(addr.into()); |
717 | 0 | } |
718 | | |
719 | | /// Sets the port. |
720 | | /// |
721 | | /// # Examples |
722 | | /// |
723 | | /// ``` |
724 | | /// # use iri_string::validate::Error; |
725 | | /// use iri_string::build::Builder; |
726 | | /// use iri_string::types::IriReferenceStr; |
727 | | /// |
728 | | /// let mut builder = Builder::new(); |
729 | | /// builder.port(80_u16); |
730 | | /// // Accepts other types that implements `Into<PortBuilder<'a>>`. |
731 | | /// //builder.port(80_u8); |
732 | | /// //builder.port("80"); |
733 | | /// |
734 | | /// let iri = builder.build::<IriReferenceStr>()?; |
735 | | /// # #[cfg(feature = "alloc")] { |
736 | | /// assert_eq!(iri.to_string(), "//:80"); |
737 | | /// # } |
738 | | /// # Ok::<_, Error>(()) |
739 | | /// ``` |
740 | | #[inline] |
741 | 0 | pub fn port<T: Into<PortBuilder<'a>>>(&mut self, v: T) { |
742 | 0 | self.authority_builder().port = v.into(); |
743 | 0 | } |
744 | | |
745 | | /// Unsets the port. |
746 | | /// |
747 | | /// # Examples |
748 | | /// |
749 | | /// ``` |
750 | | /// # use iri_string::validate::Error; |
751 | | /// use iri_string::build::Builder; |
752 | | /// use iri_string::types::IriReferenceStr; |
753 | | /// |
754 | | /// let mut builder = Builder::new(); |
755 | | /// builder.port(80_u16); |
756 | | /// // Note that this does not unset the entire authority. |
757 | | /// // Now empty authority is set. |
758 | | /// builder.unset_port(); |
759 | | /// |
760 | | /// let iri = builder.build::<IriReferenceStr>()?; |
761 | | /// # #[cfg(feature = "alloc")] { |
762 | | /// assert_eq!(iri.to_string(), "//"); |
763 | | /// # } |
764 | | /// # Ok::<_, Error>(()) |
765 | | /// ``` |
766 | | #[inline] |
767 | 0 | pub fn unset_port(&mut self) { |
768 | 0 | self.authority_builder().port = PortBuilder::default(); |
769 | 0 | } |
770 | | |
771 | | /// Sets the query. |
772 | | /// |
773 | | /// The string after `?` should be specified. |
774 | | /// |
775 | | /// # Examples |
776 | | /// |
777 | | /// ``` |
778 | | /// # use iri_string::validate::Error; |
779 | | /// use iri_string::build::Builder; |
780 | | /// use iri_string::types::IriReferenceStr; |
781 | | /// |
782 | | /// let mut builder = Builder::new(); |
783 | | /// builder.query("q=example"); |
784 | | /// |
785 | | /// let iri = builder.build::<IriReferenceStr>()?; |
786 | | /// # #[cfg(feature = "alloc")] { |
787 | | /// assert_eq!(iri.to_string(), "?q=example"); |
788 | | /// # } |
789 | | /// # Ok::<_, Error>(()) |
790 | | /// ``` |
791 | | #[inline] |
792 | 0 | pub fn query(&mut self, v: &'a str) { |
793 | 0 | self.query = Some(v); |
794 | 0 | } |
795 | | |
796 | | /// Unsets the query. |
797 | | /// |
798 | | /// # Examples |
799 | | /// |
800 | | /// ``` |
801 | | /// # use iri_string::validate::Error; |
802 | | /// use iri_string::build::Builder; |
803 | | /// use iri_string::types::IriReferenceStr; |
804 | | /// |
805 | | /// let mut builder = Builder::new(); |
806 | | /// builder.query("q=example"); |
807 | | /// builder.unset_query(); |
808 | | /// |
809 | | /// let iri = builder.build::<IriReferenceStr>()?; |
810 | | /// # #[cfg(feature = "alloc")] { |
811 | | /// assert_eq!(iri.to_string(), ""); |
812 | | /// # } |
813 | | /// # Ok::<_, Error>(()) |
814 | | /// ``` |
815 | | #[inline] |
816 | 0 | pub fn unset_query(&mut self) { |
817 | 0 | self.query = None; |
818 | 0 | } |
819 | | |
820 | | /// Sets the fragment. |
821 | | /// |
822 | | /// The string after `#` should be specified. |
823 | | /// |
824 | | /// # Examples |
825 | | /// |
826 | | /// ``` |
827 | | /// # use iri_string::validate::Error; |
828 | | /// use iri_string::build::Builder; |
829 | | /// use iri_string::types::IriReferenceStr; |
830 | | /// |
831 | | /// let mut builder = Builder::new(); |
832 | | /// builder.fragment("anchor"); |
833 | | /// |
834 | | /// let iri = builder.build::<IriReferenceStr>()?; |
835 | | /// # #[cfg(feature = "alloc")] { |
836 | | /// assert_eq!(iri.to_string(), "#anchor"); |
837 | | /// # } |
838 | | /// # Ok::<_, Error>(()) |
839 | | /// ``` |
840 | | #[inline] |
841 | 0 | pub fn fragment(&mut self, v: &'a str) { |
842 | 0 | self.fragment = Some(v); |
843 | 0 | } |
844 | | |
845 | | /// Unsets the fragment. |
846 | | /// |
847 | | /// # Examples |
848 | | /// |
849 | | /// ``` |
850 | | /// # use iri_string::validate::Error; |
851 | | /// use iri_string::build::Builder; |
852 | | /// use iri_string::types::IriReferenceStr; |
853 | | /// |
854 | | /// let mut builder = Builder::new(); |
855 | | /// builder.fragment("anchor"); |
856 | | /// builder.unset_fragment(); |
857 | | /// |
858 | | /// let iri = builder.build::<IriReferenceStr>()?; |
859 | | /// # #[cfg(feature = "alloc")] { |
860 | | /// assert_eq!(iri.to_string(), ""); |
861 | | /// # } |
862 | | /// # Ok::<_, Error>(()) |
863 | | /// ``` |
864 | | #[inline] |
865 | 0 | pub fn unset_fragment(&mut self) { |
866 | 0 | self.fragment = None; |
867 | 0 | } |
868 | | |
869 | | /// Stop normalizing the result. |
870 | | /// |
871 | | /// # Examples |
872 | | /// |
873 | | /// ``` |
874 | | /// # use iri_string::validate::Error; |
875 | | /// use iri_string::build::Builder; |
876 | | /// use iri_string::types::IriReferenceStr; |
877 | | /// |
878 | | /// let mut builder = Builder::new(); |
879 | | /// builder.scheme("http"); |
880 | | /// // `%75%73%65%72` is "user". |
881 | | /// builder.userinfo("%75%73%65%72"); |
882 | | /// builder.host("EXAMPLE.COM"); |
883 | | /// builder.port(""); |
884 | | /// builder.path("/foo/../%2e%2e/bar/%2e/baz/."); |
885 | | /// |
886 | | /// builder.unset_normalize(); |
887 | | /// |
888 | | /// let iri = builder.build::<IriReferenceStr>()?; |
889 | | /// # #[cfg(feature = "alloc")] { |
890 | | /// assert_eq!( |
891 | | /// iri.to_string(), |
892 | | /// "http://%75%73%65%72@EXAMPLE.COM:/foo/../%2e%2e/bar/%2e/baz/." |
893 | | /// ); |
894 | | /// # } |
895 | | /// # Ok::<_, Error>(()) |
896 | | /// ``` |
897 | | #[inline] |
898 | 0 | pub fn unset_normalize(&mut self) { |
899 | 0 | self.normalize = false; |
900 | 0 | } |
901 | | |
902 | | /// Normalizes the result using RFC 3986 syntax-based normalization and |
903 | | /// WHATWG URL Standard algorithm. |
904 | | /// |
905 | | /// # Normalization |
906 | | /// |
907 | | /// If `scheme` or `authority` component is present or the path is absolute, |
908 | | /// the build result will fully normalized using full syntax-based normalization: |
909 | | /// |
910 | | /// * case normalization ([RFC 3986 6.2.2.1]), |
911 | | /// * percent-encoding normalization ([RFC 3986 6.2.2.2]), and |
912 | | /// * path segment normalization ([RFC 3986 6.2.2.2]). |
913 | | /// |
914 | | /// However, if both `scheme` and `authority` is absent and the path is relative |
915 | | /// (including empty), i.e. the IRI reference to be built starts with the |
916 | | /// relative `path` component, path segment normalization will be omitted. |
917 | | /// This is because the path segment normalization depends on presence or |
918 | | /// absense of the `authority` components, and will remove extra `..` |
919 | | /// segments which should not be ignored. |
920 | | /// |
921 | | /// Note that `path` must already be empty or start with a slash **before |
922 | | /// the normalizaiton** if `authority` is present. |
923 | | /// |
924 | | /// # WHATWG URL Standard |
925 | | /// |
926 | | /// If you need to avoid WHATWG URL Standard serialization, use |
927 | | /// [`Built::ensure_rfc3986_normalizable`] method to test if the result is |
928 | | /// normalizable without WHATWG spec. |
929 | | /// |
930 | | /// # Examples |
931 | | /// |
932 | | /// ``` |
933 | | /// # use iri_string::validate::Error; |
934 | | /// use iri_string::build::Builder; |
935 | | /// use iri_string::types::IriReferenceStr; |
936 | | /// |
937 | | /// let mut builder = Builder::new(); |
938 | | /// builder.scheme("http"); |
939 | | /// // `%75%73%65%72` is "user". |
940 | | /// builder.userinfo("%75%73%65%72"); |
941 | | /// builder.host("EXAMPLE.COM"); |
942 | | /// builder.port(""); |
943 | | /// builder.path("/foo/../%2e%2e/bar/%2e/baz/."); |
944 | | /// |
945 | | /// builder.normalize(); |
946 | | /// |
947 | | /// let iri = builder.build::<IriReferenceStr>()?; |
948 | | /// # #[cfg(feature = "alloc")] { |
949 | | /// assert_eq!(iri.to_string(), "http://user@example.com/bar/baz/"); |
950 | | /// # } |
951 | | /// # Ok::<_, Error>(()) |
952 | | /// ``` |
953 | | #[inline] |
954 | 0 | pub fn normalize(&mut self) { |
955 | 0 | self.normalize = true; |
956 | 0 | } |
957 | | } |
958 | | |
959 | | /// [`Display`]-able IRI build result. |
960 | | /// |
961 | | /// The value of this type can generate an IRI using [`From`]/[`Into`] traits or |
962 | | /// [`Display`] trait. |
963 | | /// |
964 | | /// # Security consideration |
965 | | /// |
966 | | /// This can be stringified or directly printed by `std::fmt::Display`, but note |
967 | | /// that this `Display` **does not hide the password part**. Be careful **not to |
968 | | /// print the value using `Display for Built<_>` in public context**. |
969 | | /// |
970 | | /// [`From`]: `core::convert::From` |
971 | | /// [`Into`]: `core::convert::Into` |
972 | | /// [`Display`]: `core::fmt::Display` |
973 | | #[derive(Debug)] |
974 | | pub struct Built<'a, T: ?Sized> { |
975 | | /// Builder with the validated content. |
976 | | builder: Builder<'a>, |
977 | | /// Whether the path is absolute. |
978 | | path_is_absolute: bool, |
979 | | /// String type. |
980 | | _ty_str: PhantomData<fn() -> T>, |
981 | | } |
982 | | |
983 | | impl<T: ?Sized> Clone for Built<'_, T> { |
984 | | #[inline] |
985 | 0 | fn clone(&self) -> Self { |
986 | 0 | Self { |
987 | 0 | builder: self.builder.clone(), |
988 | 0 | path_is_absolute: self.path_is_absolute, |
989 | 0 | _ty_str: PhantomData, |
990 | 0 | } |
991 | 0 | } |
992 | | } |
993 | | |
994 | | /// Implements conversions to a string. |
995 | | macro_rules! impl_stringifiers { |
996 | | ($borrowed:ident, $owned:ident) => { |
997 | | impl<S: Spec> Built<'_, $borrowed<S>> { |
998 | | /// Returns Ok`(())` if the IRI is normalizable by the RFC 3986 algorithm. |
999 | | #[inline] |
1000 | 0 | pub fn ensure_rfc3986_normalizable(&self) -> Result<(), normalize::Error> { |
1001 | 0 | if self.builder.authority.is_none() { |
1002 | 0 | let path = normalize::PathToNormalize::from_single_path(self.builder.path); |
1003 | 0 | path.ensure_rfc3986_normalizable_with_authority_absent()?; |
1004 | 0 | } |
1005 | 0 | Ok(()) |
1006 | 0 | } Unexecuted instantiation: <iri_string::build::Built<iri_string::types::generic::reference::RiReferenceStr<_>>>::ensure_rfc3986_normalizable Unexecuted instantiation: <iri_string::build::Built<iri_string::types::generic::normal::RiStr<_>>>::ensure_rfc3986_normalizable Unexecuted instantiation: <iri_string::build::Built<iri_string::types::generic::absolute::RiAbsoluteStr<_>>>::ensure_rfc3986_normalizable Unexecuted instantiation: <iri_string::build::Built<iri_string::types::generic::relative::RiRelativeStr<_>>>::ensure_rfc3986_normalizable |
1007 | | } |
1008 | | |
1009 | | impl<S: Spec> fmt::Display for Built<'_, $borrowed<S>> { |
1010 | | #[inline] |
1011 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
1012 | 0 | self.builder.fmt_write_to::<S>(f, self.path_is_absolute) |
1013 | 0 | } Unexecuted instantiation: <iri_string::build::Built<iri_string::types::generic::reference::RiReferenceStr<_>> as core::fmt::Display>::fmt Unexecuted instantiation: <iri_string::build::Built<iri_string::types::generic::normal::RiStr<_>> as core::fmt::Display>::fmt Unexecuted instantiation: <iri_string::build::Built<iri_string::types::generic::absolute::RiAbsoluteStr<_>> as core::fmt::Display>::fmt Unexecuted instantiation: <iri_string::build::Built<iri_string::types::generic::relative::RiRelativeStr<_>> as core::fmt::Display>::fmt |
1014 | | } |
1015 | | |
1016 | | #[cfg(feature = "alloc")] |
1017 | | impl<S: Spec> ToDedicatedString for Built<'_, $borrowed<S>> { |
1018 | | type Target = $owned<S>; |
1019 | | |
1020 | | #[inline] |
1021 | 0 | fn try_to_dedicated_string(&self) -> Result<Self::Target, TryReserveError> { |
1022 | 0 | let s = self.try_to_string()?; |
1023 | 0 | Ok(TryFrom::try_from(s) |
1024 | 0 | .expect("[validity] the IRI to be built is already validated")) |
1025 | 0 | } Unexecuted instantiation: <iri_string::build::Built<iri_string::types::generic::reference::RiReferenceStr<_>> as iri_string::format::ToDedicatedString>::try_to_dedicated_string Unexecuted instantiation: <iri_string::build::Built<iri_string::types::generic::normal::RiStr<_>> as iri_string::format::ToDedicatedString>::try_to_dedicated_string Unexecuted instantiation: <iri_string::build::Built<iri_string::types::generic::absolute::RiAbsoluteStr<_>> as iri_string::format::ToDedicatedString>::try_to_dedicated_string Unexecuted instantiation: <iri_string::build::Built<iri_string::types::generic::relative::RiRelativeStr<_>> as iri_string::format::ToDedicatedString>::try_to_dedicated_string |
1026 | | } |
1027 | | |
1028 | | #[cfg(feature = "alloc")] |
1029 | | impl<S: Spec> From<Built<'_, $borrowed<S>>> for $owned<S> { |
1030 | | #[inline] |
1031 | 0 | fn from(builder: Built<'_, $borrowed<S>>) -> Self { |
1032 | 0 | (&builder).into() |
1033 | 0 | } Unexecuted instantiation: <iri_string::types::generic::reference::RiReferenceString<_> as core::convert::From<iri_string::build::Built<iri_string::types::generic::reference::RiReferenceStr<_>>>>::from Unexecuted instantiation: <iri_string::types::generic::normal::RiString<_> as core::convert::From<iri_string::build::Built<iri_string::types::generic::normal::RiStr<_>>>>::from Unexecuted instantiation: <iri_string::types::generic::absolute::RiAbsoluteString<_> as core::convert::From<iri_string::build::Built<iri_string::types::generic::absolute::RiAbsoluteStr<_>>>>::from Unexecuted instantiation: <iri_string::types::generic::relative::RiRelativeString<_> as core::convert::From<iri_string::build::Built<iri_string::types::generic::relative::RiRelativeStr<_>>>>::from |
1034 | | } |
1035 | | |
1036 | | #[cfg(feature = "alloc")] |
1037 | | impl<S: Spec> From<&Built<'_, $borrowed<S>>> for $owned<S> { |
1038 | | #[inline] |
1039 | 0 | fn from(builder: &Built<'_, $borrowed<S>>) -> Self { |
1040 | 0 | let s = builder.to_string(); |
1041 | 0 | Self::try_from(s).expect("[validity] the IRI to be built is already validated") |
1042 | 0 | } Unexecuted instantiation: <iri_string::types::generic::reference::RiReferenceString<_> as core::convert::From<&iri_string::build::Built<iri_string::types::generic::reference::RiReferenceStr<_>>>>::from Unexecuted instantiation: <iri_string::types::generic::normal::RiString<_> as core::convert::From<&iri_string::build::Built<iri_string::types::generic::normal::RiStr<_>>>>::from Unexecuted instantiation: <iri_string::types::generic::absolute::RiAbsoluteString<_> as core::convert::From<&iri_string::build::Built<iri_string::types::generic::absolute::RiAbsoluteStr<_>>>>::from Unexecuted instantiation: <iri_string::types::generic::relative::RiRelativeString<_> as core::convert::From<&iri_string::build::Built<iri_string::types::generic::relative::RiRelativeStr<_>>>>::from |
1043 | | } |
1044 | | }; |
1045 | | } |
1046 | | |
1047 | | impl_stringifiers!(RiReferenceStr, RiReferenceString); |
1048 | | impl_stringifiers!(RiStr, RiString); |
1049 | | impl_stringifiers!(RiAbsoluteStr, RiAbsoluteString); |
1050 | | impl_stringifiers!(RiRelativeStr, RiRelativeString); |
1051 | | |
1052 | | /// A trait for borrowed IRI string types buildable by the [`Builder`]. |
1053 | | pub trait Buildable<'a>: private::Sealed<'a> {} |
1054 | | |
1055 | | impl<'a, S: Spec> private::Sealed<'a> for RiReferenceStr<S> { |
1056 | 0 | fn validate_builder(builder: Builder<'a>) -> Result<Built<'a, Self>, Error> { |
1057 | 0 | let path_is_absolute = validate_builder_for_iri_reference::<S>(&builder)?; |
1058 | | |
1059 | 0 | Ok(Built { |
1060 | 0 | builder, |
1061 | 0 | path_is_absolute, |
1062 | 0 | _ty_str: PhantomData, |
1063 | 0 | }) |
1064 | 0 | } |
1065 | | } |
1066 | | impl<S: Spec> Buildable<'_> for RiReferenceStr<S> {} |
1067 | | |
1068 | | impl<'a, S: Spec> private::Sealed<'a> for RiStr<S> { |
1069 | 0 | fn validate_builder(builder: Builder<'a>) -> Result<Built<'a, Self>, Error> { |
1070 | 0 | if builder.scheme.is_none() { |
1071 | 0 | return Err(Error::new()); |
1072 | 0 | } |
1073 | 0 | let path_is_absolute = validate_builder_for_iri_reference::<S>(&builder)?; |
1074 | | |
1075 | 0 | Ok(Built { |
1076 | 0 | builder, |
1077 | 0 | path_is_absolute, |
1078 | 0 | _ty_str: PhantomData, |
1079 | 0 | }) |
1080 | 0 | } |
1081 | | } |
1082 | | impl<S: Spec> Buildable<'_> for RiStr<S> {} |
1083 | | |
1084 | | impl<'a, S: Spec> private::Sealed<'a> for RiAbsoluteStr<S> { |
1085 | 0 | fn validate_builder(builder: Builder<'a>) -> Result<Built<'a, Self>, Error> { |
1086 | 0 | if builder.scheme.is_none() { |
1087 | 0 | return Err(Error::new()); |
1088 | 0 | } |
1089 | 0 | if builder.fragment.is_some() { |
1090 | 0 | return Err(Error::new()); |
1091 | 0 | } |
1092 | 0 | let path_is_absolute = validate_builder_for_iri_reference::<S>(&builder)?; |
1093 | | |
1094 | 0 | Ok(Built { |
1095 | 0 | builder, |
1096 | 0 | path_is_absolute, |
1097 | 0 | _ty_str: PhantomData, |
1098 | 0 | }) |
1099 | 0 | } |
1100 | | } |
1101 | | impl<S: Spec> Buildable<'_> for RiAbsoluteStr<S> {} |
1102 | | |
1103 | | impl<'a, S: Spec> private::Sealed<'a> for RiRelativeStr<S> { |
1104 | 0 | fn validate_builder(builder: Builder<'a>) -> Result<Built<'a, Self>, Error> { |
1105 | 0 | if builder.scheme.is_some() { |
1106 | 0 | return Err(Error::new()); |
1107 | 0 | } |
1108 | 0 | let path_is_absolute = validate_builder_for_iri_reference::<S>(&builder)?; |
1109 | | |
1110 | 0 | Ok(Built { |
1111 | 0 | builder, |
1112 | 0 | path_is_absolute, |
1113 | 0 | _ty_str: PhantomData, |
1114 | 0 | }) |
1115 | 0 | } |
1116 | | } |
1117 | | impl<S: Spec> Buildable<'_> for RiRelativeStr<S> {} |
1118 | | |
1119 | | /// Checks whether the builder output is valid IRI reference. |
1120 | | /// |
1121 | | /// Returns whether the path is absolute. |
1122 | 0 | fn validate_builder_for_iri_reference<S: Spec>(builder: &Builder<'_>) -> Result<bool, Error> { |
1123 | 0 | if let Some(scheme) = builder.scheme { |
1124 | 0 | parser::validate_scheme(scheme)?; |
1125 | 0 | } |
1126 | | |
1127 | 0 | if let Some(authority) = &builder.authority { |
1128 | 0 | match &authority.userinfo.0 { |
1129 | 0 | UserinfoRepr::None => {} |
1130 | 0 | UserinfoRepr::Direct(userinfo) => { |
1131 | 0 | parser::validate_userinfo::<S>(userinfo)?; |
1132 | | } |
1133 | 0 | UserinfoRepr::UserPass(user, password) => { |
1134 | | // `user` is not allowed to have a colon, since the characters |
1135 | | // after the colon is parsed as the password. |
1136 | 0 | if user.contains(':') { |
1137 | 0 | return Err(Error::new()); |
1138 | 0 | } |
1139 | | |
1140 | | // Note that the syntax of components inside `authority` |
1141 | | // (`user` and `password`) is not specified by RFC 3986. |
1142 | 0 | parser::validate_userinfo::<S>(user)?; |
1143 | 0 | if let Some(password) = password { |
1144 | 0 | parser::validate_userinfo::<S>(password)?; |
1145 | 0 | } |
1146 | | } |
1147 | | } |
1148 | | |
1149 | 0 | match authority.host { |
1150 | 0 | HostRepr::String(s) => parser::validate_host::<S>(s)?, |
1151 | | #[cfg(feature = "std")] |
1152 | 0 | HostRepr::IpAddr(_) => {} |
1153 | | } |
1154 | | |
1155 | 0 | if let PortBuilderRepr::String(s) = authority.port.0 { |
1156 | 0 | if !s.bytes().all(|b| b.is_ascii_digit()) { |
1157 | 0 | return Err(Error::new()); |
1158 | 0 | } |
1159 | 0 | } |
1160 | 0 | } |
1161 | | |
1162 | | let path_is_absolute: bool; |
1163 | | let mut is_path_acceptable; |
1164 | 0 | if builder.normalize { |
1165 | 0 | if builder.scheme.is_some() || builder.authority.is_some() || builder.path.starts_with('/') |
1166 | | { |
1167 | 0 | if builder.authority.is_some() { |
1168 | | // Note that the path should already be in an absolute form before normalization. |
1169 | 0 | is_path_acceptable = builder.path.is_empty() || builder.path.starts_with('/'); |
1170 | 0 | } else { |
1171 | 0 | is_path_acceptable = true; |
1172 | 0 | } |
1173 | 0 | let op = normalize::NormalizationOp { |
1174 | 0 | mode: NormalizationMode::Default, |
1175 | 0 | }; |
1176 | 0 | let path_characteristic = PathCharacteristic::from_path_to_display::<S>( |
1177 | 0 | &normalize::PathToNormalize::from_single_path(builder.path), |
1178 | 0 | op, |
1179 | 0 | builder.authority.is_some(), |
1180 | | ); |
1181 | 0 | path_is_absolute = path_characteristic.is_absolute(); |
1182 | 0 | is_path_acceptable = is_path_acceptable |
1183 | 0 | && match path_characteristic { |
1184 | 0 | PathCharacteristic::CommonAbsolute | PathCharacteristic::CommonRelative => true, |
1185 | | PathCharacteristic::StartsWithDoubleSlash |
1186 | | | PathCharacteristic::RelativeFirstSegmentHasColon => { |
1187 | 0 | builder.scheme.is_some() || builder.authority.is_some() |
1188 | | } |
1189 | | }; |
1190 | 0 | } else { |
1191 | 0 | path_is_absolute = false; |
1192 | 0 | // If the path is relative (where neither scheme nor authority is |
1193 | 0 | // available), the first segment should not contain a colon. |
1194 | 0 | is_path_acceptable = prior_byte2(builder.path.as_bytes(), b'/', b':') != Some(b':'); |
1195 | 0 | } |
1196 | | } else { |
1197 | 0 | path_is_absolute = builder.path.starts_with('/'); |
1198 | 0 | is_path_acceptable = if builder.authority.is_some() { |
1199 | | // The path should be absolute or empty. |
1200 | 0 | path_is_absolute || builder.path.is_empty() |
1201 | 0 | } else if builder.scheme.is_some() || path_is_absolute { |
1202 | | // The path should not start with '//'. |
1203 | 0 | !builder.path.starts_with("//") |
1204 | | } else { |
1205 | | // If the path is relative (where neither scheme nor authority is |
1206 | | // available), the first segment should not contain a colon. |
1207 | 0 | prior_byte2(builder.path.as_bytes(), b'/', b':') != Some(b':') |
1208 | | }; |
1209 | | } |
1210 | 0 | if !is_path_acceptable { |
1211 | 0 | return Err(Error::new()); |
1212 | 0 | } |
1213 | | |
1214 | 0 | if let Some(query) = builder.query { |
1215 | 0 | parser::validate_query::<S>(query)?; |
1216 | 0 | } |
1217 | | |
1218 | 0 | if let Some(fragment) = builder.fragment { |
1219 | 0 | parser::validate_fragment::<S>(fragment)?; |
1220 | 0 | } |
1221 | | |
1222 | 0 | Ok(path_is_absolute) |
1223 | 0 | } |
1224 | | |
1225 | | /// Private module to put the trait to seal. |
1226 | | mod private { |
1227 | | use super::{Builder, Built, Error}; |
1228 | | |
1229 | | /// A trait for types buildable by the [`Builder`]. |
1230 | | pub trait Sealed<'a> { |
1231 | | /// Validates the content of the builder and returns the validated type if possible. |
1232 | | fn validate_builder(builder: Builder<'a>) -> Result<Built<'a, Self>, Error>; |
1233 | | } |
1234 | | } |