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}