cloudflare / workers-rs Goto Github PK
View Code? Open in Web Editor NEWWrite Cloudflare Workers in 100% Rust via WebAssembly
License: Apache License 2.0
Write Cloudflare Workers in 100% Rust via WebAssembly
License: Apache License 2.0
Originally posted by vorcigernix October 16, 2021
I am trying to follow example code from the readme. For some reason it returns
KvError::Serialization: expected value at line 1 column 1
panicked at 'KvError::Serialization: expected value at line 1 column 1', src/lib.rs:20:1
where error line is where I serialize object to json like
Some(jmeniny) => Response::from_json(&jmeniny.as_json::<Jmeniny>()?),
When I format the response as a string, it works fine:
Some(jmeniny) => Response::ok(&format!("response: {:?}", &jmeniny)),
results in
response: KvValue("Hynek")
It seems that from_json use serde internally, so it should work fine. Or I am overlooking something basic in KV.
Jmeniny struct is like this:
#[derive(Deserialize, Serialize)]
struct Jmeniny {
name: String,
}
I think that it is some stupid mistake, but I'll appreciate any help.
Now that @mrbbot has added support for doing this in TypeScript (cloudflare/workers-types#112), we have the infrastructure for doing this for Rust too. He actually did both but the Rust one just hasn't been integrated. Not sure what actually needs to be done here but opening this issue just to track it.
It has been two months since the last release and master contains fixes that I need.
Is there a reason to not release patch versions after every merge?
Hi! ๐
I'm currently trying to port my Cloudflare worker from TypeScript to Rust. I'm using a middleware pattern where I want pass a worker::Request
to various functions that will transform them (e.g. transform the URL, strip certain headers, etc.) before sending to the origin. Similarly, I want to transform the worker::Response
through multiple middlewares before sending it back to the end-user.
As far as I can, as the fields of both objects are private, I can only access those values through functions (e.g. req.path()
, req.headers()
), which means I can't mutate those values in place.
Is there any plan to support this type of pattern in Rust? Or is there anything I'm missing that would allow me to do that?
We'll still need to convert this in worker
itself, because it needs to turn back into a wasm_bindgen::JsValue
, but we don't need to add our own Response type, we can just use one from the ecosystem. Same for many of the other types (Request
etc).
This will mean all the logging from libraries that developers don't control will still show up in the JS console. We could implement it by registering a log handler which outputs to the JS console.
Would suggest adding GitHub Actions (clippy and build check) which runs on every PR/merge to main
that makes it easier for maintainers to review Pull Requests as well as for contributors to improve their coding style when they contribute to this repository.
Would be happy to raise a PR for the same ๐
Hi there,
I'm trying to play around with Durable Workers, so I copied in the Chatroom
example from the README into my pre-existing lib.rs (which was mostly the default example after running wrangler generate
).
My entire src/lib.rs file looks like this:
use worker::*;
#[durable_object]
pub struct Chatroom {
state: State,
env: Env, // access `Env` across requests, use inside `fetch`
}
#[durable_object]
impl DurableObject for Chatroom {
fn new(state: State, env: Env) -> Self {
Self { state: state, env }
}
async fn fetch(&mut self, _req: Request) -> Result<Response> {
todo!()
}
}
#[event(fetch)]
pub async fn main(req: Request, env: Env) -> Result<Response> {
Response::error("Bad Request", 400)
}
Running wrangler build
, I get this error:
[INFO]: Compiling to Wasm...
Compiling project_name v0.1.0 (/home/achin/tmp/70/project_name)
error[E0428]: the name `__WASM_BINDGEN_GENERATED_0bdc7e4367fd39cb` is defined multiple times
--> src/lib.rs:20:1
|
3 | #[durable_object]
| ----------------- previous definition of the value `__WASM_BINDGEN_GENERATED_0bdc7e4367fd39cb` here
...
20 | #[event(fetch)]
| ^^^^^^^^^^^^^^^ `__WASM_BINDGEN_GENERATED_0bdc7e4367fd39cb` redefined here
|
= note: `__WASM_BINDGEN_GENERATED_0bdc7e4367fd39cb` must be defined only once in the value namespace of this module
= note: this error originates in the attribute macro `event` (in Nightly builds, run with -Z macro-backtrace for more info)
For more information about this error, try `rustc --explain E0428`.
error: could not compile `project_name` due to previous error
Error: Compiling your crate to WebAssembly failed
Caused by: failed to execute `cargo build`: exited with exit status: 101
full command: "cargo" "build" "--lib" "--release" "--target" "wasm32-unknown-unknown"
Error: wasm-pack exited with status exit status: 1
Error: Build failed! Status Code: 1
I think it's illegal to define a fetch handler and also a durable object in the same file. Is that true?
If so, I would be great if this could be detected better (better error message), and even greater if the README stated this a bit more clearly :)
rustc: rustc 1.58.0-nightly (dd549dcab 2021-11-25)
wrangler: wrangler 1.19.5
workers-rs: 0.0.7
Current examples in https://github.com/cloudflare/workers-rs/blame/main/README.md#L9, https://blog.cloudflare.com/workers-rust-sdk/ and project generated by wrangler generate --type=rust
all provide some advanced usage of using router, console_log, DO, POST handling. None of them worked out of the box for me.
For those whose just start out, like me, would it be possible to provide and example worker that just returns Hello world
string so we could start from there? Thanks a lot!
The type for KvStore is not available from the worker crate. I need to add the worker_kv dependency to be able to get the KvStore type. It might be a good idea to re-export the KvStore type. This will insure that the KvStore type used in one's project is the same type being used by the worker crate.
Hi,
In the last week I applied to and attempted the Cloudflare Internship Hiring Assignment https://apply.cloudflareworkers.com/ with this crate. I would say I completed a majority of it but at some point I was too unsatisfied with how my code looked so I switched to using JavaScript for my worker. I had some general questions about what the expected/idiomatic way of doing certain things were for which I couldn't find any documentation/examples for -
worker::Error
which by default causes an error response of some sort with an HTML body. If I were developing a REST API with JSON responses what would be the cleanest way to convert my errors to the appropriate JSON responses with a unique error code ?I hope my message doesn't come of as too aggressive. I realize that Rust support is in very early stages and would be happy to contribute to this crates code / documentation / examples etc.
This is just developer friction / newbie trap.
Wrangler 1.19.2 installed by building via cargo.
With the template Rust workers demo from https://blog.cloudflare.com/workers-rust-sdk/
$ wrangler dev
๐ Running cargo install -q worker-build && worker-build --release
sh: worker-build: command not found
Error: Build failed! Status Code: 127
$ PATH="$PATH:$HOME/.cargo/bin"
$ wrangler dev
๐ Running cargo install -q worker-build && worker-build --release
Installing wasm-pack...
Ideally:
Fill out the following information about your environment.
wrangler -V
: wrangler 1.19.2node -v
: node: command not foundwrangler.toml
: unedited from templateexist: from_html(), from_json() ? But I need set CONTENT_TYPE, "text/javascript"
exist: with_headers(). But I can't figure out how to use it.
A simple example is very appreciated.
Even if we don't make it constructable, it would still be nice to make the type itself public so you can see what traits are implemented, something like this:
pub struct Error(ErrorInner);
enum ErrorInner { ... }
Hey There! ๐
Was just exploring the use of worker-rs where i eventually figured out that there is no redirect()
method implementation for the Response
struct, which means that i cannot redirect to a URL (302,308,etc) for an response recieved. It also becomes inconsistent with the documentation found at https://developers.cloudflare.com/workers/runtime-apis/response#instance-methods where it specifies an instance method for redirect()
.
Proposal:
Implement a function in the Impl
of Response struct with the apt headers, and struct fields which becomes available for the user to use
Would be happy to raise a PR for the same ๐
Iโm experimenting with using the async-graphql
crate inside a worker. When I use wee_alloc
as the global allocator, the first few bytes of each response from the worker are being consistently โmangledโ for some reason. Here is a repository with a minimal worker which replicates the issue: https://github.com/thomasfoster96/wealloc-issue and here is the worker in action: https://wealloc-issue.thomasfoster.workers.dev/.
Here I what I would expect my worker to produce:
{"data":null,"errors":[{"message":"Bad Request"}]}
Instead, this is what I get:
๏ฟฝ๏ฟฝ๏ฟฝ๏ฟฝta":null,"errors":[{"message":"Bad Request"}]}
The bytes are mangled the same way on every request. Calling serde_json::to_string
on the GraphQL request and then logging it with console_log!
produces the correct output โ this is only happening when I use Response::from_json
or similar methods.
Creating a response body in other ways produces similar results. For example, using serde_json::to_string
produces the correct output but using Response::from_html
then mangles it, as does using serde_json::to_vec
and then Response::from_bytes
.
(Apologies if any of this is unclear, Iโm very much a beginner with Rust and Cloudflare Workers)
Hi ๐
I had tried to re-implement the Workers Sites using Rust, but I was stuck by KV access.
As far as I know, the kv-asset-handler package uses the STATIC_CONTENT_MANIFEST
variable from global context generated by wrangler. Am I right?
If it is, can you provide some ways to accessing the manifest or Rust version of getAssetFromKV
function?
This is a probably a very big task, since JS Workers don't seem to have an existing library or a nice way to do it. There are some existing crates like tonic which support gRPC but these likely wouldn't work without a lot of changes. gRPC also has streams, which some platforms don't support while still supporting unary requests (possibly for similar reasons that might prevent Workers from supporting them). gRPC-web might be easier, but do not currently support streams.
Another possible issue is Cloudflare's support for gRPC since HTTP/2 isn't just enabled for everything by default and Workers might not currently support HTTP/2(?).
It seems the only reason Router
and RouteContext
data
method return an Option<Data>
is because you've decided to implement Default
for a router. Nothing seems to use that default. Could I convince you to remove the Default
and make data be not optional? That would remove a .unwrap()
from practically every handler of every app using data.
For inspiration/justification/examples:
I had wrangler login
ed to my Cloudflare account already, but when trying to wrangler dev
I received a 403 error that was not very useful in helping me determine the cause:
wrangler dev
๐ Running cargo install -q worker-build && worker-build --release
[INFO]: Checking for the Wasm target...
[INFO]: Compiling to Wasm...
Finished release [optimized] target(s) in 0.03s
[INFO]: Installing wasm-bindgen...
[INFO]: Optimizing wasm binaries with `wasm-opt`...
[INFO]: Optional fields missing from Cargo.toml: 'description', 'repository', and 'license'. These are not necessary, but recommended
[INFO]: :-) Done in 2.10s
[INFO]: :-) Your wasm pkg is ready to publish at /home/jason/src/my-project/build.
Error: HTTP status client error (403 Forbidden) for url (https://api.cloudflare.com/client/v4/accounts/ae126deafb76cf635e5e028f594434ac/workers/subdomain/edge-preview)
I think I tracked it down to not enabling the free cloudworker component in my account, but 1) I wasn't expecting a local dev
cli command to require that and 2) it would have been good to spell that out in the returned 403.
Now it seems to be working past that (but getting a different error I'm trying to make sense of). Thanks for the cool project!
This error is confusing:
error[E0428]: the name `fetch` is defined multiple times
--> rust-sandbox/src/lib.rs:38:1
|
36 | fn fetch() {}
| ---------- previous definition of the value `fetch` here
37 |
38 | #[event(fetch, respond_with_errors)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `fetch` redefined here
|
= note: `fetch` must be defined only once in the value namespace of this module
= note: this error originates in the attribute macro `event` (in Nightly builds, run with -Z macro-backtrace for more info)
It would be better to name the generated function whatever it was originally (in this case, main
).
I'd like to be able to set arbitrary status codes (201, 202, 204, 302, etc.) and currently, that is impossible because Response::status_code
is private and there is no method to create a custom response or modify a response like with_status(self, code: u16)
(similar to with_headers
.)
This will go away with #13.
The Workers runtime recently updated to V8 9.6, which has support for WebAssembly reference types. wasm-bindgen also has support with an optional flag: https://rustwasm.github.io/wasm-bindgen/reference/reference-types.html
lib.rs
:
mod utils;
use worker::*;
// could not find `Schedule` in `worker`rustc(E0433)
#[event(scheduled)]
pub async fn main(ty: String, schedule: u64, cron: String) -> Result<()> {
println!("test");
Ok(())
}
Is there something I missed?
A response from Fetch
can't be modified:
Fetch::Request(request).send().await?.with_headers(Headers::new())
The code compiles but doesn't actually update the headers.
My workaround:
use wasm_bindgen::{prelude::*, JsCast};
use worker::worker_sys;
#[wasm_bindgen]
extern "C" {
pub type Response;
#[wasm_bindgen(catch, constructor, js_class=Response)]
pub fn new_with_opt_stream_and_init(
body: Option<web_sys::ReadableStream>,
init: &web_sys::ResponseInit,
) -> std::result::Result<Response, JsValue>;
}
impl Response {
pub fn dup(response: worker::Response, headers: &mut worker::Headers) -> crate::Result<worker::Response> {
let mut response_init = web_sys::ResponseInit::new();
response_init.headers(&mut headers.0);
let response: worker_sys::Response = response.into();
let response = Response::new_with_opt_stream_and_init(response.body(), &response_init)?;
let response = response.unchecked_into::<worker_sys::Response>();
let body = worker::ResponseBody::Stream(response);
Ok(worker::Response::from_body(body)?)
}
}
It seems weird for me that functions like Response::error
still takes a u16
as the response status code, and impl Into<String>
as the response description, since the crate http
has already been a dep. So I'm expecting to have sigs like
pub fn error(code: http::StatusCode) -> Result<Self>;
pub fn status_code(&self) -> http::StatusCode;
Wrapping around http::StatusCode
to have custom types like ErrorStatusCode
will also allow us us to lift the following error to type-level & reduce runtime cost (well, maybe just a little). And I believe this is an error that should not be thrown in runtime.
if !(400..=599).contains(&status) {
return Err(Error::Internal(
"error status codes must be in the 400-599 range! see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status for more".into(),
));
}
Response::bytes()
calls text()
when inner data is ResponseBody::Streaming
so data gets corrupted with error message.
workers-rs/worker/src/response.rs
Lines 164 to 168 in 1be6eaa
All the other errors in that enum are statically impossible, so we should encode that in the type system.
I'm trying to store binary data in the KV, but the data appears to getting corrupted. Consider this code:
.get_async("/test", |req, ctx| async move {
let kv = ctx.kv("UPLOAD")?;
let bytes = [0xd2, 0xda, 0x1e, 0x77, 0x3c, 0x50, 0xd9, 0x35, 0x9a, 0x89, 0xd5, 0x53, 0xc3, 0x81, 0x6d, 0x20];
kv.put_bytes("test", &bytes)?.execute().await?;
let bytes_from_kv = kv.get("test").await?.unwrap();
assert_eq!(&bytes, bytes_from_kv.as_bytes());
Response::empty()
})
Trying to run this yields this assertion failure:
panicked at 'assertion failed: `(left == right)`
left: `[210, 218, 30, 119, 60, 80, 217, 53, 154, 137, 213, 83, 195, 129, 109, 32]`,
right: `[239, 191, 189, 239, 191, 189, 30, 119, 60, 80, 239, 191, 189, 53, 239, 191, 189, 239, 191, 189, 239, 191, 189, 83, 195, 129, 109, 32]`', src/lib.rs:56:13
However when I download this value via the https://dash.cloudflare.com/, the data is correct (so it seems like the corruption is happening during the fetch on the rust side)
Hi!
It seems like something's wrong with RequestInit::new(). Here is the simple code, it creates empty RequestInit and then returns empty response. And it stucks.
use worker::*;
mod utils;
fn log_request(req: &Request) {
console_log!(
"{} - [{}], located at: {:?}, within: {}",
Date::now().to_string(),
req.path(),
req.cf().coordinates().unwrap_or_default(),
req.cf().region().unwrap_or("unknown region".into())
);
}
#[event(fetch)]
pub async fn main(req: Request, env: Env) -> Result<Response> {
log_request(&req);
utils::set_panic_hook();
let _init = RequestInit::new(); // the code stucks here
Response::empty()
}
And here's the logs from wrangler dev
:
๐ Listening on http://127.0.0.1:8787
Sun Sep 12 2021 12:06:47 GMT+0000 (Coordinated Universal Time) - [/], located at: (59.8983, 30.2618), within: St.-Petersburg
Error: Worker exceeded CPU time limit. at line 0, col -2
{
"exceptionDetails": {
"columnNumber": -2,
"exception": {
"className": "Error",
"description": "Error: Worker exceeded CPU time limit.",
"preview": {
"description": "Error: Worker exceeded CPU time limit.",
"entries": null,
"overflow": false,
"properties": [],
"subtype": "error",
"type": "object"
},
"subtype": "error",
"type": "object",
"value": null
},
"lineNumber": 0,
"text": "Uncaught (in response)",
"url": "undefined"
},
"timestamp": 1631448407649
}
[2021-09-12 15:06:46] GET response-bot-dev.idebugger.workers.dev/ HTTP/1.1 503 Service Unavailable
A crates based on html5ever (guessing) doesn't work.
Is it possible to solve it?
While trying to use wasm-streams to implement streaming support for the worker-kv crate I ran across a bug where generated JS snippets aren't moved into the worker sub directory, causing all function calls that use that snippet to crash the worker. This is the snippet in wasm-streams that needs to be generated due to a Chromium bug.
Here is an example repo with this bug.
I'm quite new to Rust development so it's entirely possible I'm doing something obviously wrong but I've tried pulling down this example and running according to the README and am getting this error. Anything obvious I need to adjust? Is my local env not setup appropriately?
% wrangler build
๐ Running cargo install -q worker-build && worker-build --release
error: could not find `worker-build` in registry `crates-io` with version `*`
Error: Build failed! Status Code: 101
It would be really helpful to be able to pass in a closure to the router that is run when there is no matching route. In my use case, I'd like to be able to trigger logging and analytics. I'm using the worker as a gateway and I'd like to be sure that all the requests are being handled.
At the moment you can't create custom responses without worker::response::ResponseBody
which is in a private module.
I think the best course of action would be to move worker::response::ResponseBody
to worker::ResponseBody
but as I am pretty new to Rust i'll leave this up to the maintainers.
It probably makes sense for WebSocket clients (to remote servers) to use the same type as what's returned from the new WebSocketPair()
equivalent (although probably only relevant if it's added at the same time as server support).
Steps to Reproduce Error:
Wrangler generate --type=rust project_name
cd project_name
wrangler build
Following error displays:
Running cargo install -q worker-build && worker-build --release
error: linker `cc` not found
|
= note: No such file or directory (os error 2)
error: failed to compile `worker-build v0.0.3`, intermediate artifacts can be found at `/tmp/cargo-installarcZxR`
Caused by:
could not compile `anyhow` due to previous error
Error: Build failed! Status Code: 101
Attempting to Google the error doesn't result in anything substantial to work on.
The naming of the crates in this project are a bit everywhere. It'd be nice to consolidate the names of everything and reorganize before committing to any crates.io releases.
As it stands there are currently five crates each with wildly different names:
libworker could be dropped in favor of just defining everything in worker instead of using worker to re-export everything. Currently libworker is separate so it can be imported by worker-rs-macros however the macros crate never uses it so it's an unnecessary dependency. With the base name "worker", we can easily turn this into four separate easily identifiable crates.
On top of this, it may be favorable to drop the rust-sandbox crate. Instead of testing or experimenting in a sandbox, things normally written in the sandbox would be better if written as an example in the worker crate. Then things written as examples for testing during development can be referenced later by users.
It's quite strange to have FormData::get
returning FormEntry
with FormData::set
and FormData::append
accepting only &str
. It would be great if we can insert File
or something like it directly into FormData
.
I am positive that someone will try to use tokio::spawn
no matter what, but we can at least flash a big warning in the readme.
Hi,
Apologies in advance if I'm missing something obvious here, I'm new to Rust. I'm trying to use Router
in a #[durable_object]
s fetch
method like so:
use worker::*;
#[durable_object]
pub struct Chatroom {
messages: Vec<String>,
state: State,
env: Env,
}
#[durable_object]
impl DurableObject for Chatroom {
fn new(state: State, env: Env) -> Self {
Self {
messages: vec![],
state,
env,
}
}
async fn fetch(&mut self, req: Request) -> worker::Result<Response> {
let router = Router::with_data(&self.messages);
router
.get("/", |_req, context| {
Response::ok(&format!("{} messages", context.data().len()))
})
.run(req, self.env)
.await
}
}
This fails with the error:
error[E0507]: cannot move out of `self.env` which is behind a mutable reference
--> src/chatroom.rs:27:23
|
27 | .run(req, self.env)
| ^^^^^^^^ move occurs because `self.env` has type `worker::Env`, which does not implement the `Copy` trait
This makes sense to me, as Router#run
takes ownership of it's env
argument, and since I'm taking a &mut self
as a reference I can't move self.env
. The #[durable_object]
macro forces fetch
to be &mut self
so I can only give references of env
. I can't work out how you would use Router inside it?
workers-rs/worker-macros/Cargo.toml
Line 20 in 4da0065
wasm-bindgen
version 0.2.76, refusing build with newer v0.2.77 dependency. Is it intended for compatibility or convenience? It seems like this 'version pinning' has been used since the first addition.Response::error("Error", 200)
is perfectly valid and is not correct at all.
Actually, looking at the implementation it would be fine if we just renamed this to Response::with_status
. @nilslice what do you think? We could still have Response::internal_error
or Response::request_error
convenience wrappers if you think they're helpful.
(This may become a moot point after implementing #13.)
Add cfg feature for use of chrono
, mentioned in #8
Currently, it appears Response
s received from fetching are immutable. It would be ideal if the Response
could be Clone
d, modified, and then sent to a recipient.
The use case would be instances where the worker is authorized to make a request, but the authorization information in the request should not be shared with the final recipient.
If I am wrong or there is a workaround, please let me know.
How do I access Cloudflare cache?
When trying to use KVStore's put
to store bytes, I get this runtime error:
let kv = ctx.kv("UPLOAD")?;
kv.put("test", "test".as_bytes())?.execute().await?;
TypeError: KV put() accepts only strings, ArrayBuffers, ArrayBufferViews, and ReadableStreams as values.
Two bits of confusion on this:
pub fn put<T: ToRawKvValue>(
&self,
name: &str,
value: T
) -> Result<PutOptionsBuilder, KvError>;
And there is an impl ToRawKvValue for [u8]
, so this code compiles. But if this function doesn't support byte slices, then why does it accept them?
put
accept bytes? Stated another way: why is there both a put()
and put_bytes()
?Thanks!
This could be under an optional cargo feature to avoid pulling in chrono unconditionally.
Hello,
I've been working with the workers-rs api recently and noticed it has got a few rough edges in my opinion.
I've thought how that could be improved thus I am suggesting this here, willing to create a PR once the idea has been approved.
=> Should be the Self-Object not conatained in a Result, user should call Ok(Response) later.
I am aware of the error-possibility from set_headers, those can be wrapped into an Response-internal error object which is unwrapped in the final error handling. (See next point)
=>Make an error handling function built into the router which
a) supports any generic error type with a "FromWorkerError" trait
b) has the signature set_error_handler<T: FromWorkerError, E: 'static + Copy + Fn(T) -> Response>(fun: E).
This function will unwrap any possible error into a custom error message, if desired. The default error handler can just be a panic!().
pub struct ResponseWrapper {
body: Option<Vec<u8>>,
headers: worker::Headers,
status: u16,
error: Option<worker::Error>,
}
impl ResponseWrapper {
pub fn json<T: Serialize>(data: &T) -> Result<Self, serde_json::Error> {
let data = serde_json::to_vec(data)?;
Ok(Self::bytes(data)
.append_header("content-type", "application/json"))
}
pub fn bytes(data: Vec<u8>) -> Self {
Self {
body: Some(data),
headers: worker::Headers::new(),
status: 200,
error: None,
}
}
pub fn text<S: Into<String>>(data: S) -> Self {
Self {
body: Some(data.into().into_bytes()),
headers: worker::Headers::new(),
status: 200,
error: None,
}
}
pub fn empty() -> Self {
Self {
body: None,
headers: worker::Headers::new(),
status: 204,
error: None,
}
}
pub fn with_status(mut self, status: u16) -> Self {
self.status = status;
self
}
pub fn append_header(mut self, key: &str, val: &str) -> Self {
if let Some(err) = self.headers.append(key, val).err() {
self.error = Some(err);
}
self
}
}
impl Into<worker::Result<worker::Response>> for ResponseWrapper {
fn into(self) -> worker::Result<Response> {
if let Some(err) = self.error {
Err(err)
} else {
let headers = self.headers;
let status = self.status;
if let Some(data) = self.body {
Response::from_body(ResponseBody::Body(data))
.map(|resp| resp.with_headers(headers))
.map(|resp| resp.with_status(status))
} else {
Response::from_body(ResponseBody::Empty)
.map(|resp| resp.with_headers(headers))
.map(|resp| resp.with_status(status))
}
}
}
}
pub struct RequestWrapper<T, D> {
func: fn(worker::Request, worker::RouteContext<D>) -> T,
}
pub type ToResponseFn<'a, D> = Rc<dyn Fn(worker::Request, worker::RouteContext<D>) -> LocalBoxFuture<'a, ResponseWrapper>>;
pub trait ToResponseJson<'a, R, D, E> {
fn to_response_json(self, err: E) -> ToResponseFn<'a, D>;
}
pub trait ToResponsePlain<'a, R, D, E> {
fn to_response_plain(self, err: E) -> ToResponseFn<'a, D>;
}
pub trait ToResponseEmpty<'a, R, D, E> {
fn to_response_empty(self, err: E) -> ToResponseFn<'a, D>;
}
impl<R: 'static, T: 'static + Future<Output=Result<R, crate::model::Error>>, D: 'static> RequestWrapper<T, D> {
pub fn from(func: fn(worker::Request, worker::RouteContext<D>) -> T) -> Self {
Self {
func,
}
}
}
impl<
'a,
R: 'static + Serialize,
T: 'static + Future<Output=Result<R, crate::model::Error>>,
D: 'static,
E: 'static + Copy + Fn(crate::model::Error) -> ResponseWrapper
> ToResponseJson<'a, R, D, E> for RequestWrapper<T, D> {
fn to_response_json(self, err: E) -> ToResponseFn<'a, D> {
Rc::new(move |req, ctx| Box::pin(to_response_json(req, ctx, err, self.func)))
}
}
impl<
'a,
R: 'static + Into<String>,
T: 'static + Future<Output=Result<R, crate::model::Error>>,
D: 'static,
E: 'static + Copy + Fn(crate::model::Error) -> ResponseWrapper
> ToResponsePlain<'a, R, D, E> for RequestWrapper<T, D> {
fn to_response_plain(self, err: E) -> ToResponseFn<'a, D> {
Rc::new(move |req, ctx| Box::pin(to_response_plain(req, ctx, err, self.func)))
}
}
impl<
'a,
R: 'static + None,
T: 'static + Future<Output=Result<R, crate::model::Error>>,
D: 'static,
E: 'static + Copy + Fn(crate::model::Error) -> ResponseWrapper
> ToResponseEmpty<'a, R, D, E> for RequestWrapper<T, D> {
fn to_response_empty(self, err: E) -> ToResponseFn<'a, D> {
Rc::new(move |req, ctx| Box::pin(to_response_empty(req, ctx, err, self.func)))
}
}
pub fn default_error_wrapper(err: crate::model::Error) -> ResponseWrapper {
ResponseWrapper::text(format!("Error: {}", err))
.with_status(500)
}
async fn to_response_json<
R: 'static + Serialize,
T: Future<Output=Result<R, crate::model::Error>>,
D,
E: Fn(crate::model::Error) -> ResponseWrapper
>(req: worker::Request, ctx: worker::RouteContext<D>, error_wrapper: E, func: fn(worker::Request, worker::RouteContext<D>) -> T) -> ResponseWrapper {
let result = func(req, ctx).await;
match result {
Ok(data) => {
match ResponseWrapper::json(&data) {
Ok(resp) => resp,
Err(err) => {
// todo: error_wrapper?
ResponseWrapper::text(format!("{}", err))
.with_status(500)
}
}
}
Err(err) => {
error_wrapper(err)
}
}
}
async fn to_response_plain<
R: 'static + Into<String>,
T: Future<Output=Result<R, crate::model::Error>>,
D,
E: Fn(crate::model::Error) -> ResponseWrapper
>(req: worker::Request, ctx: worker::RouteContext<D>, error_wrapper: E, func: fn(worker::Request, worker::RouteContext<D>) -> T) -> ResponseWrapper {
let result = func(req, ctx).await;
match result {
Ok(data) => ResponseWrapper::text(data),
Err(err) => {
error_wrapper(err)
}
}
}
async fn to_response_empty<
R: 'static + None,
T: Future<Output=Result<R, crate::model::Error>>,
D,
E: Fn(crate::model::Error) -> ResponseWrapper
>(req: worker::Request, ctx: worker::RouteContext<D>, error_wrapper: E, func: fn(worker::Request, worker::RouteContext<D>) -> T) -> ResponseWrapper {
let result = func(req, ctx).await;
match result {
Ok(_) => ResponseWrapper::empty(),
Err(err) => {
error_wrapper(err)
}
}
}
trait None {}
impl None for () {}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.