query_string_builder/lib.rs
1//! # A query string builder for percent encoding key-value pairs
2//!
3//! This is a tiny helper crate for simplifying the construction of URL query strings.
4//! The initial `?` question mark is automatically prepended.
5//!
6//! Two builders are provided:
7//!
8//! - [`QueryString`] borrows its keys and values and never allocates strings —
9//! neither while building nor while rendering. Use it when you assemble and
10//! format the query string in one place.
11//! - [`QueryStringOwned`] owns its data and has no lifetime parameter. Use it
12//! when you want to store the builder in a struct or return it from a
13//! function. A borrowing builder can be converted via
14//! [`QueryString::into_owned`].
15//!
16//! ## Example
17//!
18//! ```
19//! use query_string_builder::QueryString;
20//!
21//! let tasty = true;
22//! let qs = QueryString::new()
23//! .with("q", "🍎 apple")
24//! .with("tasty", &tasty)
25//! .with_opt("color", None::<&str>)
26//! .with_opt("category", Some("fruits and vegetables?"));
27//!
28//! assert_eq!(
29//! format!("example.com/{qs}"),
30//! "example.com/?q=%F0%9F%8D%8E%20apple&tasty=true&category=fruits%20and%20vegetables?"
31//! );
32//! ```
33//!
34//! Since [`QueryString`] borrows its values, inline temporaries don't live long
35//! enough — bind them to a variable first, or use [`QueryStringOwned`]:
36//!
37//! ```
38//! use query_string_builder::QueryStringOwned;
39//!
40//! let qs = QueryStringOwned::new()
41//! .with("answer", 42)
42//! .with("works", true);
43//!
44//! assert_eq!(format!("example.com/{qs}"), "example.com/?answer=42&works=true");
45//! ```
46
47#![deny(unsafe_code)]
48
49mod borrowed;
50mod encode;
51mod owned;
52
53use percent_encoding::{AsciiSet, CONTROLS};
54
55pub use borrowed::{IntoPart, Part, QueryString};
56pub use owned::QueryStringOwned;
57
58/// https://url.spec.whatwg.org/#query-percent-encode-set
59pub(crate) const QUERY: &AsciiSet = &CONTROLS
60 .add(b' ')
61 .add(b'"')
62 .add(b'#')
63 .add(b'<')
64 .add(b'>')
65 // The following values are not strictly required by RFC 3986 but could help resolving recursion
66 // where a URL is passed as a value. In these cases, occurrences of equal signs and ampersands
67 // could break parsing.
68 // By a similar logic, encoding the percent sign helps to resolve ambiguity.
69 // The plus sign is also added to the set as to not confuse it with a space.
70 .add(b'%')
71 .add(b'&')
72 .add(b'=')
73 .add(b'+');