1use std::collections::BTreeMap;
4use std::sync::Arc;
5
6use chrono::{DateTime, Utc};
7use parking_lot::RwLock;
8use rand::Rng;
9use serde::{Deserialize, Serialize};
10
11use fakecloud_aws::arn::Arn;
12use fakecloud_core::multi_account::{AccountState, MultiAccountState};
13
14pub const DSQL_SNAPSHOT_SCHEMA_VERSION: u32 = 1;
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct Cluster {
20 pub identifier: String,
21 pub arn: String,
22 pub status: String,
25 pub creation_time: DateTime<Utc>,
26 pub deletion_protection_enabled: bool,
27 pub multi_region_properties: Option<MultiRegionProperties>,
28 pub encryption_details: EncryptionDetails,
29 pub endpoint: String,
31 pub tags: BTreeMap<String, String>,
32 pub policy: Option<String>,
35 pub policy_version: u64,
36 #[serde(default)]
38 pub client_token: Option<String>,
39 #[serde(default)]
42 pub deleted_at: Option<DateTime<Utc>>,
43 #[serde(default)]
45 pub streams: BTreeMap<String, Stream>,
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct MultiRegionProperties {
50 pub witness_region: Option<String>,
51 pub clusters: Vec<String>,
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct EncryptionDetails {
56 pub encryption_type: String,
58 pub kms_key_arn: Option<String>,
59 pub encryption_status: String,
61}
62
63#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct Stream {
65 pub cluster_identifier: String,
66 pub stream_identifier: String,
67 pub arn: String,
68 pub status: String,
70 pub creation_time: DateTime<Utc>,
71 pub ordering: String,
73 pub format: String,
75 pub target_definition: serde_json::Value,
77 pub status_reason: Option<String>,
78 pub tags: BTreeMap<String, String>,
79 #[serde(default)]
80 pub client_token: Option<String>,
81 #[serde(default)]
84 pub deleted_at: Option<DateTime<Utc>>,
85}
86
87#[derive(Debug, Clone, Serialize, Deserialize)]
89pub struct DsqlState {
90 pub account_id: String,
91 pub region: String,
92 pub clusters: BTreeMap<String, Cluster>,
93}
94
95impl DsqlState {
96 pub fn new(account_id: &str, region: &str) -> Self {
97 Self {
98 account_id: account_id.to_string(),
99 region: region.to_string(),
100 clusters: BTreeMap::new(),
101 }
102 }
103}
104
105impl AccountState for DsqlState {
106 fn new_for_account(account_id: &str, region: &str, _endpoint: &str) -> Self {
107 Self::new(account_id, region)
108 }
109}
110
111pub type SharedDsqlState = Arc<RwLock<MultiAccountState<DsqlState>>>;
112
113#[derive(Debug, Serialize, Deserialize)]
114pub struct DsqlSnapshot {
115 pub schema_version: u32,
116 pub accounts: MultiAccountState<DsqlState>,
117}
118
119pub fn gen_id() -> String {
122 const CHARS: &[u8] = b"abcdefghijklmnopqrstuvwxyz0123456789";
123 let mut rng = rand::thread_rng();
124 (0..26)
125 .map(|_| CHARS[rng.gen_range(0..CHARS.len())] as char)
126 .collect()
127}
128
129pub fn cluster_arn(region: &str, account_id: &str, id: &str) -> String {
131 Arn::new("dsql", region, account_id, &format!("cluster/{id}")).to_string()
132}
133
134pub fn stream_arn(region: &str, account_id: &str, cluster: &str, stream: &str) -> String {
137 Arn::new(
138 "dsql",
139 region,
140 account_id,
141 &format!("cluster/{cluster}/stream/{stream}"),
142 )
143 .to_string()
144}
145
146pub fn cluster_endpoint(id: &str, region: &str) -> String {
148 format!("{id}.dsql.{region}.on.aws")
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154
155 #[test]
156 fn gen_id_matches_aws_pattern() {
157 let id = gen_id();
158 assert_eq!(id.len(), 26);
159 assert!(id
160 .chars()
161 .all(|c| c.is_ascii_lowercase() || c.is_ascii_digit()));
162 }
163
164 #[test]
165 fn arns_have_expected_shape() {
166 let id = "abcdefghij0123456789klmnop";
167 let c = cluster_arn("us-east-1", "123456789012", id);
168 assert_eq!(
169 c,
170 format!("arn:aws:dsql:us-east-1:123456789012:cluster/{id}")
171 );
172 let s = stream_arn("us-east-1", "123456789012", id, id);
173 assert_eq!(
174 s,
175 format!("arn:aws:dsql:us-east-1:123456789012:cluster/{id}/stream/{id}")
176 );
177 }
178
179 #[test]
180 fn endpoint_shape() {
181 assert_eq!(
182 cluster_endpoint("abcdefghij0123456789klmnop", "us-west-2"),
183 "abcdefghij0123456789klmnop.dsql.us-west-2.on.aws"
184 );
185 }
186}