1use serde::{Deserialize, Deserializer, Serialize};
2use serde_json::Value;
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct AmiResponse<T> {
7 pub success: bool,
8 pub data: Option<T>,
9 pub error: Option<String>,
10}
11
12impl<T> AmiResponse<T> {
13 pub fn success(data: T) -> Self {
14 Self {
15 success: true,
16 data: Some(data),
17 error: None,
18 }
19 }
20
21 pub fn error(error: String) -> Self {
22 Self {
23 success: false,
24 data: None,
25 error: Some(error),
26 }
27 }
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
32pub struct AwsConfig {
33 pub region: String,
34 pub profile: Option<String>,
35 pub account_id: String,
36}
37
38impl Default for AwsConfig {
39 fn default() -> Self {
40 Self {
41 region: "us-east-1".to_string(),
42 profile: None,
43 account_id: Self::generate_account_id(),
44 }
45 }
46}
47
48impl AwsConfig {
49 pub fn generate_account_id() -> String {
51 use std::collections::hash_map::DefaultHasher;
52 use std::hash::{Hash, Hasher};
53
54 let mut hasher = DefaultHasher::new();
55 chrono::Utc::now()
56 .timestamp_nanos_opt()
57 .unwrap_or(0)
58 .hash(&mut hasher);
59 let hash = hasher.finish();
60
61 format!("{:012}", hash % 1_000_000_000_000)
63 }
64
65 pub fn with_account_id(account_id: String) -> Self {
67 Self {
68 region: "us-east-1".to_string(),
69 profile: None,
70 account_id,
71 }
72 }
73}
74
75#[derive(Debug, Clone, Serialize, Deserialize)]
77pub struct PaginationParams {
78 pub max_items: Option<i32>,
79 pub marker: Option<String>,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct Tag {
85 pub key: String,
86 pub value: String,
87}
88
89#[derive(Debug, Clone, Serialize, Deserialize)]
91pub struct PolicyDocument {
92 #[serde(rename = "Version")]
93 pub version: String,
94 #[serde(rename = "Statement")]
95 pub statement: Vec<PolicyStatement>,
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct PolicyStatement {
100 #[serde(rename = "Effect")]
101 pub effect: String,
102 #[serde(rename = "Action", deserialize_with = "string_or_vec")]
103 pub action: Vec<String>,
104 #[serde(rename = "Resource", deserialize_with = "string_or_vec")]
105 pub resource: Vec<String>,
106 #[serde(rename = "Condition", skip_serializing_if = "Option::is_none")]
107 pub condition: Option<Value>,
108}
109
110fn string_or_vec<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
112where
113 D: Deserializer<'de>,
114{
115 let value = Value::deserialize(deserializer)?;
116 match value {
117 Value::String(s) => Ok(vec![s]),
118 Value::Array(arr) => arr
119 .into_iter()
120 .map(|v| {
121 v.as_str()
122 .map(String::from)
123 .ok_or_else(|| serde::de::Error::custom("expected string"))
124 })
125 .collect(),
126 _ => Err(serde::de::Error::custom(
127 "expected string or array of strings",
128 )),
129 }
130}