From ddea42d7158610a54ca709a8185acd364de84dfa Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Fri, 25 Oct 2024 13:26:58 +0200 Subject: [PATCH] feat(wasi): add `wasi` crate support Signed-off-by: Roman Volosatovs --- .github/workflows/ci.yml | 3 +++ Cargo.toml | 1 + src/header/map.rs | 35 ++++++++++++++++++++++++++ src/method.rs | 53 ++++++++++++++++++++++++++++++++++++++++ src/uri/scheme.rs | 42 +++++++++++++++++++++++++++++++ 5 files changed, 134 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 365a11e2..0ab3a60f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -108,6 +108,9 @@ jobs: - name: Check run: cargo check --target wasm32-unknown-unknown + - name: Test WASI + run: cargo test --features wasi + minimal-versions: runs-on: ubuntu-latest steps: diff --git a/Cargo.toml b/Cargo.toml index 487507f0..c7483b45 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,7 @@ std = [] bytes = "1" fnv = "1.0.5" itoa = "1" +wasi = { version = "0.13", optional = true } [dev-dependencies] quickcheck = "1" diff --git a/src/header/map.rs b/src/header/map.rs index a668b311..8c5a37b2 100644 --- a/src/header/map.rs +++ b/src/header/map.rs @@ -3872,6 +3872,41 @@ mod as_header_name { impl<'a> AsHeaderName for &'a String {} } +#[cfg(feature = "wasi")] +impl TryFrom for HeaderMap { + type Error = Error; + + fn try_from(fields: wasi::http::types::Fields) -> Result { + let mut headers = HeaderMap::new(); + for (name, value) in fields.entries() { + let name = HeaderName::try_from(name)?; + let value = HeaderValue::try_from(value)?; + match headers.entry(name) { + Entry::Vacant(entry) => { + entry.insert(value); + } + Entry::Occupied(mut entry) => { + entry.append(value); + } + }; + } + Ok(headers) + } +} + +#[cfg(feature = "wasi")] +impl TryFrom for wasi::http::types::Fields { + type Error = wasi::http::types::HeaderError; + + fn try_from(headers: HeaderMap) -> Result { + let fields = wasi::http::types::Fields::new(); + for (name, value) in &headers { + fields.append(&name.to_string(), &value.as_bytes().to_vec())?; + } + Ok(fields) + } +} + #[test] fn test_bounds() { fn check_bounds() {} diff --git a/src/method.rs b/src/method.rs index 3d45ef9f..452a3107 100644 --- a/src/method.rs +++ b/src/method.rs @@ -419,6 +419,45 @@ mod extension { } } +#[cfg(feature = "wasi")] +impl From for wasi::http::types::Method { + fn from(method: Method) -> Self { + match method { + Method(Get) => Self::Get, + Method(Head) => Self::Head, + Method(Post) => Self::Post, + Method(Put) => Self::Put, + Method(Delete) => Self::Delete, + Method(Connect) => Self::Connect, + Method(Options) => Self::Options, + Method(Trace) => Self::Trace, + Method(Patch) => Self::Patch, + Method(ExtensionInline(inline)) => Self::Other(inline.as_str().into()), + Method(ExtensionAllocated(allocated)) => Self::Other(allocated.as_str().into()), + } + } +} + +#[cfg(feature = "wasi")] +impl TryFrom for Method { + type Error = InvalidMethod; + + fn try_from(method: wasi::http::types::Method) -> Result { + match method { + wasi::http::types::Method::Get => Ok(Self::GET), + wasi::http::types::Method::Head => Ok(Self::HEAD), + wasi::http::types::Method::Post => Ok(Self::POST), + wasi::http::types::Method::Put => Ok(Self::PUT), + wasi::http::types::Method::Delete => Ok(Self::DELETE), + wasi::http::types::Method::Connect => Ok(Self::CONNECT), + wasi::http::types::Method::Options => Ok(Self::OPTIONS), + wasi::http::types::Method::Trace => Ok(Self::TRACE), + wasi::http::types::Method::Patch => Ok(Self::PATCH), + wasi::http::types::Method::Other(method) => method.parse(), + } + } +} + #[cfg(test)] mod test { use super::*; @@ -482,4 +521,18 @@ mod test { ); } } + + #[cfg(feature = "wasi")] + #[test] + fn test_method_wasi_conv() { + use std::convert::TryInto; + + let m: Method = wasi::http::types::Method::Get + .try_into() + .expect("failed to convert WASI method"); + assert_eq!(m, Method::GET); + + let m: wasi::http::types::Method = Method::GET.into(); + assert!(matches!(m, wasi::http::types::Method::Get)); + } } diff --git a/src/uri/scheme.rs b/src/uri/scheme.rs index dbcc8c3f..86d324d4 100644 --- a/src/uri/scheme.rs +++ b/src/uri/scheme.rs @@ -335,6 +335,34 @@ impl From for Scheme { } } +#[cfg(feature = "wasi")] +impl From for wasi::http::types::Scheme { + fn from(scheme: Scheme) -> Self { + use self::Protocol::*; + use self::Scheme2::*; + + match scheme.inner { + Standard(Http) => Self::Http, + Standard(Https) => Self::Https, + Other(v) => Self::Other(v.to_string()), + None => unreachable!(), + } + } +} + +#[cfg(feature = "wasi")] +impl TryFrom for Scheme { + type Error = InvalidUri; + + fn try_from(scheme: wasi::http::types::Scheme) -> Result { + match scheme { + wasi::http::types::Scheme::Http => Ok(Self::HTTP), + wasi::http::types::Scheme::Https => Ok(Self::HTTPS), + wasi::http::types::Scheme::Other(scheme) => scheme.parse(), + } + } +} + #[cfg(test)] mod test { use super::*; @@ -358,4 +386,18 @@ mod test { fn scheme(s: &str) -> Scheme { s.parse().expect(&format!("Invalid scheme: {}", s)) } + + #[cfg(feature = "wasi")] + #[test] + fn wasi_conv() { + use std::convert::TryInto; + + let s: Scheme = wasi::http::types::Scheme::Http + .try_into() + .expect("failed to convert WASI scheme"); + assert_eq!(s, Scheme::HTTP); + + let s: wasi::http::types::Scheme = Scheme::HTTP.into(); + assert!(matches!(s, wasi::http::types::Scheme::Http)); + } }