ada_url/url_search_params.rs
1use crate::{ParseUrlError, ffi};
2
3#[derive(Hash)]
4pub struct UrlSearchParams(*mut ffi::ada_url_search_params);
5
6impl Drop for UrlSearchParams {
7 fn drop(&mut self) {
8 unsafe { ffi::ada_free_search_params(self.0) }
9 }
10}
11
12impl UrlSearchParams {
13 /// Parses an return a UrlSearchParams struct.
14 ///
15 /// ```
16 /// use ada_url::UrlSearchParams;
17 /// let params = UrlSearchParams::parse("a=1&b=2")
18 /// .expect("String should have been able to be parsed into an UrlSearchParams.");
19 /// assert_eq!(params.get("a"), Some("1"));
20 /// assert_eq!(params.get("b"), Some("2"));
21 /// ```
22 pub fn parse<Input>(input: Input) -> Result<Self, ParseUrlError<Input>>
23 where
24 Input: AsRef<str>,
25 {
26 Ok(Self(unsafe {
27 ffi::ada_parse_search_params(input.as_ref().as_ptr().cast(), input.as_ref().len())
28 }))
29 }
30
31 /// Returns the unique keys in a UrlSearchParams.
32 ///
33 /// ```
34 /// use ada_url::UrlSearchParams;
35 /// let params = UrlSearchParams::parse("a=1&b=2")
36 /// .expect("String should have been able to be parsed into an UrlSearchParams.");
37 /// assert_eq!(params.len(), 2);
38 /// let keys = params.keys().into_iter();
39 /// assert_eq!(keys.count(), params.len());
40 /// ```
41 pub fn len(&self) -> usize {
42 unsafe { ffi::ada_search_params_size(self.0) }
43 }
44
45 /// Returns true if no entries exist in the UrlSearchParams.
46 pub fn is_empty(&self) -> bool {
47 self.len() == 0
48 }
49
50 /// Sorts the keys of the UrlSearchParams struct.
51 pub fn sort(&mut self) {
52 unsafe { ffi::ada_search_params_sort(self.0) }
53 }
54
55 /// Appends a key/value to the UrlSearchParams struct.
56 pub fn append(&mut self, key: &str, value: &str) {
57 unsafe {
58 ffi::ada_search_params_append(
59 self.0,
60 key.as_ptr().cast(),
61 key.len(),
62 value.as_ptr().cast(),
63 value.len(),
64 )
65 }
66 }
67
68 /// Removes all pre-existing keys from the UrlSearchParams struct
69 /// and appends the new key/value.
70 ///
71 /// ```
72 /// use ada_url::UrlSearchParams;
73 /// let mut params = UrlSearchParams::parse("a=1&b=2")
74 /// .expect("String should have been able to be parsed into an UrlSearchParams.");
75 /// params.set("a", "3");
76 /// assert_eq!(params.get("a"), Some("3"));
77 /// ```
78 pub fn set(&mut self, key: &str, value: &str) {
79 unsafe {
80 ffi::ada_search_params_set(
81 self.0,
82 key.as_ptr().cast(),
83 key.len(),
84 value.as_ptr().cast(),
85 value.len(),
86 )
87 }
88 }
89
90 /// Removes a key from the UrlSearchParams struct.
91 ///
92 /// ```
93 /// use ada_url::UrlSearchParams;
94 /// let mut params = UrlSearchParams::parse("a=1&b=2")
95 /// .expect("String should have been able to be parsed into an UrlSearchParams.");
96 /// params.remove_key("a");
97 /// assert_eq!(params.get("a"), None);
98 /// ```
99 pub fn remove_key(&mut self, key: &str) {
100 unsafe { ffi::ada_search_params_remove(self.0, key.as_ptr().cast(), key.len()) }
101 }
102
103 /// Removes a key with a value from the UrlSearchParams struct.
104 ///
105 /// ```
106 /// use ada_url::UrlSearchParams;
107 /// let mut params = UrlSearchParams::parse("a=1&b=2")
108 /// .expect("String should have been able to be parsed into an UrlSearchParams.");
109 /// params.remove("a", "1");
110 /// assert_eq!(params.get("a"), None);
111 /// ```
112 pub fn remove(&mut self, key: &str, value: &str) {
113 unsafe {
114 ffi::ada_search_params_remove_value(
115 self.0,
116 key.as_ptr().cast(),
117 key.len(),
118 value.as_ptr().cast(),
119 value.len(),
120 )
121 }
122 }
123
124 /// Returns whether the [`UrlSearchParams`] contains the `key`.
125 ///
126 /// ```
127 /// use ada_url::UrlSearchParams;
128 /// let params = UrlSearchParams::parse("a=1&b=2")
129 /// .expect("String should have been able to be parsed into an UrlSearchParams.");
130 /// assert_eq!(params.contains_key("a"), true);
131 /// ```
132 pub fn contains_key(&self, key: &str) -> bool {
133 unsafe { ffi::ada_search_params_has(self.0, key.as_ptr().cast(), key.len()) }
134 }
135
136 /// Returns whether the [`UrlSearchParams`] contains the `key` with the `value`.
137 ///
138 /// ```
139 /// use ada_url::UrlSearchParams;
140 /// let params = UrlSearchParams::parse("a=1&b=2")
141 /// .expect("String should have been able to be parsed into an UrlSearchParams.");
142 /// assert_eq!(params.contains("a", "1"), true);
143 /// ```
144 pub fn contains(&self, key: &str, value: &str) -> bool {
145 unsafe {
146 ffi::ada_search_params_has_value(
147 self.0,
148 key.as_ptr().cast(),
149 key.len(),
150 value.as_ptr().cast(),
151 value.len(),
152 )
153 }
154 }
155
156 /// Returns the value of the key.
157 ///
158 /// ```
159 /// use ada_url::UrlSearchParams;
160 /// let params = UrlSearchParams::parse("a=1&b=2")
161 /// .expect("String should have been able to be parsed into an UrlSearchParams.");
162 /// assert_eq!(params.get("a"), Some("1"));
163 /// assert_eq!(params.get("c"), None);
164 /// ```
165 pub fn get(&self, key: &str) -> Option<&str> {
166 unsafe {
167 let out = ffi::ada_search_params_get(self.0, key.as_ptr().cast(), key.len());
168
169 if out.data.is_null() {
170 return None;
171 }
172 Some(out.as_str())
173 }
174 }
175
176 /// Returns all values of the key.
177 ///
178 /// ```
179 /// use ada_url::UrlSearchParams;
180 /// let params = UrlSearchParams::parse("a=1&a=2")
181 /// .expect("String should have been able to be parsed into an UrlSearchParams.");
182 /// let pairs = params.get_all("a");
183 /// assert_eq!(pairs.len(), 2);
184 /// ```
185 pub fn get_all(&self, key: &str) -> UrlSearchParamsEntry<'_> {
186 unsafe {
187 let strings = ffi::ada_search_params_get_all(self.0, key.as_ptr().cast(), key.len());
188 let size = ffi::ada_strings_size(strings);
189 UrlSearchParamsEntry::new(strings, size)
190 }
191 }
192
193 /// Returns all keys as an iterator
194 ///
195 /// ```
196 /// use ada_url::UrlSearchParams;
197 /// let params = UrlSearchParams::parse("a=1")
198 /// .expect("String should have been able to be parsed into an UrlSearchParams.");
199 /// let mut keys = params.keys();
200 /// assert!(keys.next().is_some());
201 pub fn keys(&self) -> UrlSearchParamsKeyIterator<'_> {
202 let iterator = unsafe { ffi::ada_search_params_get_keys(self.0) };
203 UrlSearchParamsKeyIterator::new(iterator)
204 }
205
206 /// Returns all values as an iterator
207 ///
208 /// ```
209 /// use ada_url::UrlSearchParams;
210 /// let params = UrlSearchParams::parse("a=1")
211 /// .expect("String should have been able to be parsed into an UrlSearchParams.");
212 /// let mut values = params.values();
213 /// assert!(values.next().is_some());
214 pub fn values(&self) -> UrlSearchParamsValueIterator<'_> {
215 let iterator = unsafe { ffi::ada_search_params_get_values(self.0) };
216 UrlSearchParamsValueIterator::new(iterator)
217 }
218
219 /// Returns all entries as an iterator
220 ///
221 /// ```
222 /// use ada_url::UrlSearchParams;
223 /// let params = UrlSearchParams::parse("a=1")
224 /// .expect("String should have been able to be parsed into an UrlSearchParams.");
225 /// let mut entries = params.entries();
226 /// assert_eq!(entries.next(), Some(("a", "1")));
227 /// ```
228 pub fn entries(&self) -> UrlSearchParamsEntryIterator<'_> {
229 let iterator = unsafe { ffi::ada_search_params_get_entries(self.0) };
230 UrlSearchParamsEntryIterator::new(iterator)
231 }
232}
233
234#[cfg(feature = "std")]
235impl core::str::FromStr for UrlSearchParams {
236 type Err = ParseUrlError<Box<str>>;
237
238 fn from_str(s: &str) -> Result<Self, Self::Err> {
239 Self::parse(s).map_err(|ParseUrlError { input }| ParseUrlError {
240 input: input.into(),
241 })
242 }
243}
244
245/// Returns the stringified version of the UrlSearchParams struct.
246///
247/// ```
248/// use ada_url::UrlSearchParams;
249/// let params = UrlSearchParams::parse("a=1&b=2")
250/// .expect("String should have been able to be parsed into an UrlSearchParams.");
251/// assert_eq!(params.to_string(), "a=1&b=2");
252/// ```
253impl core::fmt::Display for UrlSearchParams {
254 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
255 let str = unsafe { ffi::ada_search_params_to_string(self.0) };
256 f.write_str(str.as_ref())
257 }
258}
259
260#[cfg(feature = "std")]
261impl<Input> Extend<(Input, Input)> for UrlSearchParams
262where
263 Input: AsRef<str>,
264{
265 /// Supports extending UrlSearchParams through an iterator.
266 ///
267 ///```
268 /// use ada_url::UrlSearchParams;
269 /// let mut params = UrlSearchParams::parse("a=1&b=2")
270 /// .expect("String should have been able to be parsed into an UrlSearchParams.");
271 /// assert_eq!(params.len(), 2);
272 /// params.extend([("foo", "bar")]);
273 /// assert_eq!(params.len(), 3);
274 /// ```
275 fn extend<T: IntoIterator<Item = (Input, Input)>>(&mut self, iter: T) {
276 for item in iter {
277 self.append(item.0.as_ref(), item.1.as_ref());
278 }
279 }
280}
281
282#[cfg(feature = "std")]
283impl<Input> FromIterator<(Input, Input)> for UrlSearchParams
284where
285 Input: AsRef<str>,
286{
287 /// Converts an iterator to UrlSearchParams
288 ///
289 /// ```
290 /// use ada_url::UrlSearchParams;
291 /// let iterator = std::iter::repeat(("hello", "world")).take(5);
292 /// let params = UrlSearchParams::from_iter(iterator);
293 /// assert_eq!(params.len(), 5);
294 /// ```
295 fn from_iter<T: IntoIterator<Item = (Input, Input)>>(iter: T) -> Self {
296 let mut params = UrlSearchParams::parse("")
297 .expect("Should be able to parse empty string. This is likely due to a bug");
298 for item in iter {
299 params.append(item.0.as_ref(), item.1.as_ref());
300 }
301 params
302 }
303}
304
305#[derive(Hash)]
306pub struct UrlSearchParamsKeyIterator<'a> {
307 iterator: *mut ffi::ada_url_search_params_keys_iter,
308 _phantom: core::marker::PhantomData<&'a str>,
309}
310
311impl Drop for UrlSearchParamsKeyIterator<'_> {
312 fn drop(&mut self) {
313 unsafe { ffi::ada_free_search_params_keys_iter(self.iterator) }
314 }
315}
316
317impl<'a> Iterator for UrlSearchParamsKeyIterator<'a> {
318 type Item = &'a str;
319
320 fn next(&mut self) -> Option<Self::Item> {
321 let has_next = unsafe { ffi::ada_search_params_keys_iter_has_next(self.iterator) };
322 if has_next {
323 let string = unsafe { ffi::ada_search_params_keys_iter_next(self.iterator) };
324 Some(string.as_str())
325 } else {
326 None
327 }
328 }
329}
330
331#[derive(Hash)]
332pub struct UrlSearchParamsValueIterator<'a> {
333 iterator: *mut ffi::ada_url_search_params_values_iter,
334 _phantom: core::marker::PhantomData<&'a str>,
335}
336
337impl<'a> UrlSearchParamsKeyIterator<'a> {
338 fn new(iterator: *mut ffi::ada_url_search_params_keys_iter) -> UrlSearchParamsKeyIterator<'a> {
339 UrlSearchParamsKeyIterator {
340 iterator,
341 _phantom: core::marker::PhantomData,
342 }
343 }
344}
345
346impl Drop for UrlSearchParamsValueIterator<'_> {
347 fn drop(&mut self) {
348 unsafe { ffi::ada_free_search_params_values_iter(self.iterator) }
349 }
350}
351
352impl<'a> Iterator for UrlSearchParamsValueIterator<'a> {
353 type Item = &'a str;
354
355 fn next(&mut self) -> Option<Self::Item> {
356 let has_next = unsafe { ffi::ada_search_params_values_iter_has_next(self.iterator) };
357 if has_next {
358 let string = unsafe { ffi::ada_search_params_values_iter_next(self.iterator) };
359 Some(string.as_str())
360 } else {
361 None
362 }
363 }
364}
365
366impl<'a> UrlSearchParamsValueIterator<'a> {
367 fn new(
368 iterator: *mut ffi::ada_url_search_params_values_iter,
369 ) -> UrlSearchParamsValueIterator<'a> {
370 UrlSearchParamsValueIterator {
371 iterator,
372 _phantom: core::marker::PhantomData,
373 }
374 }
375}
376
377pub struct UrlSearchParamsEntry<'a> {
378 strings: *mut ffi::ada_strings,
379 size: usize,
380 _phantom: core::marker::PhantomData<&'a str>,
381}
382
383impl<'a> UrlSearchParamsEntry<'a> {
384 fn new(strings: *mut ffi::ada_strings, size: usize) -> UrlSearchParamsEntry<'a> {
385 UrlSearchParamsEntry {
386 strings,
387 size,
388 _phantom: core::marker::PhantomData,
389 }
390 }
391
392 /// Returns whether the key value pair is empty or not
393 ///
394 /// ```
395 /// use ada_url::UrlSearchParams;
396 /// let params = UrlSearchParams::parse("a=1&b=2")
397 /// .expect("String should have been able to be parsed into an UrlSearchParams.");
398 /// let pairs = params.get_all("a");
399 /// assert_eq!(pairs.is_empty(), false);
400 /// ```
401 pub fn is_empty(&self) -> bool {
402 self.size == 0
403 }
404
405 /// Returns the size of the key value pairs
406 ///
407 /// ```
408 /// use ada_url::UrlSearchParams;
409 /// let params = UrlSearchParams::parse("a=1&b=2")
410 /// .expect("String should have been able to be parsed into an UrlSearchParams.");
411 /// let pairs = params.get_all("a");
412 /// assert_eq!(pairs.len(), 1);
413 /// ```
414 pub fn len(&self) -> usize {
415 self.size
416 }
417
418 /// Get an entry by index
419 ///
420 /// ```
421 /// use ada_url::UrlSearchParams;
422 /// let params = UrlSearchParams::parse("a=1&a=2")
423 /// .expect("String should have been able to be parsed into an UrlSearchParams.");
424 /// let pairs = params.get_all("a");
425 /// assert_eq!(pairs.len(), 2);
426 /// assert_eq!(pairs.get(0), Some("1"));
427 /// assert_eq!(pairs.get(1), Some("2"));
428 /// assert_eq!(pairs.get(2), None);
429 /// assert_eq!(pairs.get(55), None);
430 /// ```
431 pub fn get(&self, index: usize) -> Option<&str> {
432 if self.size == 0 || index > self.size - 1 {
433 return None;
434 }
435
436 unsafe {
437 let string = ffi::ada_strings_get(self.strings, index);
438 Some(string.as_str())
439 }
440 }
441}
442
443impl Drop for UrlSearchParamsEntry<'_> {
444 fn drop(&mut self) {
445 unsafe { ffi::ada_free_strings(self.strings) }
446 }
447}
448
449#[cfg(feature = "std")]
450impl<'a> From<UrlSearchParamsEntry<'a>> for Vec<&'a str> {
451 fn from(val: UrlSearchParamsEntry<'a>) -> Self {
452 let mut vec = Vec::with_capacity(val.size);
453 unsafe {
454 for index in 0..val.size {
455 let string = ffi::ada_strings_get(val.strings, index);
456 let slice = core::slice::from_raw_parts(string.data.cast(), string.length);
457 vec.push(core::str::from_utf8_unchecked(slice));
458 }
459 }
460 vec
461 }
462}
463
464#[derive(Hash)]
465pub struct UrlSearchParamsEntryIterator<'a> {
466 iterator: *mut ffi::ada_url_search_params_entries_iter,
467 _phantom: core::marker::PhantomData<&'a str>,
468}
469
470impl<'a> UrlSearchParamsEntryIterator<'a> {
471 fn new(
472 iterator: *mut ffi::ada_url_search_params_entries_iter,
473 ) -> UrlSearchParamsEntryIterator<'a> {
474 UrlSearchParamsEntryIterator {
475 iterator,
476 _phantom: core::marker::PhantomData,
477 }
478 }
479}
480
481impl Drop for UrlSearchParamsEntryIterator<'_> {
482 fn drop(&mut self) {
483 unsafe { ffi::ada_free_search_params_entries_iter(self.iterator) }
484 }
485}
486
487impl<'a> Iterator for UrlSearchParamsEntryIterator<'a> {
488 type Item = (&'a str, &'a str);
489
490 fn next(&mut self) -> Option<Self::Item> {
491 let has_next = unsafe { ffi::ada_search_params_entries_iter_has_next(self.iterator) };
492 if has_next {
493 let pair = unsafe { ffi::ada_search_params_entries_iter_next(self.iterator) };
494 Some((pair.key.as_str(), pair.value.as_str()))
495 } else {
496 None
497 }
498 }
499}