momento_functions/macros/function_web.rs
1use momento_functions_host::encoding::Extract;
2use momento_functions_wit::function_web::exports::momento::functions::guest_function_web;
3
4use crate::response::IntoWebResponse;
5/// Create a handler that accepts a post payload and returns a response.
6///
7/// You can accept raw bytes (`Vec<u8>`) as input, or any type for which [Extract] is implemented.
8/// If you choose to use an extracted type, this will automatically return a 400 error containing
9/// the error details if the input bytes cannot be extracted into the specified input type.
10/// If you would rather handle extraction errors yourself, you should accept raw bytes as input
11/// and perform extraction yourself.
12///
13/// Your implementation function must return a value which implements the [IntoWebResponse] trait.
14/// Implementations of this trait are provided for
15/// - [WebResponse]: A basic response representation and builder
16/// - `WebResult<impl IntoWebResponse>`: Allows you to return results where errors will be converted
17/// to 500 responses.
18/// - [()]: Results in an empty 204.
19/// - [String] and [&str]: Results in a 200 with the string body.
20/// - `Vec<u8>` and `&[u8]`: Results in a 200 with the binary body.
21/// - [Json]: Results in a 200 with the Json body, or a 500 if the Json could not be serialized.
22///
23/// You may also implement [IntoWebResponse] for your own types.
24///
25/// **Raw Bytes Input:**
26/// ```rust
27/// use std::error::Error;///
28///
29/// use momento_functions::WebResponse;
30///
31/// momento_functions::post!(ping);
32/// fn ping(payload: Vec<u8>) -> &'static str {
33/// "pong"
34/// }
35/// ```
36///
37/// **Typed JSON Input:**
38/// ```rust
39/// use std::error::Error;
40/// use momento_functions::WebResponse;
41/// use momento_functions_host::encoding::Json;
42///
43/// #[derive(serde::Deserialize)]
44/// struct Request {
45/// name: String,
46/// }
47/// #[derive(serde::Serialize)]
48/// struct Response {
49/// message: String,
50/// }
51///
52/// momento_functions::post!(greet);
53/// fn greet(Json(request): Json<Request>) -> Json<Response> {
54/// Json(Response { message: format!("Hello, {}!", request.name) })
55/// }
56/// ```
57#[macro_export]
58macro_rules! post {
59 ($post_handler: ident) => {
60 struct WebFunction;
61 momento_functions_wit::__export_web_function_impl!(WebFunction);
62
63 #[automatically_derived]
64 impl momento_functions_wit::function_web::exports::momento::functions::guest_function_web::Guest for WebFunction {
65 fn post(payload: Vec<u8>) -> momento_functions_wit::function_web::exports::momento::functions::guest_function_web::Response {
66 momento_functions::post_template(payload, $post_handler)
67 }
68 }
69 };
70}
71
72/// An internal helper for the post! macro.
73#[doc(hidden)]
74pub fn post_template<TExtract, TResponse>(
75 payload: Vec<u8>,
76 handler: fn(request: TExtract) -> TResponse,
77) -> guest_function_web::Response
78where
79 TExtract: Extract,
80 TResponse: IntoWebResponse,
81{
82 let request = match TExtract::extract(payload) {
83 Ok(request) => request,
84 Err(error) => {
85 return guest_function_web::Response {
86 status: 400,
87 headers: vec![],
88 body: format!("Failed to parse request body: {error}")
89 .to_string()
90 .as_bytes()
91 .to_vec(),
92 };
93 }
94 };
95 handler(request).response()
96}