Skip to content

Commit db0ee32

Browse files
authored
Merge pull request #20 from fermyon/helper-crate
Helper crate
2 parents 5aa8463 + 35840cb commit db0ee32

File tree

18 files changed

+203
-271
lines changed

18 files changed

+203
-271
lines changed

Cargo.lock

+16
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@ resolver = "2"
1717

1818
[workspace.dependencies]
1919
anyhow = "1.0"
20+
helper = { path = "crates/helper" }
2021
wit-bindgen = "0.26"

components/key-value/Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ edition = "2021"
77
crate-type = ["cdylib"]
88

99
[dependencies]
10-
wit-bindgen = { workspace = true }
1110
anyhow = { workspace = true }
11+
helper = { workspace = true }
12+
wit-bindgen = { workspace = true }

components/key-value/src/lib.rs

+13-47
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,23 @@
1-
use anyhow::Context;
2-
use bindings::{
3-
exports::wasi::http0_2_0::incoming_handler::Guest,
4-
fermyon::spin2_0_0::key_value::{Error, Store},
5-
wasi::http0_2_0::types::{
6-
ErrorCode, Headers, IncomingRequest, OutgoingBody, OutgoingResponse, ResponseOutparam,
7-
},
8-
};
9-
10-
mod bindings {
11-
wit_bindgen::generate!({
12-
world: "http-trigger",
13-
path: "../../wit",
14-
});
15-
use super::Component;
16-
export!(Component);
17-
}
1+
use anyhow::Context as _;
182

193
struct Component;
204

