From 4816774d07a483e62b6bf0d6927e31ceb1de2c41 Mon Sep 17 00:00:00 2001 From: Yagiz Nizipli Date: Sat, 25 Nov 2023 10:14:59 -0500 Subject: [PATCH] feat: add URLSearchParams implementation --- src/ffi.rs | 134 ++++++++++++++++++++++++++++ src/lib.rs | 4 +- src/url_search_params.rs | 184 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 321 insertions(+), 1 deletion(-) create mode 100644 src/url_search_params.rs diff --git a/src/ffi.rs b/src/ffi.rs index 4110bed..e2e9e90 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -7,6 +7,12 @@ pub struct ada_url { _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>, } +#[repr(C)] +pub struct ada_url_search_params { + _unused: [u8; 0], + _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>, +} + #[repr(C)] pub struct ada_string { pub data: *const c_char, @@ -37,6 +43,38 @@ impl AsRef for ada_owned_string { } } +/// Represents an std::vector +#[repr(C)] +pub struct ada_strings { + _unused: [u8; 0], + _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>, +} + +#[repr(C)] +pub struct ada_url_search_params_keys_iter { + _unused: [u8; 0], + _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>, +} + +#[repr(C)] +pub struct ada_url_search_params_values_iter { + _unused: [u8; 0], + _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>, +} + +#[repr(C)] +pub struct ada_url_search_params_entries_iter { + _unused: [u8; 0], + _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>, +} + +/// Represents a key/value pair of strings +#[repr(C)] +pub struct ada_string_pair { + pub key: ada_string, + pub value: ada_string, +} + #[repr(C)] pub struct ada_url_components { pub protocol_end: u32, @@ -116,4 +154,100 @@ extern "C" { // IDNA methods pub fn ada_idna_to_unicode(input: *const c_char, length: usize) -> ada_owned_string; pub fn ada_idna_to_ascii(input: *const c_char, length: usize) -> ada_owned_string; + + // URLSearchParams + pub fn ada_parse_search_params( + input: *const c_char, + length: usize, + ) -> *mut ada_url_search_params; + pub fn ada_free_search_params(search_params: *mut ada_url_search_params); + pub fn ada_search_params_size(search_params: *mut ada_url_search_params) -> usize; + pub fn ada_search_params_sort(search_params: *mut ada_url_search_params); + pub fn ada_search_params_to_string( + search_params: *mut ada_url_search_params, + ) -> ada_owned_string; + pub fn ada_search_params_append( + search_params: *mut ada_url_search_params, + name: *const c_char, + name_length: usize, + value: *const c_char, + value_length: usize, + ); + pub fn ada_search_params_set( + search_params: *mut ada_url_search_params, + name: *const c_char, + name_length: usize, + value: *const c_char, + value_length: usize, + ); + pub fn ada_search_params_remove( + search_params: *mut ada_url_search_params, + name: *const c_char, + name_length: usize, + ); + pub fn ada_search_params_remove_value( + search_params: *mut ada_url_search_params, + name: *const c_char, + name_length: usize, + value: *const c_char, + value_length: usize, + ); + pub fn ada_search_params_has( + search_params: *mut ada_url_search_params, + name: *const c_char, + name_length: usize, + ) -> bool; + pub fn ada_search_params_has_value( + search_params: *mut ada_url_search_params, + name: *const c_char, + name_length: usize, + value: *const c_char, + value_length: usize, + ) -> bool; + pub fn ada_search_params_get( + search_params: *mut ada_url_search_params, + key: *const c_char, + key_length: usize, + ) -> ada_string; + pub fn ada_search_params_get_all( + // not implemented + search_params: *mut ada_url_search_params, + key: *const c_char, + key_length: usize, + ) -> *mut ada_strings; + pub fn ada_search_params_get_keys( + search_params: *mut ada_url_search_params, + ) -> *mut ada_url_search_params_keys_iter; + pub fn ada_search_params_get_values( + search_params: *mut ada_url_search_params, + ) -> *mut ada_url_search_params_values_iter; + pub fn ada_search_params_get_entries( + search_params: *mut ada_url_search_params, + ) -> *mut ada_url_search_params_entries_iter; + + pub fn ada_free_strings(strings: *mut ada_strings); + pub fn ada_strings_size(strings: *mut ada_strings) -> usize; + pub fn ada_strings_get(strings: *mut ada_strings, index: usize) -> ada_string; + pub fn ada_free_search_params_keys_iter(iter: *mut ada_url_search_params_keys_iter); + pub fn ada_search_params_keys_iter_next( + iter: *mut ada_url_search_params_keys_iter, + ) -> *mut ada_string; + pub fn ada_search_params_keys_iter_has_next(iter: *mut ada_url_search_params_keys_iter) + -> bool; + + pub fn ada_free_search_params_values_iter(iter: *mut ada_url_search_params_values_iter); + pub fn ada_search_params_values_iter_next( + iter: *mut ada_url_search_params_values_iter, + ) -> *mut ada_string; + pub fn ada_search_params_values_iter_has_next( + iter: *mut ada_url_search_params_values_iter, + ) -> bool; + + pub fn ada_free_search_params_entries_iter(iter: *mut ada_url_search_params_entries_iter); + pub fn ada_search_params_entries_iter_next( + iter: *mut ada_url_search_params_entries_iter, + ) -> *mut ada_string_pair; + pub fn ada_search_params_entries_iter_has_next( + iter: *mut ada_url_search_params_entries_iter, + ) -> bool; } diff --git a/src/lib.rs b/src/lib.rs index 32f0b19..d3b38f9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,7 +44,9 @@ pub mod ffi; mod idna; +mod url_search_params; pub use idna::Idna; +pub use url_search_params::URLSearchParams; use core::{borrow, ffi::c_uint, fmt, hash, ops}; use derive_more::Display; @@ -730,7 +732,7 @@ impl PartialEq for Url { impl PartialOrd for Url { fn partial_cmp(&self, other: &Self) -> Option { - self.href().partial_cmp(other.href()) + Some(self.cmp(other)) } } diff --git a/src/url_search_params.rs b/src/url_search_params.rs new file mode 100644 index 0000000..c5bca2d --- /dev/null +++ b/src/url_search_params.rs @@ -0,0 +1,184 @@ +use crate::ffi; + +pub struct URLSearchParams(*mut ffi::ada_url_search_params); + +impl Drop for URLSearchParams { + fn drop(&mut self) { + unsafe { ffi::ada_free_search_params(self.0) } + } +} + +impl URLSearchParams { + /// Parses an return a URLSearchParams struct. + /// + /// ``` + /// use ada_url::URLSearchParams; + /// let params = URLSearchParams::parse("a=1&b=2"); + /// assert_eq!(params.get("a"), Some("1")); + /// assert_eq!(params.get("b"), Some("2")); + /// ``` + pub fn parse(input: &str) -> Self { + Self(unsafe { ffi::ada_parse_search_params(input.as_ptr().cast(), input.len()) }) + } + + /// Returns the size of the URLSearchParams struct. + /// + /// ``` + /// use ada_url::URLSearchParams; + /// let params = URLSearchParams::parse("a=1&b=2"); + /// assert_eq!(params.size(), 2); + /// ``` + pub fn size(&self) -> usize { + unsafe { ffi::ada_search_params_size(self.0) } + } + + /// Sorts the keys of the URLSearchParams struct. + pub fn sort(&self) { + unsafe { ffi::ada_search_params_sort(self.0) } + } + + /// Appends a key/value to the URLSearchParams struct. + pub fn append(&self, key: &str, value: &str) { + unsafe { + ffi::ada_search_params_append( + self.0, + key.as_ptr().cast(), + key.len(), + value.as_ptr().cast(), + value.len(), + ) + } + } + + /// Removes all keys pre-existing keys from the URLSearchParams struct + /// and appends the new key/value. + /// + /// ``` + /// use ada_url::URLSearchParams; + /// let params = URLSearchParams::parse("a=1&b=2"); + /// params.set("a", "3"); + /// assert_eq!(params.get("a"), Some("3")); + /// ``` + pub fn set(&self, key: &str, value: &str) { + unsafe { + ffi::ada_search_params_set( + self.0, + key.as_ptr().cast(), + key.len(), + value.as_ptr().cast(), + value.len(), + ) + } + } + + /// Removes a key/value from the URLSearchParams struct. + /// Depending on the value parameter, it will either remove + /// the key/value pair or just the key. + /// + /// ``` + /// use ada_url::URLSearchParams; + /// let params = URLSearchParams::parse("a=1&b=2"); + /// params.remove("a", Some("1")); + /// assert_eq!(params.get("a"), None); + /// ``` + pub fn remove(&self, key: &str, value: Option<&str>) { + if let Some(value) = value { + unsafe { + ffi::ada_search_params_remove_value( + self.0, + key.as_ptr().cast(), + key.len(), + value.as_ptr().cast(), + value.len(), + ) + } + } else { + unsafe { ffi::ada_search_params_remove(self.0, key.as_ptr().cast(), key.len()) } + } + } + + /// Retruns true if the URLSearchParams struct contains the key. + /// + /// ``` + /// use ada_url::URLSearchParams; + /// let params = URLSearchParams::parse("a=1&b=2"); + /// assert_eq!(params.has("a", None), true); + /// ``` + pub fn has(&self, key: &str, value: Option<&str>) -> bool { + if let Some(value) = value { + unsafe { + ffi::ada_search_params_has_value( + self.0, + key.as_ptr().cast(), + key.len(), + value.as_ptr().cast(), + value.len(), + ) + } + } else { + unsafe { ffi::ada_search_params_has(self.0, key.as_ptr().cast(), key.len()) } + } + } + + /// Returns the value of the key. + /// + /// ``` + /// use ada_url::URLSearchParams; + /// let params = URLSearchParams::parse("a=1&b=2"); + /// assert_eq!(params.get("a"), Some("1")); + /// assert_eq!(params.get("c"), None); + /// ``` + pub fn get(&self, key: &str) -> Option<&str> { + unsafe { + let out = ffi::ada_search_params_get(self.0, key.as_ptr().cast(), key.len()); + + if out.data.is_null() { + return None; + } + let slice = core::slice::from_raw_parts(out.data.cast(), out.length); + Some(core::str::from_utf8_unchecked(slice)) + } + } + + /// Returns the stringified version of the URLSearchParams struct. + /// + /// ``` + /// use ada_url::URLSearchParams; + /// let params = URLSearchParams::parse("a=1&b=2"); + /// assert_eq!(params.to_string(), "a=1&b=2"); + /// ``` + pub fn to_string(&self) -> &str { + unsafe { + let out = ffi::ada_search_params_to_string(self.0); + let slice = core::slice::from_raw_parts(out.data.cast(), out.length); + core::str::from_utf8_unchecked(slice) + } + } + + /// Returns all values of the key. + /// + /// ``` + /// use ada_url::URLSearchParams; + /// let params = URLSearchParams::parse("a=1&a=2"); + /// assert_eq!(params.get_all("a"), vec!["1", "2"]); + /// ``` + pub fn get_all(&self, key: &str) -> Vec<&str> { + unsafe { + let strings = ffi::ada_search_params_get_all(self.0, key.as_ptr().cast(), key.len()); + let size = ffi::ada_strings_size(strings); + let mut out = Vec::with_capacity(size); + + if size == 0 { + return out; + } + + for index in 0..size { + let string = ffi::ada_strings_get(strings, index); + let slice = core::slice::from_raw_parts(string.data.cast(), string.length); + out.push(core::str::from_utf8_unchecked(slice)); + } + + out + } + } +}