iri_string/types.rs
1//! URI and IRI types.
2//!
3//! # URI and IRI
4//!
5//! IRIs (Internationalized Resource Identifiers) are defined in [RFC 3987],
6//! and URIs (Uniform Resource Identifiers) are defined in [RFC 3986].
7//!
8//! URI consists of only ASCII characters, and is a subset of IRI.
9//!
10//! IRIs are defined as below:
11//!
12//! ```text
13//! IRI = scheme ":" ihier-part [ "?" iquery ] [ "#" ifragment ]
14//! IRI-reference = IRI / irelative-ref
15//! absolute-IRI = scheme ":" ihier-part [ "?" iquery ]
16//! irelative-ref = irelative-part [ "?" iquery ] [ "#" ifragment ]
17//! (`irelative-part` is roughly same as `ihier-part`.)
18//! ```
19//!
20//! Definitions for URIs are almost same, but they cannot have non-ASCII characters.
21//!
22//! # Types
23//!
24//! Types can be categorized by:
25//!
26//! * syntax,
27//! * spec, and
28//! * ownership.
29//!
30//! ## Syntax
31//!
32//! Since URIs and IRIs have almost same syntax and share algorithms, they are implemented by
33//! generic types.
34//!
35//! * [`RiStr`] and [`RiString`]
36//! + String types for `IRI` and `URI` rules.
37//! * [`RiAbsoluteStr`] and [`RiAbsoluteString`]
38//! + String types for `absolute-IRI` and `absolute-URI` rules.
39//! * [`RiReferenceStr`] and [`RiReferenceString`]
40//! + String types for `IRI-reference` and `URI-reference` rules.
41//! * [`RiRelativeStr`] and [`RiRelativeString`]
42//! + String types for `irelative-ref` and `relative-ref` rules.
43//! * [`RiFragmentStr`] and [`RiFragmentString`]
44//! + String types for `ifragment` and `fragment` rules.
45//! + Note that these types represents a substring of an IRI / URI references.
46//! They are not intended to used directly as an IRI / URI references.
47//!
48//! "Ri" stands for "Resource Identifier".
49//!
50//! ## Spec
51//!
52//! These types have a type parameter, which represents RFC specification.
53//! [`IriSpec`] represents [RFC 3987] spec, and [`UriSpec`] represents [RFC 3986] spec.
54//! For example, `RiAbsoluteStr<IriSpec>` can have `absolute-IRI` string value,
55//! and `RiReferenceStr<UriSpec>` can have `URI-reference` string value.
56//!
57//! ## Ownership
58//!
59//! String-like types have usually two variations, borrowed and owned.
60//!
61//! Borrowed types (such as `str`, `Path`, `OsStr`) are unsized, and used by reference style.
62//! Owned types (such as `String`, `PathBuf`, `OsString`) are sized, and requires heap allocation.
63//! Owned types can be coerced to a borrowed type (for example, `&String` is automatically coerced
64//! to `&str` in many context).
65//!
66//! IRI / URI types have same variations, `RiFooStr` and `RiFooString`
67//! (`Foo` part represents syntax).
68//! They are very similar to `&str` and `String`.
69//! `Deref` is implemented, `RiFooStr::len()` is available, `&RiFooString` can be coerced to
70//! `&RiFooStr`, `Cow<'_, RiFooStr>` and `Box<RiFooStr>` is available, and so on.
71//!
72//! # Hierarchy and safe conversion
73//!
74//! IRI syntaxes have the hierarchy below.
75//!
76//! ```text
77//! RiReferenceStr
78//! |-- RiStr
79//! | `-- RiAbsoluteStr
80//! `-- RiRelativeStr
81//! ```
82//!
83//! Therefore, the conversions below are safe and cheap:
84//!
85//! * `RiStr -> RiReferenceStr`
86//! * `RiAbsoluteStr -> RiStr`
87//! * `RiAbsoluteStr -> RiReferenceStr`
88//! * `RiRelativeStr -> RiReferenceStr`
89//!
90//! For safely convertible types (consider `FooStr -> BarStr` is safe), traits
91//! below are implemented:
92//!
93//! * `AsRef<BarStr> for FooStr`
94//! * `AsRef<BarStr> for FooString`
95//! * `From<FooString> for BarString`
96//! * `PartialEq<FooStr> for BarStr`, and lots of impls like that
97//! + `PartialEq` and `ParitalOrd`.
98//! + Slice, owned, `Cow`, reference, etc...
99//!
100//! ## Fallible conversions
101//!
102//! Fallible conversions are implemented from plain string into IRI strings.
103//!
104//! * `TryFrom<&str> for &FooStr`
105//! * `TryFrom<&str> for FooString`
106//! * `TryFrom<String> for FooString`
107//! * `FromStr for FooString`
108//!
109//! Some IRI string types provide more convenient methods to convert between IRI types.
110//! For example, [`RiReferenceString::into_iri()`] tries to convert an IRI reference into an IRI,
111//! and returns `Result<IriString, IriRelativeString>`.
112//! This is because an IRI reference is valid as an IRI or a relative IRI reference.
113//! Such methods are usually more efficient than using `TryFrom` for plain strings, because they
114//! prevents you from losing ownership of a string, and does a conversion without extra memory
115//! allocation.
116//!
117//! # Aliases
118//!
119//! This module contains type aliases for RFC 3986 URI types and RFC 3987 IRI types.
120//!
121//! `IriFooStr{,ing}` are aliases of `RiFooStr{,ing}<IriSpec>`, and `UriFooStr{,ing}` are aliases
122//! of `RiFooStr{,ing}<UriSpec>`.
123//!
124//! # Wrapped string types
125//!
126//! Similar to string types in std (such as `str`, `std::path::Path`, and `std::ffi::OsStr`),
127//! IRI string types in this crate provides convenient conversions to:
128//!
129//! * `std::box::Box`,
130//! * `std::borrow::Cow`,
131//! * `std::rc::Rc`, and
132//! * `std::sync::Arc`.
133//!
134//! ```
135//! # use iri_string::validate::Error;
136//! # #[cfg(feature = "std")] {
137//! use std::borrow::Cow;
138//! use std::rc::Rc;
139//! use std::sync::Arc;
140//!
141//! use iri_string::types::IriStr;
142//!
143//! let iri = IriStr::new("http://example.com/")?;
144//! let iri_owned = iri.to_owned();
145//!
146//! // From slice.
147//! let cow_1_1: Cow<'_, IriStr> = iri.into();
148//! let cow_1_2 = Cow::<'_, IriStr>::from(iri);
149//! assert!(matches!(cow_1_1, Cow::Borrowed(_)));
150//! assert!(matches!(cow_1_2, Cow::Borrowed(_)));
151//! // From owned.
152//! let cow_2_1: Cow<'_, IriStr> = iri_owned.clone().into();
153//! let cow_2_2 = Cow::<'_, IriStr>::from(iri_owned.clone());
154//! assert!(matches!(cow_2_1, Cow::Owned(_)));
155//! assert!(matches!(cow_2_2, Cow::Owned(_)));
156//!
157//! // From slice.
158//! let box_1_1: Box<IriStr> = iri.into();
159//! let box_1_2 = Box::<IriStr>::from(iri);
160//! // From owned.
161//! let box_2_1: Box<IriStr> = iri_owned.clone().into();
162//! let box_2_2 = Box::<IriStr>::from(iri_owned.clone());
163//!
164//! // From slice.
165//! let rc_1_1: Rc<IriStr> = iri.into();
166//! let rc_1_2 = Rc::<IriStr>::from(iri);
167//! // From owned.
168//! // Note that `From<owned> for Rc<borrowed>` is not implemented for now.
169//! // Get borrowed string by `.as_slice()` and convert it.
170//! let rc_2_1: Rc<IriStr> = iri_owned.clone().as_slice().into();
171//! let rc_2_2 = Rc::<IriStr>::from(iri_owned.clone().as_slice());
172//!
173//! // From slice.
174//! let arc_1_1: Arc<IriStr> = iri.into();
175//! let arc_1_2 = Arc::<IriStr>::from(iri);
176//! // From owned.
177//! // Note that `From<owned> for Arc<borrowed>` is not implemented for now.
178//! // Get borrowed string by `.as_slice()` and convert it.
179//! let arc_2_1: Arc<IriStr> = iri_owned.clone().as_slice().into();
180//! let arc_2_2 = Arc::<IriStr>::from(iri_owned.clone().as_slice());
181//! # }
182//! # Ok::<_, Error>(())
183//! ```
184//!
185//! # IDNA encoding
186//!
187//! This crate does not have built-in IDNA converter, but the user can provide
188//! such conversion function and replace the domain part of IRIs.
189//!
190//! ## Slice IRI types
191//!
192//! 1. Get host by `authority_components()?.host()`.
193//! 2. Process the name.
194//! 3. Create a builder by `Builder::from(&...)`.
195//! 4. Overwrite the domain by `.host(...)`.
196//! 5. Build the new IRI by `.build()`.
197//!
198//! ```
199//! # #[cfg(feature = "alloc")] extern crate alloc;
200//! # #[cfg(feature = "alloc")] use alloc::string::ToString;
201//! use iri_string::build::Builder;
202//! use iri_string::types::{IriStr, UriStr};
203//!
204//! struct IdnaEncodedDomain<'a> {
205//! /* ... */
206//! # raw: &'a str,
207//! }
208//! impl IdnaEncodedDomain<'_> {
209//! pub fn as_str(&self) -> &str {
210//! /* ... */
211//! # match self.raw {
212//! # "alpha.\u{03B1}.example.com" => "alpha.xn--mxa.example.com",
213//! # _ => unimplemented!(),
214//! # }
215//! }
216//! }
217//! // Usually IDNA conversion requires dynamic memory allocation, but
218//! // `iri-string` itself does not require or assume that. It is enough if the
219//! // conversion result can be retrieved as `&str`, so users can do whatever
220//! // such as limiting the possible input and/or using statically allocated buffer.
221//! fn apply_idna(s: &str) -> IdnaEncodedDomain<'_> {
222//! /* ... */
223//! # IdnaEncodedDomain { raw: s }
224//! }
225//!
226//! let orig_iri = IriStr::new("https://alpha.\u{03B1}.example.com").unwrap();
227//!
228//! // 1. Get the host.
229//! let orig_host = orig_iri.authority_components()
230//! .expect("orig_iri has a host")
231//! .host();
232//! debug_assert_eq!(orig_host, "alpha.\u{03B1}.example.com");
233//!
234//! // 2. Process the name.
235//! let new_domain = apply_idna(orig_host);
236//!
237//! // 3. Create a builder.
238//! let mut builder = Builder::from(orig_iri);
239//!
240//! // 4. Overwrite the domain.
241//! builder.host(new_domain.as_str());
242//!
243//! // 5. Build the new IRI.
244//! let new_iri = builder.build::<UriStr>()
245//! .expect("the new host is a valid domain and now they are US-ASCII only");
246//!
247//! // Note that `ToString::to_string()` requires `alloc` feature.
248//! #[cfg(feature = "alloc")]
249//! debug_assert_eq!(new_iri.to_string(), "https://alpha.xn--mxa.example.com");
250//! ```
251//!
252//! ## Allocated IRI types
253//!
254//! For allocated types such as `IriString`, you can use
255//! `{,try_}replace_host{,_reg_name}` methods.
256//!
257//! 1. Get host by `authority_components()?.host()`.
258//! 2. Process the name.
259//! 3. Replace the host by the new result.
260//!
261//! ```
262//! # #[cfg(feature = "alloc")] {
263//! # extern crate alloc;
264//! # use alloc::string::String;
265//! use iri_string::types::IriString;
266//!
267//! fn apply_idna(s: &str) -> String {
268//! /* ... */
269//! # match s {
270//! # "alpha.\u{03B1}.example.com" => "alpha.xn--mxa.example.com".to_owned(),
271//! # _ => unimplemented!(),
272//! # }
273//! }
274//!
275//! let mut iri =
276//! IriString::try_from("https://alpha.\u{03B1}.example.com")
277//! .unwrap();
278//!
279//! // 1. Get the host.
280//! let orig_host = iri.authority_components()
281//! .expect("orig_iri has a host")
282//! .host();
283//! debug_assert_eq!(orig_host, "alpha.\u{03B1}.example.com");
284//!
285//! // 2. Process the name.
286//! let new_domain = apply_idna(orig_host);
287//!
288//! // 3. Replace the host.
289//! iri.replace_host(&new_domain);
290//! debug_assert_eq!(iri, "https://alpha.xn--mxa.example.com");
291//! # }
292//! ```
293//!
294//! [RFC 3986]: https://www.rfc-editor.org/rfc/rfc3986.html
295//! [RFC 3987]: https://www.rfc-editor.org/rfc/rfc3987.html
296//! [`RiStr`]: struct.RiStr.html
297//! [`RiString`]: struct.RiString.html
298//! [`RiAbsoluteStr`]: struct.RiAbsoluteStr.html
299//! [`RiAbsoluteString`]: struct.RiAbsoluteString.html
300//! [`RiFragmentStr`]: struct.RiFragmentStr.html
301//! [`RiFragmentString`]: struct.RiFragmentString.html
302//! [`RiReferenceStr`]: struct.RiReferenceStr.html
303//! [`RiReferenceString`]: struct.RiReferenceString.html
304//! [`RiReferenceString::into_iri()`]: struct.RiReferenceString.html#method.into_iri
305//! [`RiRelativeStr`]: struct.RiRelativeStr.html
306//! [`RiRelativeString`]: struct.RiRelativeString.html
307//! [`IriSpec`]: ../spec/enum.IriSpec.html
308//! [`UriSpec`]: ../spec/enum.UriSpec.html
309
310#[cfg(feature = "alloc")]
311pub use self::{
312 generic::{
313 CreationError, RiAbsoluteString, RiFragmentString, RiQueryString, RiReferenceString,
314 RiRelativeString, RiString,
315 },
316 iri::{
317 IriAbsoluteString, IriFragmentString, IriQueryString, IriReferenceString,
318 IriRelativeString, IriString,
319 },
320 uri::{
321 UriAbsoluteString, UriFragmentString, UriQueryString, UriReferenceString,
322 UriRelativeString, UriString,
323 },
324};
325pub use self::{
326 generic::{RiAbsoluteStr, RiFragmentStr, RiQueryStr, RiReferenceStr, RiRelativeStr, RiStr},
327 iri::{IriAbsoluteStr, IriFragmentStr, IriQueryStr, IriReferenceStr, IriRelativeStr, IriStr},
328 uri::{UriAbsoluteStr, UriFragmentStr, UriQueryStr, UriReferenceStr, UriRelativeStr, UriStr},
329};
330
331pub(crate) mod generic;
332mod iri;
333mod uri;