Skip to main content

iri_string/
build.rs

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