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}