assemblylift_awslambda_guest/
lib.rs

1extern crate assemblylift_core_guest;
2extern crate assemblylift_core_io_guest;
3
4use std::collections::HashMap;
5use std::fmt;
6use std::fmt::Debug;
7
8use serde::{Deserialize, Serialize};
9
10use assemblylift_core_guest::*;
11
12extern "C" {
13    fn __asml_abi_console_log(ptr: *const u8, len: usize);
14    fn __asml_abi_success(ptr: *const u8, len: usize);
15}
16
17pub struct AwsLambdaClient(Guest);
18
19impl AwsLambdaClient {
20    pub fn new() -> AwsLambdaClient {
21        AwsLambdaClient { 0: Guest {} }
22    }
23}
24
25impl GuestCore for AwsLambdaClient {
26    fn console_log(message: String) {
27        unsafe { __asml_abi_console_log(message.as_ptr(), message.len()) }
28    }
29
30    fn success(response: String) {
31        unsafe { __asml_abi_success(response.as_ptr(), response.len()) }
32    }
33}
34
35#[derive(Serialize, Deserialize, Clone, Debug)]
36pub struct ApiGatewayEvent {
37    pub resource: String,
38    pub path: String,
39    #[serde(rename = "httpMethod")]
40    pub http_method: String,
41    pub headers: HashMap<String, String>,
42    #[serde(rename = "queryStringParameters")]
43    pub query_string_parameters: Option<HashMap<String, String>>,
44    #[serde(rename = "pathParameters")]
45    pub path_parameters: Option<HashMap<String, String>>,
46    #[serde(rename = "stageVariables")]
47    pub stage_variables: Option<HashMap<String, String>>,
48    #[serde(rename = "requestContext")]
49    pub request_context: Option<ApiGatewayRequestContext>,
50    pub body: Option<String>,
51}
52
53pub type StatusCode = u16;
54#[derive(Serialize, Deserialize)]
55pub struct ApiGatewayResponse {
56    #[serde(rename = "isBase64Encoded")]
57    is_base64_encoded: bool,
58    #[serde(rename = "statusCode")]
59    status_code: StatusCode,
60    headers: HashMap<String, String>,
61    body: String,
62}
63
64#[derive(Serialize, Deserialize)]
65pub struct ApiGatewayError {
66    pub code: StatusCode,
67    pub desc: String,
68    pub message: String,
69}
70
71#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
72pub enum ApiGatewayErrorCode {
73    NotFound = 404,
74    FunctionError = 520,
75}
76
77#[derive(Serialize, Deserialize, Clone, Debug)]
78pub struct ApiGatewayRequestContext {
79    pub authorizer: Option<ApiGatewayRequestContextAuthorizer>,
80    pub identity: Option<ApiGatewayRequestContextIdentity>,
81}
82
83#[derive(Serialize, Deserialize, Clone, Debug)]
84pub struct ApiGatewayRequestContextAuthorizer {
85    pub claims: Option<HashMap<String, String>>,
86    pub scopes: Option<Vec<String>>,
87}
88
89#[derive(Serialize, Deserialize, Clone, Debug)]
90pub struct ApiGatewayRequestContextIdentity {
91    #[serde(rename = "accessKey")]
92    pub access_key: Option<String>,
93    #[serde(rename = "accountId")]
94    pub account_id: Option<String>,
95    pub caller: Option<String>,
96    #[serde(rename = "cognitoAmr")]
97    pub cognito_amr: Option<String>,
98    #[serde(rename = "cognitoAuthenticationProvider")]
99    pub cognito_authentication_provider: Option<String>,
100    #[serde(rename = "cognitoAuthenticationType")]
101    pub cognito_authentication_type: Option<String>,
102    #[serde(rename = "cognitoIdentityId")]
103    pub cognito_identity_id: Option<String>,
104    #[serde(rename = "cognitoIdentityPoolId")]
105    pub cognito_identity_pool_id: Option<String>,
106    #[serde(rename = "principalOrgId")]
107    pub principal_org_id: Option<String>,
108    #[serde(rename = "sourceIp")]
109    pub source_ip: String,
110    pub user: Option<String>,
111    #[serde(rename = "userAgent")]
112    pub user_agent: Option<String>,
113    #[serde(rename = "userArn")]
114    pub user_arn: Option<String>,
115}
116
117impl fmt::Display for ApiGatewayErrorCode {
118    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
119        match self {
120            ApiGatewayErrorCode::NotFound => write!(f, "Missing Resource"),
121            ApiGatewayErrorCode::FunctionError => write!(f, "Function Error"),
122        }
123    }
124}
125
126impl ApiGatewayResponse {
127    pub fn ok(body: String, content_type: Option<String>, is_base64_encoded: bool, gzip: bool) -> Self {
128        let mut headers = HashMap::default();
129        headers.insert(
130            "content-type".to_string(),
131            content_type.unwrap_or_else(|| String::from("application/json")),
132        );
133        if gzip {
134            headers.insert(
135                "content-encoding".to_string(),
136                "gzip".to_string(),
137            );
138        }
139
140        Self {
141            status_code: 200,
142            is_base64_encoded,
143            headers,
144            body,
145        }
146    }
147
148    pub fn error(message: String, code: ApiGatewayErrorCode) -> Self {
149        let mut headers = HashMap::default();
150        headers.insert(
151            String::from("content-type"),
152            String::from("application/json"),
153        );
154
155        Self {
156            status_code: code as StatusCode,
157            is_base64_encoded: false,
158            headers,
159            body: serde_json::to_string(&ApiGatewayError {
160                code: code as StatusCode,
161                desc: code.to_string(),
162                message,
163            })
164            .unwrap(),
165        }
166    }
167}
168
169pub struct LambdaContext<'a, E: Serialize + Deserialize<'a> + Clone + Debug> {
170    pub client: AwsLambdaClient,
171    pub event: E,
172    pub _phantom: std::marker::PhantomData<&'a E>,
173}
174
175#[macro_export]
176macro_rules! handler {
177    ($context:ident: $type:ty, $async_handler:expr) => {
178        #[no_mangle]
179        pub fn handler() -> i32 {
180            use assemblylift_core_io_guest;
181            use assemblylift_core_io_guest::get_time;
182            use direct_executor;
183
184            let client = AwsLambdaClient::new();
185            let mut fib = std::io::BufReader::new(assemblylift_core_io_guest::FunctionInputBuffer::new());
186
187            let event = match serde_json::from_reader(fib) {
188                Ok(event) => event,
189                Err(why) => {
190                    AwsLambdaClient::console_log(format!(
191                        "ERROR deserializing Lambda Event: {}",
192                        why.to_string()
193                    ));
194                    return -1;
195                }
196            };
197
198            let $context: $type = LambdaContext { client, event, _phantom: std::marker::PhantomData };
199
200            direct_executor::run_spinning($async_handler);
201
202            0
203        }
204    };
205}
206
207#[macro_export]
208macro_rules! http_ok {
209    ($response:ident) => {
210        AwsLambdaClient::success(
211            serde_json::to_string(&ApiGatewayResponse::ok(
212                serde_json::to_string(&$response).unwrap(),
213                None,
214                false,
215                false,
216            ))
217            .unwrap(),
218        );
219    };
220
221    ($response:expr, $type:expr, $isb64:expr, $isgzip:expr) => {
222        AwsLambdaClient::success(
223            serde_json::to_string(&ApiGatewayResponse::ok(
224                $response,
225                $type,
226                $isb64,
227                $isgzip,
228            ))
229            .unwrap(),
230        );
231    };
232}
233
234#[macro_export]
235macro_rules! http_error {
236    ($message:expr) => {
237        AwsLambdaClient::success(
238            serde_json::to_string(&ApiGatewayResponse::error(
239                $message,
240                ApiGatewayErrorCode::FunctionError,
241            ))
242            .unwrap(),
243        );
244    };
245}
246
247#[macro_export]
248macro_rules! http_not_found {
249    ($resource_name:expr) => {
250        AwsLambdaClient::success(
251            serde_json::to_string(&ApiGatewayResponse::error(
252                format!("missing resource {:?}", $resource_name),
253                ApiGatewayErrorCode::NotFound,
254            ))
255            .unwrap(),
256        );
257    };
258}