initial commit

This commit is contained in:
Peter Cai 2020-04-07 14:23:45 +08:00
commit 9e4bf24377
No known key found for this signature in database
GPG Key ID: 71F5FB4E4F3FD54F
9 changed files with 291 additions and 0 deletions

0
.cargo-ok Normal file
View File

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
/target
**/*.rs.bk
Cargo.lock
bin/
pkg/
wasm-pack.log
worker/generated/
config.json

38
Cargo.toml Normal file
View File

@ -0,0 +1,38 @@
[package]
name = "paprika"
version = "0.1.0"
authors = ["Peter Cai <peter@typeblog.net>"]
edition = "2018"
[lib]
crate-type = ["cdylib", "rlib"]
[features]
default = ["console_error_panic_hook"]
[dependencies]
cfg-if = "0.1.2"
lazy_static = "1.4"
serde = { version = "1.0", features = [ "derive" ] }
serde_json = "1.0"
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
web-sys = { version = "0.3", features = [ "Request", "Response", "ResponseInit", "Url" ] }
# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
console_error_panic_hook = { version = "0.1.1", optional = true }
# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size
# compared to the default allocator's ~10K. It is slower than the default
# allocator, however.
wee_alloc = { version = "0.4.2", optional = true }
[dev-dependencies]
wasm-bindgen-test = "0.2"
[profile.release]
# Tell `rustc` to optimize for small code size.
opt-level = "s"

39
README.md Normal file
View File

@ -0,0 +1,39 @@
# 👷‍♀️🦀🕸️ `rustwasm-worker-template`
A template for kick starting a Cloudflare worker project using
[`wasm-pack`](https://github.com/rustwasm/wasm-pack).
This template is designed for compiling Rust libraries into WebAssembly and
publishing the resulting worker to Cloudflare's worker infrastructure.
## 🔋 Batteries Included
* [`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) for communicating
between WebAssembly and JavaScript.
* [`console_error_panic_hook`](https://github.com/rustwasm/console_error_panic_hook)
for logging panic messages to the developer console.
* [`wee_alloc`](https://github.com/rustwasm/wee_alloc), an allocator optimized
for small code size.
## 🚴 Usage
### 🐑 Use `wrangler generate` to Clone this Template
[Learn more about `wrangler generate` here.](https://github.com/cloudflare/wrangler)
```
wrangler generate wasm-worker https://github.com/cloudflare/rustwasm-worker-template.git
cd wasm-worker
```
### 🛠️ Build with `wasm-pack build`
```
wasm-pack build
```
### 🔬 Test in Headless Browsers with `wasm-pack test`
```
wasm-pack test --headless --firefox
```

60
src/lib.rs Normal file
View File

@ -0,0 +1,60 @@
#[macro_use]
extern crate lazy_static;
mod utils;
mod router;
use cfg_if::cfg_if;
use utils::{Error, MyResult};
use wasm_bindgen::prelude::*;
use web_sys::*;
cfg_if! {
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
// allocator.
if #[cfg(feature = "wee_alloc")] {
extern crate wee_alloc;
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
}
}
lazy_static! {
static ref ROUTER: router::Router = {
build_routes()
};
}
fn build_routes() -> router::Router {
let mut router = router::Router::new(&default_route);
router.add_route("/hello", &hello_world);
return router;
}
async fn default_route(_req: Request, _url: Url) -> MyResult<Response> {
Err(Error::NotFound("This page is not available".into()))
}
async fn hello_world(_req: Request, _url: Url) -> MyResult<Response> {
Ok(Response::new_with_opt_str_and_init(
Some("Hello, world from Rust"),
ResponseInit::new().status(200)
).unwrap())
}
#[wasm_bindgen]
pub async fn handle_request_rs(req: Request) -> Response {
let url = Url::new(&req.url()).unwrap();
let result = ROUTER.execute(req, url).await;
match result {
Ok(resp) => resp,
Err(err) => {
let code = err.status_code();
let reason: String = err.into();
Response::new_with_opt_str_and_init(
Some(&reason), ResponseInit::new().status(code)
).unwrap()
}
}
}

70
src/router.rs Normal file
View File

@ -0,0 +1,70 @@
use crate::utils::MyResult;
use std::future::Future;
use std::pin::Pin;
use std::vec::Vec;
use web_sys::*;
// We have to box everything in order to make them fit in a Vec
type RouteHandler = Box<dyn Sync + Fn(Request, Url) -> Pin<Box<dyn Future<Output = MyResult<Response>>>>>;
// Convert a async function to RouteHandler
// both boxes the function and the returned Future
macro_rules! async_fn_boxed {
($f:ident) => {
Box::new(move |req, url| Box::pin($f(req, url)))
};
}
struct Route {
path: String,
handler: RouteHandler
}
pub struct Router {
routes: Vec<Route>,
default_handler: RouteHandler
}
impl Router {
pub fn new<F, T>(default_handler: &'static F) -> Router
where F: Sync + Fn(Request, Url) -> T,
T: 'static + Future<Output = MyResult<Response>> {
Router {
routes: vec![],
default_handler: async_fn_boxed!(default_handler)
}
}
pub fn add_route<F, T>(
&mut self,
path: &str,
handler: &'static F
) where F: Sync + Fn(Request, Url) -> T,
T: 'static + Future<Output = MyResult<Response>>
{
self.routes.push(Route {
path: path.into(),
handler: async_fn_boxed!(handler)
});
}
pub async fn execute(&self, req: Request, url: Url) -> MyResult<Response> {
for route in self.routes.iter() {
// Routes added earlier overrides routes added later
// e.g. if '/path/aaa' was added before '/path/', then
// calls to '/path/aaa' will not be dispatched to '/path/'
// Routes ending with '/' are considered prefixes.
if route.path.ends_with("/") {
if url.pathname().starts_with(&route.path) {
return (route.handler)(req, url).await;
}
} else {
if url.pathname() == route.path {
return (route.handler)(req, url).await;
}
}
}
return (self.default_handler)(req, url).await;
}
}

52
src/utils.rs Normal file
View File

@ -0,0 +1,52 @@
use cfg_if::cfg_if;
use serde::Deserialize;
cfg_if! {
// When the `console_error_panic_hook` feature is enabled, we can call the
// `set_panic_hook` function at least once during initialization, and then
// we will get better error messages if our code ever panics.
//
// For more details see
// https://github.com/rustwasm/console_error_panic_hook#readme
if #[cfg(feature = "console_error_panic_hook")] {
extern crate console_error_panic_hook;
pub use self::console_error_panic_hook::set_once as set_panic_hook;
} else {
#[inline]
pub fn set_panic_hook() {}
}
}
pub type MyResult<T> = Result<T, Error>;
pub enum Error {
NotFound(String)
}
impl Error {
pub fn status_code(&self) -> u16 {
match self {
Error::NotFound(_) => 404
}
}
}
impl Into<String> for Error {
fn into(self) -> String {
match self {
Error::NotFound(reason) => {
format!("Not Found, Reason: {}", reason)
}
}
}
}
#[derive(Deserialize)]
pub struct Config {
// The secret value used to authenticate the Standard Notes plugin link
secret: String
}
pub fn get_config() -> Config {
serde_json::from_str(std::include_str!("../config.json")).unwrap()
}

10
worker/metadata_wasm.json Normal file
View File

@ -0,0 +1,10 @@
{
"body_part": "script",
"bindings": [
{
"name": "wasm",
"type": "wasm_module",
"part": "wasmprogram"
}
]
}

14
worker/worker.js Normal file
View File

@ -0,0 +1,14 @@
const { handle_request_rs } = wasm_bindgen;
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
/**
* Fetch and log a request
* @param {Request} request
*/
async function handleRequest(request) {
await wasm_bindgen(wasm);
return await handle_request_rs(request);
}