Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions ohkami/src/x_worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,36 @@

pub use ::ohkami_macros::{worker, bindings, DurableObject};

pub trait FromEnv: Sized {
fn from_env(env: &worker::Env) -> Result<Self, worker::Error>;

#[doc(hidden)]
fn bindings_meta() -> &'static [(&'static str, &'static str)] {
&[]
}
#[doc(hidden)]
fn dummy_env() -> worker::Env {
use worker::wasm_bindgen::{JsCast, closure::Closure};
use worker::js_sys::{Object, Reflect, Function};

let env = Object::new();
for (binding_name, binding_type) in Self::bindings_meta() {
let binding = Object::new();
if !binding_type.starts_with('$') {
let constructor = Function::unchecked_from_js(Closure::<dyn Fn()>::new(|| {}).into_js_value());
{
let attributes = Object::new();
Reflect::set(&attributes, &"value".into(), &(*binding_type).into()).unwrap();
Reflect::define_property(&constructor, &"name".into(), &attributes).unwrap();
}
Reflect::set(&binding, &"constructor".into(), &constructor).unwrap();
}
Reflect::set(&env, &(*binding_name).into(), &binding).unwrap();
}
worker::Env::unchecked_from_js(env.unchecked_into())
}
}

pub mod bindings {
/// `Var` binding can also be accessed via associated const
/// of the same name.
Expand Down
55 changes: 52 additions & 3 deletions ohkami_macros/src/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,32 @@ use crate::util;

use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{spanned::Spanned, Error, Ident, ItemFn, ItemStruct, Fields, LitStr};
use syn::{spanned::Spanned, Error, Ident, ItemFn, FnArg, ItemStruct, Fields, LitStr};


pub fn worker(args: TokenStream, ohkami_fn: TokenStream) -> Result<TokenStream, syn::Error> {
let worker_meta: meta::WorkerMeta = syn::parse2(args)?;

let ohkami_fn: ItemFn = syn::parse2(ohkami_fn)?;
if ohkami_fn.sig.inputs.len() >= 2 {
return Err(syn::Error::new(
ohkami_fn.span(),
"`#[worker]` doesn't support multiple arguments of the fn, \
accepting 0 args or single arg which impls `FromEnv`."
))
}

let gen_ohkami = {
let name = &ohkami_fn.sig.ident;
let name = &ohkami_fn.sig.ident;
let env = ohkami_fn.sig.inputs.first().map(|_| quote! {
::ohkami::FromEnv::from_env(&env).expect("`#[worker]` bindings arg has wrong `FromEnv` impl")
});
let awaiting = ohkami_fn.sig.asyncness.is_some().then_some(quote! {
.await
});

quote! {
#name()#awaiting
#name(#env)#awaiting
}
};

Expand Down Expand Up @@ -51,6 +62,16 @@ pub fn worker(args: TokenStream, ohkami_fn: TokenStream) -> Result<TokenStream,
def
});

let dummy_env_def = ohkami_fn.sig.inputs.first().map(|a| {
let ty = match a {
FnArg::Receiver(r) => &r.ty,
FnArg::Typed(p) => &p.ty,
};
quote! {
let env = <#ty as ::ohkami::FromEnv>::dummy_env();
}
});

