Skip to content

Commit

Permalink
feat: add URLSearchParams implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
anonrig committed Nov 25, 2023
1 parent 57b5a20 commit 4816774
Show file tree
Hide file tree
Showing 3 changed files with 321 additions and 1 deletion.
134 changes: 134 additions & 0 deletions src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -37,6 +43,38 @@ impl AsRef<str> for ada_owned_string {
}
}

/// Represents an std::vector<std::string>
#[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,
Expand Down Expand Up @@ -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;
}
4 changes: 3 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -730,7 +732,7 @@ impl PartialEq for Url {

impl PartialOrd for Url {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
self.href().partial_cmp(other.href())
Some(self.cmp(other))
}
}

Expand Down
184 changes: 184 additions & 0 deletions src/url_search_params.rs
Original file line number Diff line number Diff line change
@@ -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
}
}
}

0 comments on commit 4816774

Please sign in to comment.