Skip to main content

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'+');