21-
impl Guest for Component {
5+
helper::gen_http_trigger_bindings!(Component);
6+
7+
use helper::bindings::{
8+
fermyon::spin2_0_0::key_value::{Error, Store},
9+
wasi::http0_2_0::types::{IncomingRequest, OutgoingResponse, ResponseOutparam},
10+
};
11+
12+
impl bindings::Guest for Component {
2213
fn handle(request: IncomingRequest, response_out: ResponseOutparam) {
23-
let result = handle(request)
24-
.map(|r| match r {
25-
Err(e) => response(500, format!("{e}").as_bytes()),
26-
Ok(()) => response(200, b""),
27-
})
28-
.map_err(|e| ErrorCode::InternalError(Some(e.to_string())));
29-
ResponseOutparam::set(response_out, result)
14+
helper::handle_result(handle(request), response_out);
3015
}
3116
}
3217

33-
fn handle(_req: IncomingRequest) -> anyhow::Result<Result<(), Error>> {
18+
fn handle(_req: IncomingRequest) -> anyhow::Result<OutgoingResponse> {
3419
anyhow::ensure!(matches!(Store::open("forbidden"), Err(Error::AccessDenied)));
35-
let store = match Store::open("default") {
36-
Ok(s) => s,
37-
Err(e) => return Ok(Err(e)),
38-
};
20+
let store = Store::open("default")?;
3921

4022
// Ensure nothing set in `bar` key
4123
store.delete("bar").context("could not delete 'bar' key")?;
@@ -71,21 +53,5 @@ fn handle(_req: IncomingRequest) -> anyhow::Result<Result<(), Error>> {
7153
anyhow::ensure!(matches!(store.get("qux"), Ok(None)));
7254
anyhow::ensure!(matches!(store.get_keys().as_deref(), Ok(&[])));
7355

74-
Ok(Ok(()))
75-
}
76-
77-
fn response(status: u16, body: &[u8]) -> OutgoingResponse {
78-
let response = OutgoingResponse::new(Headers::new());
79-
response.set_status_code(status).unwrap();
80-
if !body.is_empty() {
81-
assert!(body.len() <= 4096);
82-
let outgoing_body = response.body().unwrap();
83-
{
84-
let outgoing_stream = outgoing_body.write().unwrap();
85-
outgoing_stream.blocking_write_and_flush(body).unwrap();
86-
// The outgoing stream must be dropped before the outgoing body is finished.
87-
}
88-
OutgoingBody::finish(outgoing_body, None).unwrap();
89-
}
90-
response
56+
Ok(helper::ok_response())
9157
}

components/outbound-redis/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ crate-type = ["cdylib"]
88

99
[dependencies]
1010
anyhow = { workspace = true }
11+
helper = { workspace = true }
1112
wit-bindgen = { workspace = true }

components/outbound-redis/src/lib.rs

+14-45
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,29 @@
11
use anyhow::Context as _;
2-
use bindings::{
3-
exports::wasi::http0_2_0::incoming_handler::Guest,
4-
fermyon::spin2_0_0::redis,
5-
wasi::http0_2_0::types::{
6-
Headers, IncomingRequest, OutgoingBody, OutgoingResponse, ResponseOutparam,
7-
},
8-
};
92

103
struct Component;
114

12-
mod bindings {
13-
wit_bindgen::generate!({
14-
world: "http-trigger",
15-
path: "../../wit",
16-
});
17-
use super::Component;
18-
export!(Component);
19-
}
5+
helper::gen_http_trigger_bindings!(Component);
206

21-
impl Guest for Component {
7+
use helper::bindings::{
8+
fermyon::spin2_0_0::redis,
9+
wasi::http0_2_0::types::{IncomingRequest, OutgoingResponse, ResponseOutparam},
10+
};
11+
12+
impl bindings::Guest for Component {
2213
fn handle(request: IncomingRequest, response_out: ResponseOutparam) {
23-
let result = match handle(request) {
24-
Err(e) => response(500, format!("{e}").as_bytes()),
25-
Ok(r) => r,
26-
};
27-
ResponseOutparam::set(response_out, Ok(result))
14+
helper::handle_result(handle(request), response_out);
2815
}
2916
}
3017

3118
const REDIS_ADDRESS_HEADER: &str = "REDIS_ADDRESS";
3219

3320
fn handle(request: IncomingRequest) -> anyhow::Result<OutgoingResponse> {
34-
let Some(address) = request
35-
.headers()
36-
.get(&REDIS_ADDRESS_HEADER.to_owned())
37-
.pop()
38-
.and_then(|v| String::from_utf8(v).ok())
39-
else {
21+
let Some(address) = helper::get_header(request, &REDIS_ADDRESS_HEADER.to_owned()) else {
4022
// Otherwise, return a 400 Bad Request response.
41-
return Ok(response(400, b"Bad Request"));
23+
return Ok(helper::response(
24+
400,
25+
format!("missing header: {REDIS_ADDRESS_HEADER}").as_bytes(),
26+
));
4227
};
4328
let connection = redis::Connection::open(&address)?;
4429

@@ -87,21 +72,5 @@ fn handle(request: IncomingRequest) -> anyhow::Result<OutgoingResponse> {
8772
values.as_slice(),
8873
&[redis::RedisResult::Binary(ref b)] if b == b"Eureka! I've got it!"));
8974

90-
Ok(response(200, b""))
91-
}
92-
93-
fn response(status: u16, body: &[u8]) -> OutgoingResponse {
94-
let response = OutgoingResponse::new(Headers::new());
95-
response.set_status_code(status).unwrap();
96-
if !body.is_empty() {
97-
assert!(body.len() <= 4096);
98-
let outgoing_body = response.body().unwrap();
99-
{
100-
let outgoing_stream = outgoing_body.write().unwrap();
101-
outgoing_stream.blocking_write_and_flush(body).unwrap();
102-
// The outgoing stream must be dropped before the outgoing body is finished.
103-
}
104-
OutgoingBody::finish(outgoing_body, None).unwrap();
105-
}
106-
response
75+
Ok(helper::ok_response())
10776
}

components/outbound-wasi-http-v0.2.0/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,7 @@ edition = "2021"
77
crate-type = ["cdylib"]
88

99
[dependencies]
10+
anyhow = { workspace = true }
11+
helper = { workspace = true }
1012
url = "2.4.0"
1113
wit-bindgen = { workspace = true }

components/outbound-wasi-http-v0.2.0/src/lib.rs

+11-34
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,16 @@
1-
wit_bindgen::generate!({
2-
path: "../../wit",
3-
world: "wasi:http/proxy@0.2.0",
4-
});
5-
6-
use exports::wasi::http0_2_0::incoming_handler;
7-
use url::Url;
8-
use wasi::{
9-
http0_2_0::{
10-
outgoing_handler,
11-
types::{
12-
Headers, IncomingRequest, Method, OutgoingBody, OutgoingRequest, OutgoingResponse,
13-
ResponseOutparam, Scheme,
14-
},
15-
},
16-
io0_2_0::streams::StreamError,
1+
use helper::bindings::wasi::http0_2_0::outgoing_handler;
2+
use helper::bindings::wasi::http0_2_0::types::{
3+
Headers, IncomingRequest, Method, OutgoingBody, OutgoingRequest, OutgoingResponse,
4+
ResponseOutparam, Scheme,
175
};
6+
use helper::bindings::wasi::io0_2_0::streams::StreamError;
7+
use url::Url;
188

199
struct Component;
2010

21-
export!(Component);
11+
helper::gen_http_trigger_bindings!(Component);
2212

23-
impl incoming_handler::Guest for Component {
13+
impl bindings::Guest for Component {
2414
fn handle(request: IncomingRequest, outparam: ResponseOutparam) {
2515
// The request must have a "url" header.
2616
let Some(url) = request.headers().entries().iter().find_map(|(k, v)| {
@@ -30,7 +20,7 @@ impl incoming_handler::Guest for Component {
3020
.and_then(|v| Url::parse(v).ok())
3121
}) else {
3222
// Otherwise, return a 400 Bad Request response.
33-
return_response(outparam, 400, b"Bad Request");
23+
return_response(outparam, 400, b"missing header: url");
3424
return;
3525
};
3626

@@ -55,7 +45,7 @@ impl incoming_handler::Guest for Component {
5545
.unwrap();
5646

5747
// Write the request body.
58-
write_outgoing_body(outgoing_request.body().unwrap(), b"Hello, world!");
48+
helper::write_outgoing_body(outgoing_request.body().unwrap(), b"Hello, world!");
5949

6050
// Get the incoming response.
6151
let response = match outgoing_handler::handle(outgoing_request, None) {
@@ -103,20 +93,7 @@ impl incoming_handler::Guest for Component {
10393
}
10494
}
10595

106-
fn write_outgoing_body(outgoing_body: OutgoingBody, message: &[u8]) {
107-
assert!(message.len() <= 4096);
108-
{
109-
let outgoing_stream = outgoing_body.write().unwrap();
110-
outgoing_stream.blocking_write_and_flush(message).unwrap();
111-
// The outgoing stream must be dropped before the outgoing body is finished.
112-
}
113-
OutgoingBody::finish(outgoing_body, None).unwrap();
114-
}
115-
11696
fn return_response(outparam: ResponseOutparam, status: u16, body: &[u8]) {
117-
let response = OutgoingResponse::new(Headers::new());
118-
response.set_status_code(status).unwrap();
119-
write_outgoing_body(response.body().unwrap(), body);
120-
97+
let response = helper::response(status, body);
12198
ResponseOutparam::set(outparam, Ok(response));
12299
}

components/request-shape/Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ edition = "2021"
99
crate-type = ["cdylib"]
1010

1111
[dependencies]
12-
wit-bindgen = { workspace = true }
1312
anyhow = { workspace = true }
13+
helper = { workspace = true }
14+
wit-bindgen = { workspace = true }

components/request-shape/src/lib.rs

+8-22
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,24 @@
1-
use anyhow::Context;
1+
use anyhow::Context as _;
22
use std::collections::HashMap;
33

4-
mod bindings {
5-
wit_bindgen::generate!({
6-
world: "http-trigger",
7-
path: "../../wit",
8-
});
9-
use super::Component;
10-
export!(Component);
11-
}
12-
13-
use bindings::{
14-
exports::wasi::http0_2_0::incoming_handler::{Guest, IncomingRequest, ResponseOutparam},
15-
wasi::http0_2_0::types::{ErrorCode, Headers, OutgoingResponse},
4+
use helper::bindings::wasi::http0_2_0::types::{
5+
IncomingRequest, Method, OutgoingResponse, ResponseOutparam, Scheme,
166
};
177

18-
use crate::bindings::wasi::http0_2_0::types::{Method, Scheme};
19-
208
pub struct Component;
9+
helper::gen_http_trigger_bindings!(Component);
2110

22-
impl Guest for Component {
11+
impl bindings::Guest for Component {
2312
fn handle(request: IncomingRequest, response_out: ResponseOutparam) {
24-
let result = handle(request)
25-
.map(|_| OutgoingResponse::new(Headers::new()))
26-
.map_err(|e| ErrorCode::InternalError(Some(e.to_string())));
27-
ResponseOutparam::set(response_out, result)
13+
helper::handle_result(handle(request), response_out);
2814
}
2915
}
3016

31-
fn handle(req: IncomingRequest) -> anyhow::Result<()> {
17+
fn handle(req: IncomingRequest) -> anyhow::Result<OutgoingResponse> {
3218
check_method(&req)?;
3319
check_url(&req)?;
3420
check_headers(&req)?;
35-
Ok(())
21+
Ok(helper::ok_response())
3622
}
3723

3824
fn check_method(req: &IncomingRequest) -> anyhow::Result<()> {

components/sqlite/Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ version = "0.1.0"
44
edition = "2021"
55

66
[dependencies]
7-
wit-bindgen = { workspace = true }
87
anyhow = { workspace = true }
8+
helper = { workspace = true }
9+
wit-bindgen = { workspace = true }
910

1011
[lib]
1112
crate-type = ["cdylib"]

0 commit comments

Comments
 (0)