LibUnicode/Rust: Expose IDNA to_ascii to Rust
Add a thin FFI wrapper for Unicode::IDNA::to_ascii.
This commit is contained in:
parent
f985ed83d4
commit
8cc458e73a
3 changed files with 90 additions and 0 deletions
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include <LibUnicode/ICU.h>
|
||||
#include <LibUnicode/IDNA.h>
|
||||
#include <LibUnicode/RustFFI.h>
|
||||
|
||||
#include <unicode/idna.h>
|
||||
|
||||
|
|
@ -63,3 +64,27 @@ ErrorOr<String> to_ascii(Utf8View domain_name, ToAsciiOptions const& options)
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
namespace Unicode::FFI {
|
||||
|
||||
extern "C" void unicode_rust_idna_to_ascii(uint8_t const* domain, size_t domain_length, ToAsciiOptions const* options, void* ctx, FfiIdnaResultFn on_success)
|
||||
{
|
||||
Unicode::IDNA::ToAsciiOptions const cpp_options {
|
||||
options->check_hyphens ? Unicode::IDNA::CheckHyphens::Yes : Unicode::IDNA::CheckHyphens::No,
|
||||
options->check_bidi ? Unicode::IDNA::CheckBidi::Yes : Unicode::IDNA::CheckBidi::No,
|
||||
options->check_joiners ? Unicode::IDNA::CheckJoiners::Yes : Unicode::IDNA::CheckJoiners::No,
|
||||
options->use_std3_ascii_rules ? Unicode::IDNA::UseStd3AsciiRules::Yes : Unicode::IDNA::UseStd3AsciiRules::No,
|
||||
options->transitional_processing ? Unicode::IDNA::TransitionalProcessing::Yes : Unicode::IDNA::TransitionalProcessing::No,
|
||||
options->verify_dns_length ? Unicode::IDNA::VerifyDnsLength::Yes : Unicode::IDNA::VerifyDnsLength::No,
|
||||
options->ignore_invalid_punycode ? Unicode::IDNA::IgnoreInvalidPunycode::Yes : Unicode::IDNA::IgnoreInvalidPunycode::No,
|
||||
};
|
||||
|
||||
auto result = Unicode::IDNA::to_ascii(Utf8View { StringView { domain, domain_length } }, cpp_options);
|
||||
if (result.is_error())
|
||||
return;
|
||||
|
||||
auto const ascii_bytes = result.value().bytes_as_string_view();
|
||||
on_success(ctx, reinterpret_cast<uint8_t const*>(ascii_bytes.characters_without_null_termination()), ascii_bytes.length());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
64
Libraries/LibUnicode/Rust/src/idna.rs
Normal file
64
Libraries/LibUnicode/Rust/src/idna.rs
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright (c) 2026, Shannon Booth <shannon@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
//! Rust bindings for the LibUnicode IDNA API.
|
||||
|
||||
use std::ffi::c_void;
|
||||
|
||||
/// Options controlling IDNA `to_ascii` processing (C++ `UnicodeRustToAsciiOptions`).
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub struct ToAsciiOptions {
|
||||
pub check_hyphens: bool,
|
||||
pub check_bidi: bool,
|
||||
pub check_joiners: bool,
|
||||
pub use_std3_ascii_rules: bool,
|
||||
pub transitional_processing: bool,
|
||||
pub verify_dns_length: bool,
|
||||
pub ignore_invalid_punycode: bool,
|
||||
}
|
||||
|
||||
type FfiIdnaResultFn = unsafe extern "C" fn(*mut c_void, *const u8, usize);
|
||||
|
||||
unsafe extern "C" {
|
||||
fn unicode_rust_idna_to_ascii(
|
||||
domain: *const u8,
|
||||
domain_length: usize,
|
||||
options: *const ToAsciiOptions,
|
||||
ctx: *mut c_void,
|
||||
on_success: FfiIdnaResultFn,
|
||||
);
|
||||
}
|
||||
|
||||
/// Called by C++ on success with the ASCII result bytes.
|
||||
///
|
||||
/// # Safety
|
||||
/// `ctx` must be a valid `*mut Option<String>` live for the duration of the
|
||||
/// enclosing `idna_to_ascii` call. `data` must point to `len` valid ASCII bytes.
|
||||
unsafe extern "C" fn on_idna_result(ctx: *mut c_void, data: *const u8, len: usize) {
|
||||
// SAFETY: `ctx` was set to `addr_of_mut!(result)` in `idna_to_ascii`.
|
||||
let out = unsafe { &mut *(ctx as *mut Option<String>) };
|
||||
// SAFETY: `data` is valid for `len` bytes; IDNA to_ascii guarantees ASCII output.
|
||||
let bytes = unsafe { std::slice::from_raw_parts(data, len) };
|
||||
debug_assert!(bytes.is_ascii(), "unicode_rust_idna_to_ascii must return ASCII");
|
||||
// SAFETY: ASCII is a strict subset of UTF-8.
|
||||
*out = Some(unsafe { String::from_utf8_unchecked(bytes.to_vec()) });
|
||||
}
|
||||
|
||||
pub fn idna_to_ascii(domain: &str, options: ToAsciiOptions) -> Option<String> {
|
||||
let mut result: Option<String> = None;
|
||||
// SAFETY: `domain` is valid for its length; `options` and `result` are valid for the call.
|
||||
unsafe {
|
||||
unicode_rust_idna_to_ascii(
|
||||
domain.as_ptr(),
|
||||
domain.len(),
|
||||
&raw const options,
|
||||
std::ptr::addr_of_mut!(result) as *mut c_void,
|
||||
on_idna_result,
|
||||
);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
|
@ -10,3 +10,4 @@ mod rust_allocator;
|
|||
|
||||
pub mod calendar;
|
||||
pub mod character_types;
|
||||
pub mod idna;
|
||||
|
|
|
|||
Loading…
Reference in a new issue