bind & implement more rust-url utilities

This commit is contained in:
Halil Durak
2026-06-05 17:42:51 +03:00
parent 234482fd32
commit f2cd349866
2 changed files with 106 additions and 3 deletions

View File

@@ -272,6 +272,15 @@ pub unsafe extern "C" fn url_set_port(url: *mut Url, port: u16) -> i32 {
}
}
#[no_mangle]
pub unsafe extern "C" fn url_set_port_to_null(url: *mut Url) -> i32 {
let url = unsafe { &mut *url };
match url.set_port(None) {
Ok(()) => 0,
Err(()) => -1,
}
}
#[no_mangle]
pub unsafe extern "C" fn url_get_port(url: *const Url) -> i32 {
let url = unsafe { &*url };
@@ -281,8 +290,9 @@ pub unsafe extern "C" fn url_get_port(url: *const Url) -> i32 {
}
}
/// WHATWG `hostname` setter: sets the host without touching the port.
#[no_mangle]
pub unsafe extern "C" fn url_set_host(url: *mut Url, ptr: *const c_uchar, len: usize) -> i32 {
pub unsafe extern "C" fn url_set_hostname(url: *mut Url, ptr: *const c_uchar, len: usize) -> i32 {
let url = unsafe { &mut *url };
let slice = match str_from(ptr, len) {
@@ -296,8 +306,10 @@ pub unsafe extern "C" fn url_set_host(url: *mut Url, ptr: *const c_uchar, len: u
}
}
/// WHATWG `hostname` getter: the host without the port. Borrows into the Url's
/// buffer; returns -1 (and writes an empty slice) when there is no host.
#[no_mangle]
pub unsafe extern "C" fn url_get_host(
pub unsafe extern "C" fn url_get_hostname(
url: *const Url,
out_ptr: *mut *const c_uchar,
out_len: *mut usize,
@@ -317,6 +329,76 @@ pub unsafe extern "C" fn url_get_host(
}
}
#[no_mangle]
pub unsafe extern "C" fn url_get_host(
url: *const Url,
out_ptr: *mut *const c_uchar,
out_len: *mut usize,
) -> i32 {
let url = unsafe { &*url };
if url.host_str().is_none() {
unsafe {
*out_len = 0;
}
return -1;
}
let host = &url[url::Position::BeforeHost..url::Position::AfterPort];
unsafe {
*out_ptr = host.as_ptr();
*out_len = host.len();
}
0
}
/// rust-url's `set_host` discards any trailing `:port`, so we split it off
/// ourselves (respecting IPv6 `[...]` literals) and apply the port separately.
#[no_mangle]
pub unsafe extern "C" fn url_set_host(url: *mut Url, ptr: *const c_uchar, len: usize) -> i32 {
let url = unsafe { &mut *url };
let input = match str_from(ptr, len) {
Some(s) => s,
None => return -1,
};
// Find the port separator ':', but only outside an IPv6 [...] literal.
let colon = if input.starts_with('[') {
input
.find(']')
.and_then(|b| input[b..].find(':').map(|i| b + i))
} else {
input.find(':')
};
let Some(i) = colon else {
// No port given: set the host only, leaving any existing port untouched.
return if url.set_host(Some(input)).is_ok() {
0
} else {
-1
};
};
let (host, port_str) = (&input[..i], &input[i + 1..]);
// Validate the port up-front so we never apply the host and then fail.
let new_port: Option<u16> = if port_str.is_empty() {
None // "host:" clears the port
} else {
match port_str.parse::<u16>() {
Ok(p) => Some(p),
Err(_) => return -1,
}
};
if url.set_host(Some(host)).is_err() {
return -1;
}
// set_port only errors on cannot-be-a-base, already ruled out by set_host.
let _ = url.set_port(new_port);
0
}
#[no_mangle]
pub unsafe extern "C" fn url_set_scheme(url: *mut Url, ptr: *const c_uchar, len: usize) -> i32 {
let url = unsafe { &mut *url };
@@ -408,6 +490,12 @@ pub unsafe extern "C" fn url_set_fragment(url: *mut Url, ptr: *const c_uchar, le
0
}
#[no_mangle]
pub unsafe extern "C" fn url_set_fragment_to_null(url: *mut Url) {
let url = unsafe { &mut *url };
url.set_fragment(None);
}
#[no_mangle]
pub unsafe extern "C" fn url_get_fragment(
url: *const Url,

View File

@@ -56,12 +56,27 @@ pub extern "c" fn url_free(url: *Url) void;
pub extern "c" fn url_can_parse(ptr: [*]const u8, len: usize) bool;
pub extern "c" fn url_can_parse_with_base(base_ptr: [*]const u8, base_len: usize, ptr: [*]const u8, len: usize) bool;
pub extern "c" fn url_to_string(url: *const Url, out_ptr: *[*]const u8, out_len: *usize) void;
pub extern "c" fn url_set_hostname(url: *Url, ptr: [*]const u8, len: usize) i32;
pub extern "c" fn url_get_hostname(url: *const Url, out_ptr: *[*]const u8, out_len: *usize) i32;
pub extern "c" fn url_set_host(url: *Url, ptr: [*]const u8, len: usize) i32;
pub extern "c" fn url_get_host(url: *const Url, out_ptr: *[*]const u8, out_len: *usize) void;
pub extern "c" fn url_get_host(url: *const Url, out_ptr: *[*]const u8, out_len: *usize) i32;
/// This function allocates on Rust-side.
pub extern "c" fn url_get_origin(url: *const Url) OwnedString;
pub extern "c" fn free_owned_string(owned: OwnedString) void;
pub extern "c" fn url_set_port(url: *Url, port: u16) i32;
/// Sets the port to null.
pub extern "c" fn url_set_port_to_null(url: *Url) i32;
pub extern "c" fn url_set_username(url: *Url, ptr: [*]const u8, len: usize) i32;
pub extern "c" fn url_get_username(url: *const Url, out_ptr: *[*]const u8, out_len: *usize) void;
pub extern "c" fn url_set_password(url: *Url, ptr: [*]const u8, len: usize) i32;
pub extern "c" fn url_get_password(url: *const Url, out_ptr: *[*]const u8, out_len: *usize) i32;
pub extern "c" fn url_set_path(url: *Url, ptr: [*]const u8, len: usize) i32;
pub extern "c" fn url_get_path(url: *const Url, out_ptr: *[*]const u8, out_len: *usize) void;
pub extern "c" fn url_set_scheme(url: *Url, ptr: [*]const u8, len: usize) i32;
pub extern "c" fn url_get_scheme(url: *const Url, out_ptr: *[*]const u8, out_len: *usize) void;
pub extern "c" fn url_set_fragment(url: *Url, ptr: [*]const u8, len: usize) i32;
pub extern "c" fn url_set_fragment_to_null(url: *Url) void;
pub extern "c" fn url_get_fragment(url: *const Url, out_ptr: *[*]const u8, out_len: *usize) i32;
extern "c" fn url_get_port(url: *const Url) i32;
pub inline fn urlGetPort(url: *const Url) ?u16 {