Coverage Report

Created: 2026-01-30 06:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}