#![deny(clippy::all, clippy::pedantic, clippy::nursery)]
#![deny(
missing_docs,
missing_debug_implementations,
missing_copy_implementations,
trivial_casts,
trivial_numeric_casts,
unsafe_code,
unstable_features,
unused_import_braces,
unused_qualifications
)]
#![allow(clippy::must_use_candidate, clippy::used_underscore_binding)]
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use std::collections::HashMap;
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub struct CloudformationPayload<T> {
pub request_id: String,
#[serde(rename = "ResponseURL")]
pub response_url: String,
pub resource_type: String,
pub logical_resource_id: String,
pub stack_id: String,
pub physical_resource_id: Option<Uuid>,
pub resource_properties: T,
}
impl<T> CloudformationPayload<T> {
pub fn respond_with_success(self, reason: &str) -> CustomResourceResponse {
CustomResourceResponse {
status: ResponseType::Success,
reason: reason.to_owned(),
physical_resource_id: self.physical_resource_id.unwrap_or_else(Uuid::new_v4),
stack_id: self.stack_id,
request_id: self.request_id,
logical_resource_id: self.logical_resource_id,
response_url: self.response_url,
no_echo: None,
data: None,
}
}
pub fn respond_with_failure(self, reason: &str) -> CustomResourceResponse {
CustomResourceResponse {
status: ResponseType::Failed,
reason: reason.to_owned(),
physical_resource_id: self.physical_resource_id.unwrap_or_else(Uuid::new_v4),
stack_id: self.stack_id,
request_id: self.request_id,
logical_resource_id: self.logical_resource_id,
response_url: self.response_url,
no_echo: None,
data: None,
}
}
pub fn respond_with<D, E>(self, result: Result<D, E>) -> CustomResourceResponse
where
E: std::error::Error,
{
match result {
Ok(_) => self.respond_with_success("Success"),
Err(e) => self.respond_with_failure(&format!("{:?}", e)),
}
}
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(tag = "RequestType")]
pub enum CustomResourceEvent<T> {
Create(CloudformationPayload<T>),
Update(CloudformationPayload<T>),
Delete(CloudformationPayload<T>),
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "UPPERCASE")]
enum ResponseType {
Success,
Failed,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "PascalCase")]
pub struct CustomResourceResponse {
status: ResponseType,
reason: String,
physical_resource_id: Uuid,
stack_id: String,
request_id: String,
logical_resource_id: String,
no_echo: Option<bool>,
data: Option<HashMap<String, String>>,
#[serde(skip)]
response_url: String,
}
impl CustomResourceResponse {
pub fn set_no_echo(mut self, value: bool) -> Self {
self.no_echo = Some(value);
self
}
pub fn add_data(mut self, key: &str, value: &str) -> Self {
if self.data.is_none() {
self.data = Some(HashMap::new());
}
self.data
.as_mut()
.unwrap()
.insert(key.to_owned(), value.to_owned());
self
}
pub async fn finish(self) -> Result<(), reqwest::Error> {
let client = reqwest::Client::new();
client.put(&self.response_url).json(&self).send().await?;
Ok(())
}
}