quote! {
const _: () = {
// `#[wasm_bindgen]` direcly references this modules in epxpaned code
Expand All @@ -59,6 +80,7 @@ pub fn worker(args: TokenStream, ohkami_fn: TokenStream) -> Result<TokenStream,
#[doc(hidden)]
#[::worker::wasm_bindgen::prelude::wasm_bindgen(js_name = "OpenAPIDocumentBytes")]
pub async fn __openapi_document_bytes__() -> Vec<u8> {
#dummy_env_def
let ohkami: ::ohkami::Ohkami = #gen_ohkami;
ohkami.__openapi_document_bytes__(::ohkami::openapi::OpenAPI {
title: #title,
Expand Down Expand Up @@ -272,6 +294,32 @@ pub fn bindings(env_name: TokenStream, bindings_struct: TokenStream) -> Result<T
}
};

let impl_from_env = {
let bindings_meta = bindings.iter()
.filter(|(name, _)| named_fields.as_ref().is_none_or(
|n| n.iter().any(|(field_name, _)| *field_name == name)
))
.map(|(name, binding)| {
let binding_name = LitStr::new(&name.to_string(), name.span());
let binding_type = LitStr::new(binding.binding_type(), Span::call_site());
quote! {
(#binding_name, #binding_type)
}
});

quote! {
impl ::ohkami::FromEnv for #name {
fn from_env(env: &worker::Env) -> Result<Self, worker::Error> {
Self::new(env)
}

fn bindings_meta() -> &'static [(&'static str, &'static str)] {
&[#(#bindings_meta),*]
}
}
}
};

let impl_send_sync = if
bindings.is_empty() || named_fields.is_some_and(|n| n.is_empty())
{
Expand All @@ -288,6 +336,7 @@ pub fn bindings(env_name: TokenStream, bindings_struct: TokenStream) -> Result<T
#const_vars
#impl_new
#impl_from_request
#impl_from_env
#impl_send_sync
})
}
Expand Down
13 changes: 13 additions & 0 deletions ohkami_macros/src/worker/binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,19 @@ pub enum Binding {
}

impl Binding {
pub fn binding_type(&self) -> &'static str {
match self {
Self::Variable(_) => "String",
Self::AI => "Ai",
Self::D1 => "D1Database",
Self::KV => "$KV",
Self::R2 => "R2Bucket",
Self::Service => "Fetcher",
Self::Queue => "WorkerQueue",
Self::DurableObject => "DurableObjectNamespace",
}
}

pub fn tokens_ty(&self) -> TokenStream {
match self {
Self::Variable(_) => quote!(&'static str),
Expand Down
10 changes: 8 additions & 2 deletions samples/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,19 @@ cd $SAMPLES/streaming && \
test $? -ne 0 && exit 156 || :

cd $SAMPLES/worker-bindings && \
cargo check
cargo check && \
wasm-pack build --target nodejs --dev --no-opt --no-pack --no-typescript && \
node dummy_env_test.js
test $? -ne 0 && exit 157 || :

cd $SAMPLES/worker-durable-websocket && \
cargo check
test $? -ne 0 && exit 158 || :

cd $SAMPLES/worker-with-global-bindings && \
npm run openapi
test $? -ne 0 && exit 159 || :

cd $SAMPLES/worker-with-openapi && \
cp wrangler.toml.sample wrangler.toml && \
(test -f openapi.json || echo '{}' >> openapi.json) && \
Expand All @@ -78,4 +84,4 @@ cd $SAMPLES/worker-with-openapi && \
diff openapi.json tmp.json \
; (test -f tmp.json && rm tmp.json) \
|| :)
test $? -ne 0 && exit 159 || :
test $? -ne 0 && exit 160 || :
1 change: 1 addition & 0 deletions samples/worker-bindings/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ crate-type = ["cdylib", "rlib"]
# set `default-features = false` to assure "DEBUG" feature be off even when DEBUGing `../ohkami`
ohkami = { path = "../../ohkami", default-features = false, features = ["rt_worker"] }
worker = { version = "0.5", features = ["queue", "d1"] }
console_error_panic_hook = "0.1"
13 changes: 13 additions & 0 deletions samples/worker-bindings/dummy_env_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#! /usr/bin/env node

import { join } from 'node:path';
import { cwd, exit } from 'node:process';

const wasmpack_js = await import(join(cwd(), `pkg`, `worker_with_openapi.js`));
if (!wasmpack_js) {
exit("wasmpack_js is not found")
}

wasmpack_js.handle_dummy_env();

console.log("ok");
36 changes: 36 additions & 0 deletions samples/worker-bindings/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use ohkami::bindings;
use worker::wasm_bindgen;

#[bindings]
struct AutoBindings;
Expand Down Expand Up @@ -71,3 +72,38 @@ fn __test_bindings_new__(env: &worker::Env) -> Result<(), worker::Error> {
let _: ManualBindings = ManualBindings::new(env)?;
Ok(())
}

#[wasm_bindgen::prelude::wasm_bindgen]
pub fn handle_dummy_env() {
use worker::wasm_bindgen::{JsCast, closure::Closure};
use worker::js_sys::{Object, Reflect, Function};

console_error_panic_hook::set_once();

let dummy_db = {
let o = Object::new();
{
let constructor = Function::unchecked_from_js(Closure::<dyn Fn()>::new(|| {}).into_js_value());
{
let attributes = Object::new();
Reflect::set(&attributes, &"value".into(), &"D1Database".into()).unwrap();
Reflect::define_property(&constructor, &"name".into(), &attributes).unwrap();
}
Reflect::set(&o, &"constructor".into(), &constructor).unwrap();
}
o
};

let dummy_env = {
let o = Object::new();
{
Reflect::set(&o, &"DB".into(), &dummy_db).unwrap();
Reflect::set(&o, &"MY_KVSTORE".into(), &Object::new()).unwrap();
}
worker::Env::unchecked_from_js(o.unchecked_into())
};

let _: ohkami::bindings::D1 = dummy_env.d1("DB").unwrap();

let _: ohkami::bindings::KV = dummy_env.kv("MY_KVSTORE").unwrap();
}
2 changes: 2 additions & 0 deletions samples/worker-with-global-bindings/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/target
Cargo.lock
23 changes: 23 additions & 0 deletions samples/worker-with-global-bindings/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[package]
name = "worker-with-global-bindings"
version = "0.1.0"
edition = "2024"

[dependencies]
# set `default-features = false` to assure "DEBUG" feature be off even when DEBUGing `../ohkami`
ohkami = { path = "../../ohkami", default-features = false, features = ["rt_worker"] }
worker = { version = "0.5", features = ["d1"] }
thiserror = "1.0"
console_error_panic_hook = "0.1"

[lib]
crate-type = ["cdylib", "rlib"]

[profile.release]
opt-level = "s"

[features]
openapi = ["ohkami/openapi"]

# `--no-default-features` in release profile
default = ["openapi"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CREATE TABLE users (
id INTEGER NOT NULL PRIMARY KEY,
name TEXT NOT NULL UNIQUE,
age INTEGER
);
Loading