Skip to main content

momento_functions_http/
invoke.rs

1use momento_functions_bytes::Data;
2use thiserror::Error;
3
4use crate::{request::Request, wit::momento::http::http};
5
6/// An error returned by an HTTP request.
7#[derive(Debug, Error)]
8pub enum HttpError {
9    /// An internal error occurred within Momento.
10    #[error("internal error")]
11    InternalError,
12    /// An error occurred while making the request.
13    #[error("request error: {0}")]
14    RequestError(String),
15    /// The provided URL was not valid.
16    #[error("invalid url '{url}': {error}")]
17    InvalidUrl { url: String, error: String },
18    /// A provided header name was not valid.
19    #[error("invalid header name '{header}': {error}")]
20    InvalidHeaderName { header: String, error: String },
21    /// A provided header value was not valid.
22    #[error("invalid header value '{value}': {error}")]
23    InvalidHeaderValue { value: String, error: String },
24}
25
26impl From<http::Error> for HttpError {
27    fn from(e: http::Error) -> Self {
28        match e {
29            http::Error::InternalError => HttpError::InternalError,
30            http::Error::RequestError(s) => HttpError::RequestError(s),
31            http::Error::InvalidUrl(u) => HttpError::InvalidUrl {
32                url: u.url,
33                error: u.error,
34            },
35            http::Error::InvalidHeaderName(h) => HttpError::InvalidHeaderName {
36                header: h.header,
37                error: h.error,
38            },
39            http::Error::InvalidHeaderValue(v) => HttpError::InvalidHeaderValue {
40                value: v.value,
41                error: v.error,
42            },
43        }
44    }
45}
46
47/// A response from an HTTP request.
48///
49/// The `body` is exposed as [`Data`] so bytes are only read when you need them.
50/// Call [`Data::into_bytes`] to fully materialize the body, or read it in
51/// chunks via the underlying buffer resource.
52pub struct Response {
53    /// The HTTP status code.
54    pub status: u16,
55    /// Response headers as name-value pairs.
56    pub headers: Vec<(String, String)>,
57    /// The response body. Read on demand to avoid unnecessary allocation.
58    pub body: Data,
59}
60
61impl From<http::Response> for Response {
62    fn from(r: http::Response) -> Self {
63        Response {
64            status: r.status,
65            headers: r.headers,
66            body: Data::from(r.body),
67        }
68    }
69}
70
71/// Send an HTTP request.
72///
73/// # Arguments
74/// * `request` - The request to send.
75///
76/// # Examples
77/// ________
78/// Send a GET request:
79/// ```rust,no_run
80/// use momento_functions_http::{invoke, Request};
81///
82/// match invoke(Request::new("https://example.com/api", "GET")) {
83///     Ok(response) => println!("status: {}", response.status),
84///     Err(e) => eprintln!("request failed: {e}"),
85/// }
86/// ```
87///
88/// Send a POST request with a JSON body:
89/// ```rust,no_run
90/// use momento_functions_http::{invoke, Request};
91///
92/// match invoke(
93///     Request::new("https://example.com/api", "POST")
94///         .with_header("Content-Type", "application/json")
95///         .with_body(b"{\"key\": \"value\"}".to_vec()),
96/// ) {
97///     Ok(response) => println!("status: {}", response.status),
98///     Err(e) => eprintln!("request failed: {e}"),
99/// }
100/// ```
101pub fn invoke(request: Request) -> Result<Response, HttpError> {
102    http::invoke(request.into())
103        .map(Into::into)
104        .map_err(Into::into)
105}