Add wasix_http_client Rust client library

Adds a new crate "wasix_http_client", which is a wrapper around the WAI
generated sys bindings to the wasix_http_client_v1.wai component.

Enables easy http client usage from Rust code.
This commit is contained in:
Christoph Herzog
2022-12-11 06:02:30 +01:00
parent 680ee18c20
commit 7a4358de7a
6 changed files with 751 additions and 13 deletions

77
Cargo.lock generated
View File

@@ -3913,9 +3913,9 @@ checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
[[package]]
name = "wai-bindgen-gen-core"
version = "0.2.1"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd16a4dc5fb71faaa3d21530a1d004b7fc88f0bf70cda6f5c4099220254d947b"
checksum = "c43a21791697e899140bb82c27db1fac26ad28ef60eddca55f05e5ca365a6e4c"
dependencies = [
"anyhow",
"wai-parser",
@@ -3923,9 +3923,9 @@ dependencies = [
[[package]]
name = "wai-bindgen-gen-rust"
version = "0.2.1"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17625f823712dd6eff5656b4308842b7aeac9eca429f6eea36443d4305cadf7d"
checksum = "dd86fcd3046f63882137f46723c643b7cc33ea4c739c0f9999c6bd76f4ae21f2"
dependencies = [
"heck 0.3.3",
"wai-bindgen-gen-core",
@@ -3933,9 +3933,20 @@ dependencies = [
[[package]]
name = "wai-bindgen-gen-rust-wasm"
version = "0.2.1"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "757fbff15d11d0578ee879cea209b111bd09f8b39346c7b212a7e7f52585d2f7"
checksum = "ab8850eeef5405252a368086f0b3e0eefef4ed47666f37ff9a321ecdcfc96376"
dependencies = [
"heck 0.3.3",
"wai-bindgen-gen-core",
"wai-bindgen-gen-rust",
]
[[package]]
name = "wai-bindgen-gen-wasmer"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34f3b3d04d56cc09616cf8daab90b5601e0e894509f34c81a3829aab03f2b457"
dependencies = [
"heck 0.3.3",
"wai-bindgen-gen-core",
@@ -3944,19 +3955,20 @@ dependencies = [
[[package]]
name = "wai-bindgen-rust"
version = "0.2.1"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90e6c91f681f714599326252f4ae8b9897293110055d25c576fa09ffb01661cd"
checksum = "de0eef889bfee6c2d1f21304b5e8c5786629827ae4f26c44e24d0e0ded80f6cd"
dependencies = [
"async-trait",
"bitflags",
"wai-bindgen-rust-impl",
]
[[package]]
name = "wai-bindgen-rust-impl"
version = "0.2.1"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb36c407339837a5e1fcd9b41641fbe36488fc53ab0664add14d5f7105f6fbbb"
checksum = "6d3c00bda01d3cf02e9a0a6c7e6b738a6da5cbdefa92f979486f7968b74f290d"
dependencies = [
"proc-macro2",
"syn",
@@ -3965,10 +3977,37 @@ dependencies = [
]
[[package]]
name = "wai-parser"
version = "0.2.1"
name = "wai-bindgen-wasmer"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a24cfad91494925ba41c6a03bd7d092eb3c3cdbccf4ef680b06831030bd7a69"
checksum = "b69eca47105340af3e4dc5ba47951a23534d7139a62b12550cc6702a5cb6ab70"
dependencies = [
"anyhow",
"bitflags",
"once_cell",
"thiserror",
"tracing",
"wai-bindgen-wasmer-impl",
"wasmer",
]
[[package]]
name = "wai-bindgen-wasmer-impl"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "faaf3a72820d1708185cdd26eafe034e3f88d740bae27b5d852379d242d9ff68"
dependencies = [
"proc-macro2",
"syn",
"wai-bindgen-gen-core",
"wai-bindgen-gen-wasmer",
]
[[package]]
name = "wai-parser"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2811f0b0c8883f92b9c616b6c38a1b462dc37d04cfc700be60e4990497ed1f7d"
dependencies = [
"anyhow",
"id-arena",
@@ -4048,6 +4087,15 @@ dependencies = [
"wast 24.0.0",
]
[[package]]
name = "wasix_http_client"
version = "0.1.0"
dependencies = [
"anyhow",
"http",
"wai-bindgen-rust",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.83"
@@ -4664,6 +4712,7 @@ dependencies = [
"generational-arena",
"getrandom",
"hex",
"http",
"lazy_static",
"libc",
"linked_hash_set",
@@ -4734,6 +4783,7 @@ dependencies = [
name = "wasmer-wasi-types"
version = "3.0.2"
dependencies = [
"anyhow",
"bitflags",
"byteorder",
"cfg-if 1.0.0",
@@ -4745,6 +4795,7 @@ dependencies = [
"wai-bindgen-gen-rust",
"wai-bindgen-gen-rust-wasm",
"wai-bindgen-rust",
"wai-bindgen-wasmer",
"wai-parser",
"wasmer",
"wasmer-derive",

View File

@@ -47,6 +47,7 @@ members = [
"lib/wasi-types",
"lib/wasi-experimental-io-devices",
"lib/wasi-local-networking",
"lib/wasix/wasix-http-client",
"lib/c-api/tests/wasmer-c-api-test-runner",
"lib/c-api/examples/wasmer-capi-examples-runner",
"lib/types",

View File

@@ -0,0 +1,12 @@
[package]
name = "wasix_http_client"
description = "Wasix bindings library for Webassembly."
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.66"
http = "0.2.8"
wai-bindgen-rust = "0.2.2"

View File

@@ -0,0 +1,20 @@
use wasix_http_client::{Body, HttpClient, RequestBuilder};
fn main() {
let c = HttpClient::new().unwrap();
let r = RequestBuilder::new()
.uri("http://ferris2.christoph.app.wapm.dev/http-client-test")
.body(Body::empty())
.unwrap();
eprintln!("fetching: {r:?}");
let res = c.send(r).unwrap();
dbg!(&res);
assert!(res.status().is_success());
let body = res.into_body().read_all().unwrap();
let s = String::from_utf8(body).unwrap();
eprintln!("Response body: {s}");
assert!(s.contains("http-client-test"));
}

View File

@@ -0,0 +1,140 @@
// Auto-generated sys bindings.
#[allow(dead_code)]
mod wasix_http_client_v1;
use anyhow::bail;
use crate::wasix_http_client_v1 as sys;
pub use http::{header, Method, StatusCode};
#[derive(Clone)]
pub struct HttpClient {
client: sys::Client,
}
// TODO: use proper custom error type
impl HttpClient {
pub fn new() -> Result<Self, anyhow::Error> {
let client = sys::Client::new().map_err(anyhow::Error::msg)?;
Ok(Self { client })
}
pub fn send(&self, request: Request) -> Result<Response, anyhow::Error> {
let (parts, body) = request.into_parts();
let uri = parts.uri.to_string();
let method = match &parts.method {
m if m == Method::GET => sys::Method::Get,
m if m == Method::HEAD => sys::Method::Head,
m if m == Method::POST => sys::Method::Post,
m if m == Method::PUT => sys::Method::Put,
m if m == Method::DELETE => sys::Method::Delete,
m if m == Method::CONNECT => sys::Method::Connect,
m if m == Method::OPTIONS => sys::Method::Options,
m if m == Method::TRACE => sys::Method::Trace,
m if m == Method::PATCH => sys::Method::Patch,
m => sys::Method::Other(m.as_str()),
};
let headers: Vec<_> = parts
.headers
.iter()
.map(|(key, val)| sys::HeaderParam {
key: key.as_str(),
value: val.as_bytes(),
})
.collect();
let body = match &body.0 {
BodyInner::Data(d) => Some(sys::BodyParam::Data(&d)),
};
let req = sys::Request {
url: &uri,
method,
headers: &headers,
body,
timeout: None,
redirect_policy: None,
};
let res = self.client.send(req).map_err(anyhow::Error::msg)?;
let body = match res.body {
sys::BodyResult::Data(d) => Body::new_data(d),
sys::BodyResult::Fd(_) => {
bail!("Received file descriptor response body, which is not suppported yet");
}
};
let mut builder = http::Response::builder().status(res.status);
for param in res.headers {
builder = builder.header(param.key, param.value);
}
builder.body(body).map_err(anyhow::Error::from)
}
}
// TODO: use custom request which can also hold timeout config, etc.
// #[derive(Clone, Debug)]
// pub struct Request {
// pub method: Method,
// pub headers: http::HeaderMap,
// }
pub type Request = http::Request<Body>;
pub type Response = http::Response<Body>;
#[derive(Debug)]
pub struct Body(BodyInner);
impl Body {
pub fn empty() -> Self {
Self::new_data(Vec::<u8>::new())
}
pub fn new_data<I>(data: I) -> Self
where
I: Into<Vec<u8>>,
{
Self(BodyInner::Data(data.into()))
}
pub fn read_all(self) -> Result<Vec<u8>, anyhow::Error> {
match self.0 {
BodyInner::Data(d) => Ok(d),
}
}
}
#[derive(Debug)]
pub enum BodyInner {
Data(Vec<u8>),
}
impl From<Vec<u8>> for Body {
fn from(value: Vec<u8>) -> Self {
Self(BodyInner::Data(value))
}
}
impl<'a> From<&'a [u8]> for Body {
fn from(value: &'a [u8]) -> Self {
Self(BodyInner::Data(value.to_vec()))
}
}
impl From<String> for Body {
fn from(value: String) -> Self {
Self(BodyInner::Data(value.into_bytes()))
}
}
impl<'a> From<&'a str> for Body {
fn from(value: &'a str) -> Self {
Self(BodyInner::Data(value.as_bytes().to_vec()))
}
}
pub type RequestBuilder = http::request::Builder;

View File

@@ -0,0 +1,514 @@
#[derive(Clone)]
pub enum Method<'a> {
Get,
Head,
Post,
Put,
Delete,
Connect,
Options,
Trace,
Patch,
Other(&'a str),
}
impl<'a> core::fmt::Debug for Method<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Method::Get => f.debug_tuple("Method::Get").finish(),
Method::Head => f.debug_tuple("Method::Head").finish(),
Method::Post => f.debug_tuple("Method::Post").finish(),
Method::Put => f.debug_tuple("Method::Put").finish(),
Method::Delete => f.debug_tuple("Method::Delete").finish(),
Method::Connect => f.debug_tuple("Method::Connect").finish(),
Method::Options => f.debug_tuple("Method::Options").finish(),
Method::Trace => f.debug_tuple("Method::Trace").finish(),
Method::Patch => f.debug_tuple("Method::Patch").finish(),
Method::Other(e) => f.debug_tuple("Method::Other").field(e).finish(),
}
}
}
#[derive(Clone)]
pub struct HeaderParam<'a> {
pub key: &'a str,
pub value: &'a [u8],
}
impl<'a> core::fmt::Debug for HeaderParam<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("HeaderParam")
.field("key", &self.key)
.field("value", &self.value)
.finish()
}
}
#[derive(Clone)]
pub struct HeaderResult {
pub key: String,
pub value: Vec<u8>,
}
impl core::fmt::Debug for HeaderResult {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("HeaderResult")
.field("key", &self.key)
.field("value", &self.value)
.finish()
}
}
pub type HeaderListParam<'a> = &'a [HeaderParam<'a>];
pub type HeaderListResult = Vec<HeaderResult>;
pub type Fd = u32;
pub type TimeoutMs = u32;
#[repr(C)]
#[derive(Copy, Clone)]
pub struct RedirectFollow {
pub max: u32,
}
impl core::fmt::Debug for RedirectFollow {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("RedirectFollow")
.field("max", &self.max)
.finish()
}
}
#[derive(Clone, Copy)]
pub enum RedirectPolicy {
NoFollow,
Follow(RedirectFollow),
}
impl core::fmt::Debug for RedirectPolicy {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
RedirectPolicy::NoFollow => f.debug_tuple("RedirectPolicy::NoFollow").finish(),
RedirectPolicy::Follow(e) => f.debug_tuple("RedirectPolicy::Follow").field(e).finish(),
}
}
}
#[derive(Clone)]
pub enum BodyParam<'a> {
Data(&'a [u8]),
Fd(Fd),
}
impl<'a> core::fmt::Debug for BodyParam<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
BodyParam::Data(e) => f.debug_tuple("BodyParam::Data").field(e).finish(),
BodyParam::Fd(e) => f.debug_tuple("BodyParam::Fd").field(e).finish(),
}
}
}
#[derive(Clone)]
pub enum BodyResult {
Data(Vec<u8>),
Fd(Fd),
}
impl core::fmt::Debug for BodyResult {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
BodyResult::Data(e) => f.debug_tuple("BodyResult::Data").field(e).finish(),
BodyResult::Fd(e) => f.debug_tuple("BodyResult::Fd").field(e).finish(),
}
}
}
#[derive(Clone)]
pub struct Request<'a> {
pub url: &'a str,
pub method: Method<'a>,
pub headers: HeaderListParam<'a>,
pub body: Option<BodyParam<'a>>,
pub timeout: Option<TimeoutMs>,
pub redirect_policy: Option<RedirectPolicy>,
}
impl<'a> core::fmt::Debug for Request<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Request")
.field("url", &self.url)
.field("method", &self.method)
.field("headers", &self.headers)
.field("body", &self.body)
.field("timeout", &self.timeout)
.field("redirect-policy", &self.redirect_policy)
.finish()
}
}
#[derive(Clone)]
pub struct Response {
pub status: u16,
pub headers: HeaderListResult,
pub body: BodyResult,
pub redirect_urls: Option<Vec<String>>,
}
impl core::fmt::Debug for Response {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Response")
.field("status", &self.status)
.field("headers", &self.headers)
.field("body", &self.body)
.field("redirect-urls", &self.redirect_urls)
.finish()
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct Client(i32);
impl Client {
pub unsafe fn from_raw(raw: i32) -> Self {
Self(raw)
}
pub fn into_raw(self) -> i32 {
let ret = self.0;
core::mem::forget(self);
return ret;
}
pub fn as_raw(&self) -> i32 {
self.0
}
}
impl Drop for Client {
fn drop(&mut self) {
#[link(wasm_import_module = "canonical_abi")]
extern "C" {
#[link_name = "resource_drop_client"]
fn close(fd: i32);
}
unsafe {
close(self.0);
}
}
}
impl Clone for Client {
fn clone(&self) -> Self {
#[link(wasm_import_module = "canonical_abi")]
extern "C" {
#[link_name = "resource_clone_client"]
fn clone(val: i32) -> i32;
}
unsafe { Self(clone(self.0)) }
}
}
impl Client {
pub fn new() -> Result<Client, String> {
unsafe {
let ptr0 = WASIX_HTTP_CLIENT_V1_RET_AREA.0.as_mut_ptr() as i32;
#[link(wasm_import_module = "wasix_http_client_v1")]
extern "C" {
#[cfg_attr(target_arch = "wasm32", link_name = "client::new")]
#[cfg_attr(
not(target_arch = "wasm32"),
link_name = "wasix_http_client_v1_client::new"
)]
fn wai_import(_: i32);
}
wai_import(ptr0);
match i32::from(*((ptr0 + 0) as *const u8)) {
0 => Ok(Client(*((ptr0 + 4) as *const i32))),
1 => Err({
let len1 = *((ptr0 + 8) as *const i32) as usize;
String::from_utf8(Vec::from_raw_parts(
*((ptr0 + 4) as *const i32) as *mut _,
len1,
len1,
))
.unwrap()
}),
_ => panic!("invalid enum discriminant"),
}
}
}
}
impl Client {
pub fn send(&self, request: Request<'_>) -> Result<Response, String> {
unsafe {
let ptr0 = WASIX_HTTP_CLIENT_V1_RET_AREA.0.as_mut_ptr() as i32;
*((ptr0 + 0) as *mut i32) = self.0;
let Request {
url: url1,
method: method1,
headers: headers1,
body: body1,
timeout: timeout1,
redirect_policy: redirect_policy1,
} = request;
let vec2 = url1;
let ptr2 = vec2.as_ptr() as i32;
let len2 = vec2.len() as i32;
*((ptr0 + 8) as *mut i32) = len2;
*((ptr0 + 4) as *mut i32) = ptr2;
match method1 {
Method::Get => {
let e = ();
{
*((ptr0 + 12) as *mut u8) = (0i32) as u8;
let () = e;
}
}
Method::Head => {
let e = ();
{
*((ptr0 + 12) as *mut u8) = (1i32) as u8;
let () = e;
}
}
Method::Post => {
let e = ();
{
*((ptr0 + 12) as *mut u8) = (2i32) as u8;
let () = e;
}
}
Method::Put => {
let e = ();
{
*((ptr0 + 12) as *mut u8) = (3i32) as u8;
let () = e;
}
}
Method::Delete => {
let e = ();
{
*((ptr0 + 12) as *mut u8) = (4i32) as u8;
let () = e;
}
}
Method::Connect => {
let e = ();
{
*((ptr0 + 12) as *mut u8) = (5i32) as u8;
let () = e;
}
}
Method::Options => {
let e = ();
{
*((ptr0 + 12) as *mut u8) = (6i32) as u8;
let () = e;
}
}
Method::Trace => {
let e = ();
{
*((ptr0 + 12) as *mut u8) = (7i32) as u8;
let () = e;
}
}
Method::Patch => {
let e = ();
{
*((ptr0 + 12) as *mut u8) = (8i32) as u8;
let () = e;
}
}
Method::Other(e) => {
*((ptr0 + 12) as *mut u8) = (9i32) as u8;
let vec3 = e;
let ptr3 = vec3.as_ptr() as i32;
let len3 = vec3.len() as i32;
*((ptr0 + 20) as *mut i32) = len3;
*((ptr0 + 16) as *mut i32) = ptr3;
}
};
let vec7 = headers1;
let len7 = vec7.len() as i32;
let layout7 = core::alloc::Layout::from_size_align_unchecked(vec7.len() * 16, 4);
let result7 = std::alloc::alloc(layout7);
if result7.is_null() {
std::alloc::handle_alloc_error(layout7);
}
for (i, e) in vec7.into_iter().enumerate() {
let base = result7 as i32 + (i as i32) * 16;
{
let HeaderParam {
key: key4,
value: value4,
} = e;
let vec5 = key4;
let ptr5 = vec5.as_ptr() as i32;
let len5 = vec5.len() as i32;
*((base + 4) as *mut i32) = len5;
*((base + 0) as *mut i32) = ptr5;
let vec6 = value4;
let ptr6 = vec6.as_ptr() as i32;
let len6 = vec6.len() as i32;
*((base + 12) as *mut i32) = len6;
*((base + 8) as *mut i32) = ptr6;
}
}
*((ptr0 + 28) as *mut i32) = len7;
*((ptr0 + 24) as *mut i32) = result7 as i32;
match body1 {
Some(e) => {
*((ptr0 + 32) as *mut u8) = (1i32) as u8;
match e {
BodyParam::Data(e) => {
*((ptr0 + 36) as *mut u8) = (0i32) as u8;
let vec8 = e;
let ptr8 = vec8.as_ptr() as i32;
let len8 = vec8.len() as i32;
*((ptr0 + 44) as *mut i32) = len8;
*((ptr0 + 40) as *mut i32) = ptr8;
}
BodyParam::Fd(e) => {
*((ptr0 + 36) as *mut u8) = (1i32) as u8;
*((ptr0 + 40) as *mut i32) = wai_bindgen_rust::rt::as_i32(e);
}
};
}
None => {
let e = ();
{
*((ptr0 + 32) as *mut u8) = (0i32) as u8;
let () = e;
}
}
};
match timeout1 {
Some(e) => {
*((ptr0 + 48) as *mut u8) = (1i32) as u8;
*((ptr0 + 52) as *mut i32) = wai_bindgen_rust::rt::as_i32(e);
}
None => {
let e = ();
{
*((ptr0 + 48) as *mut u8) = (0i32) as u8;
let () = e;
}
}
};
match redirect_policy1 {
Some(e) => {
*((ptr0 + 56) as *mut u8) = (1i32) as u8;
match e {
RedirectPolicy::NoFollow => {
let e = ();
{
*((ptr0 + 60) as *mut u8) = (0i32) as u8;
let () = e;
}
}
RedirectPolicy::Follow(e) => {
*((ptr0 + 60) as *mut u8) = (1i32) as u8;
let RedirectFollow { max: max9 } = e;
*((ptr0 + 64) as *mut i32) = wai_bindgen_rust::rt::as_i32(max9);
}
};
}
None => {
let e = ();
{
*((ptr0 + 56) as *mut u8) = (0i32) as u8;
let () = e;
}
}
};
let ptr10 = WASIX_HTTP_CLIENT_V1_RET_AREA.0.as_mut_ptr() as i32;
#[link(wasm_import_module = "wasix_http_client_v1")]
extern "C" {
#[cfg_attr(target_arch = "wasm32", link_name = "client::send")]
#[cfg_attr(
not(target_arch = "wasm32"),
link_name = "wasix_http_client_v1_client::send"
)]
fn wai_import(_: i32, _: i32);
}
wai_import(ptr0, ptr10);
std::alloc::dealloc(result7, layout7);
match i32::from(*((ptr10 + 0) as *const u8)) {
0 => Ok({
let base13 = *((ptr10 + 8) as *const i32);
let len13 = *((ptr10 + 12) as *const i32);
let mut result13 = Vec::with_capacity(len13 as usize);
for i in 0..len13 {
let base = base13 + i * 16;
result13.push({
let len11 = *((base + 4) as *const i32) as usize;
let len12 = *((base + 12) as *const i32) as usize;
HeaderResult {
key: String::from_utf8(Vec::from_raw_parts(
*((base + 0) as *const i32) as *mut _,
len11,
len11,
))
.unwrap(),
value: Vec::from_raw_parts(
*((base + 8) as *const i32) as *mut _,
len12,
len12,
),
}
});
}
std::alloc::dealloc(
base13 as *mut _,
std::alloc::Layout::from_size_align_unchecked((len13 as usize) * 16, 4),
);
Response {
status: i32::from(*((ptr10 + 4) as *const u16)) as u16,
headers: result13,
body: match i32::from(*((ptr10 + 16) as *const u8)) {
0 => BodyResult::Data({
let len14 = *((ptr10 + 24) as *const i32) as usize;
Vec::from_raw_parts(
*((ptr10 + 20) as *const i32) as *mut _,
len14,
len14,
)
}),
1 => BodyResult::Fd(*((ptr10 + 20) as *const i32) as u32),
_ => panic!("invalid enum discriminant"),
},
redirect_urls: match i32::from(*((ptr10 + 28) as *const u8)) {
0 => None,
1 => Some({
let base16 = *((ptr10 + 32) as *const i32);
let len16 = *((ptr10 + 36) as *const i32);
let mut result16 = Vec::with_capacity(len16 as usize);
for i in 0..len16 {
let base = base16 + i * 8;
result16.push({
let len15 = *((base + 4) as *const i32) as usize;
String::from_utf8(Vec::from_raw_parts(
*((base + 0) as *const i32) as *mut _,
len15,
len15,
))
.unwrap()
});
}
std::alloc::dealloc(
base16 as *mut _,
std::alloc::Layout::from_size_align_unchecked(
(len16 as usize) * 8,
4,
),
);
result16
}),
_ => panic!("invalid enum discriminant"),
},
}
}),
1 => Err({
let len17 = *((ptr10 + 8) as *const i32) as usize;
String::from_utf8(Vec::from_raw_parts(
*((ptr10 + 4) as *const i32) as *mut _,
len17,
len17,
))
.unwrap()
}),
_ => panic!("invalid enum discriminant"),
}
}
}
}
#[repr(align(4))]
struct RetArea([u8; 68]);
static mut WASIX_HTTP_CLIENT_V1_RET_AREA: RetArea = RetArea([0; 68]);