1pub mod batch_execute_statement;
2pub mod batch_get_item;
3pub mod batch_write_item;
4pub mod create_table;
5pub mod delete_item;
6pub mod delete_table;
7pub mod describe_stream;
8pub mod describe_table;
9pub mod describe_time_to_live;
10pub mod execute_statement;
11pub mod execute_transaction;
12pub mod get_item;
13pub mod get_records;
14pub mod get_shard_iterator;
15pub(crate) mod gsi;
16pub(crate) mod helpers;
17pub mod import_items;
18pub mod list_streams;
19pub mod list_tables;
20pub mod list_tags_of_resource;
21pub(crate) mod lsi;
22pub mod put_item;
23pub mod query;
24pub mod scan;
25pub mod tag_resource;
26pub mod transact_get_items;
27pub mod transact_write_items;
28pub mod untag_resource;
29pub mod update_item;
30pub mod update_table;
31pub mod update_time_to_live;
32
33use crate::types::{
34 AttributeDefinition, GlobalSecondaryIndex, KeySchemaElement, LocalSecondaryIndex, Projection,
35};
36use serde::{Deserialize, Serialize};
37
38#[derive(Debug, Clone, Default, Serialize, Deserialize)]
40pub struct TableDescription {
41 #[serde(rename = "TableName")]
42 pub table_name: String,
43
44 #[serde(rename = "TableId", skip_serializing_if = "Option::is_none")]
45 pub table_id: Option<String>,
46
47 #[serde(rename = "TableArn")]
48 pub table_arn: String,
49
50 #[serde(rename = "TableStatus")]
51 pub table_status: String,
52
53 #[serde(rename = "KeySchema")]
54 pub key_schema: Vec<KeySchemaElement>,
55
56 #[serde(rename = "AttributeDefinitions")]
57 pub attribute_definitions: Vec<AttributeDefinition>,
58
59 #[serde(rename = "CreationDateTime", skip_serializing_if = "Option::is_none")]
60 pub creation_date_time: Option<f64>,
61
62 #[serde(rename = "ItemCount", skip_serializing_if = "Option::is_none")]
63 pub item_count: Option<i64>,
64
65 #[serde(rename = "TableSizeBytes", skip_serializing_if = "Option::is_none")]
66 pub table_size_bytes: Option<i64>,
67
68 #[serde(
69 rename = "ProvisionedThroughput",
70 skip_serializing_if = "Option::is_none"
71 )]
72 pub provisioned_throughput: Option<TableProvisionedThroughputDescription>,
73
74 #[serde(rename = "BillingModeSummary", skip_serializing_if = "Option::is_none")]
75 pub billing_mode_summary: Option<BillingModeSummary>,
76
77 #[serde(
78 rename = "TableThroughputModeSummary",
79 skip_serializing_if = "Option::is_none"
80 )]
81 pub table_throughput_mode_summary: Option<TableThroughputModeSummary>,
82
83 #[serde(
84 rename = "GlobalSecondaryIndexes",
85 skip_serializing_if = "Option::is_none"
86 )]
87 pub global_secondary_indexes: Option<Vec<GlobalSecondaryIndexDescription>>,
88
89 #[serde(
90 rename = "LocalSecondaryIndexes",
91 skip_serializing_if = "Option::is_none"
92 )]
93 pub local_secondary_indexes: Option<Vec<LocalSecondaryIndexDescription>>,
94
95 #[serde(
96 rename = "StreamSpecification",
97 skip_serializing_if = "Option::is_none"
98 )]
99 pub stream_specification: Option<StreamSpecificationDescription>,
100
101 #[serde(rename = "LatestStreamArn", skip_serializing_if = "Option::is_none")]
102 pub latest_stream_arn: Option<String>,
103
104 #[serde(rename = "LatestStreamLabel", skip_serializing_if = "Option::is_none")]
105 pub latest_stream_label: Option<String>,
106
107 #[serde(rename = "SSEDescription", skip_serializing_if = "Option::is_none")]
108 pub sse_description: Option<SseDescription>,
109
110 #[serde(rename = "TableClassSummary", skip_serializing_if = "Option::is_none")]
111 pub table_class_summary: Option<TableClassSummary>,
112
113 #[serde(
114 rename = "DeletionProtectionEnabled",
115 skip_serializing_if = "Option::is_none"
116 )]
117 pub deletion_protection_enabled: Option<bool>,
118}
119
120#[derive(Debug, Clone, Default, Serialize, Deserialize)]
122pub struct SseDescription {
123 #[serde(rename = "Status")]
124 pub status: String,
125 #[serde(rename = "SSEType", skip_serializing_if = "Option::is_none")]
126 pub sse_type: Option<String>,
127 #[serde(rename = "KMSMasterKeyArn", skip_serializing_if = "Option::is_none")]
128 pub kms_master_key_arn: Option<String>,
129}
130
131#[derive(Debug, Clone, Default, Serialize, Deserialize)]
133pub struct StreamSpecificationDescription {
134 #[serde(rename = "StreamEnabled")]
135 pub stream_enabled: bool,
136 #[serde(rename = "StreamViewType", skip_serializing_if = "Option::is_none")]
137 pub stream_view_type: Option<String>,
138}
139
140#[derive(Debug, Clone, Default, Serialize, Deserialize)]
142pub struct TableClassSummary {
143 #[serde(rename = "TableClass")]
144 pub table_class: String,
145}
146
147#[derive(Debug, Clone, Default, Serialize, Deserialize)]
149pub struct TableProvisionedThroughputDescription {
150 #[serde(rename = "ReadCapacityUnits")]
151 pub read_capacity_units: u64,
152 #[serde(rename = "WriteCapacityUnits")]
153 pub write_capacity_units: u64,
154 #[serde(rename = "NumberOfDecreasesToday")]
155 pub number_of_decreases_today: u64,
156 #[serde(
157 rename = "LastIncreaseDateTime",
158 skip_serializing_if = "Option::is_none"
159 )]
160 pub last_increase_date_time: Option<f64>,
161 #[serde(
162 rename = "LastDecreaseDateTime",
163 skip_serializing_if = "Option::is_none"
164 )]
165 pub last_decrease_date_time: Option<f64>,
166}
167
168#[derive(Debug, Clone, Default, Serialize, Deserialize)]
170pub struct BillingModeSummary {
171 #[serde(rename = "BillingMode")]
172 pub billing_mode: String,
173 #[serde(
174 rename = "LastUpdateToPayPerRequestDateTime",
175 skip_serializing_if = "Option::is_none"
176 )]
177 pub last_update_to_pay_per_request_date_time: Option<f64>,
178}
179
180#[derive(Debug, Clone, Default, Serialize, Deserialize)]
182pub struct TableThroughputModeSummary {
183 #[serde(rename = "TableThroughputMode")]
184 pub table_throughput_mode: String,
185 #[serde(
186 rename = "LastUpdateToPayPerRequestDateTime",
187 skip_serializing_if = "Option::is_none"
188 )]
189 pub last_update_to_pay_per_request_date_time: Option<f64>,
190}
191
192#[derive(Debug, Clone, Default, Serialize, Deserialize)]
194pub struct GlobalSecondaryIndexDescription {
195 #[serde(rename = "IndexName")]
196 pub index_name: String,
197 #[serde(rename = "IndexArn")]
198 pub index_arn: String,
199 #[serde(rename = "KeySchema")]
200 pub key_schema: Vec<KeySchemaElement>,
201 #[serde(rename = "Projection")]
202 pub projection: Projection,
203 #[serde(rename = "IndexStatus")]
204 pub index_status: String,
205 #[serde(
206 rename = "ProvisionedThroughput",
207 skip_serializing_if = "Option::is_none"
208 )]
209 pub provisioned_throughput: Option<TableProvisionedThroughputDescription>,
210 #[serde(rename = "ItemCount", skip_serializing_if = "Option::is_none")]
211 pub item_count: Option<i64>,
212 #[serde(rename = "IndexSizeBytes", skip_serializing_if = "Option::is_none")]
213 pub index_size_bytes: Option<i64>,
214}
215
216#[derive(Debug, Clone, Default, Serialize, Deserialize)]
218pub struct LocalSecondaryIndexDescription {
219 #[serde(rename = "IndexName")]
220 pub index_name: String,
221 #[serde(rename = "IndexArn")]
222 pub index_arn: String,
223 #[serde(rename = "KeySchema")]
224 pub key_schema: Vec<KeySchemaElement>,
225 #[serde(rename = "Projection")]
226 pub projection: Projection,
227 #[serde(rename = "ItemCount", skip_serializing_if = "Option::is_none")]
228 pub item_count: Option<i64>,
229 #[serde(rename = "IndexSizeBytes", skip_serializing_if = "Option::is_none")]
230 pub index_size_bytes: Option<i64>,
231}
232
233fn generate_table_id() -> String {
235 uuid::Uuid::new_v4().to_string()
236}
237
238pub(crate) fn build_table_description(
240 meta: &crate::storage::TableMetadata,
241 item_count: Option<i64>,
242 table_size_bytes: Option<i64>,
243) -> TableDescription {
244 use crate::streams;
245
246 let key_schema: Vec<KeySchemaElement> =
247 serde_json::from_str(&meta.key_schema).unwrap_or_default();
248 let attribute_definitions: Vec<AttributeDefinition> =
249 serde_json::from_str(&meta.attribute_definitions).unwrap_or_default();
250
251 let gsi_definitions: Option<Vec<GlobalSecondaryIndex>> = meta
252 .gsi_definitions
253 .as_ref()
254 .and_then(|s| serde_json::from_str(s).ok());
255
256 let table_name = &meta.table_name;
257
258 let global_secondary_indexes = gsi_definitions.map(|gsis| {
259 gsis.into_iter()
260 .map(|gsi| {
261 let idx_arn = streams::index_arn(table_name, &gsi.index_name);
262 GlobalSecondaryIndexDescription {
263 index_name: gsi.index_name,
264 index_arn: idx_arn,
265 key_schema: gsi.key_schema,
266 projection: gsi.projection,
267 index_status: "ACTIVE".to_string(),
268 provisioned_throughput: Some(if let Some(pt) = gsi.provisioned_throughput {
269 TableProvisionedThroughputDescription {
270 read_capacity_units: pt.read_capacity_units.unwrap_or(0) as u64,
271 write_capacity_units: pt.write_capacity_units.unwrap_or(0) as u64,
272 number_of_decreases_today: 0,
273 last_increase_date_time: None,
274 last_decrease_date_time: None,
275 }
276 } else {
277 TableProvisionedThroughputDescription {
279 read_capacity_units: 0,
280 write_capacity_units: 0,
281 number_of_decreases_today: 0,
282 last_increase_date_time: None,
283 last_decrease_date_time: None,
284 }
285 }),
286 item_count: Some(0),
287 index_size_bytes: Some(0),
288 }
289 })
290 .collect()
291 });
292
293 let lsi_definitions: Option<Vec<LocalSecondaryIndex>> = meta
294 .lsi_definitions
295 .as_ref()
296 .and_then(|s| serde_json::from_str(s).ok());
297
298 let local_secondary_indexes = lsi_definitions.map(|lsis| {
299 lsis.into_iter()
300 .map(|lsi| {
301 let idx_arn = streams::index_arn(table_name, &lsi.index_name);
302 LocalSecondaryIndexDescription {
303 index_name: lsi.index_name,
304 index_arn: idx_arn,
305 key_schema: lsi.key_schema,
306 projection: lsi.projection,
307 item_count: Some(0),
308 index_size_bytes: Some(0),
309 }
310 })
311 .collect()
312 });
313
314 let billing_mode = meta.billing_mode.clone();
315
316 let provisioned_throughput = if let Some(pt_json) = &meta.provisioned_throughput {
317 serde_json::from_str::<serde_json::Value>(pt_json)
319 .ok()
320 .map(|v| {
321 let rcu = v
322 .get("ReadCapacityUnits")
323 .and_then(|v| v.as_i64())
324 .unwrap_or(0) as u64;
325 let wcu = v
326 .get("WriteCapacityUnits")
327 .and_then(|v| v.as_i64())
328 .unwrap_or(0) as u64;
329 let last_inc = v.get("LastIncreaseDateTime").and_then(|v| v.as_f64());
330 let last_dec = v.get("LastDecreaseDateTime").and_then(|v| v.as_f64());
331 let num_dec = v
332 .get("NumberOfDecreasesToday")
333 .and_then(|v| v.as_u64())
334 .unwrap_or(0);
335 TableProvisionedThroughputDescription {
336 read_capacity_units: rcu,
337 write_capacity_units: wcu,
338 number_of_decreases_today: num_dec,
339 last_increase_date_time: last_inc,
340 last_decrease_date_time: last_dec,
341 }
342 })
343 } else if billing_mode.as_deref() == Some("PAY_PER_REQUEST") {
344 Some(TableProvisionedThroughputDescription {
346 read_capacity_units: 0,
347 write_capacity_units: 0,
348 number_of_decreases_today: 0,
349 last_increase_date_time: None,
350 last_decrease_date_time: None,
351 })
352 } else {
353 None
354 };
355
356 let stream_specification = if meta.stream_enabled {
357 Some(StreamSpecificationDescription {
358 stream_enabled: true,
359 stream_view_type: meta.stream_view_type.clone(),
360 })
361 } else {
362 None
363 };
364
365 let latest_stream_arn = if meta.stream_enabled {
366 meta.stream_label
367 .as_ref()
368 .map(|label| streams::stream_arn(table_name, label))
369 } else {
370 None
371 };
372
373 let latest_stream_label = if meta.stream_enabled {
374 meta.stream_label.clone()
375 } else {
376 None
377 };
378
379 let sse_description = meta.sse_specification.as_ref().and_then(|json| {
381 serde_json::from_str::<crate::types::SseSpecification>(json)
382 .ok()
383 .map(|spec| SseDescription {
384 status: if spec.enabled.unwrap_or(false) {
385 "ENABLED".to_string()
386 } else {
387 "DISABLED".to_string()
388 },
389 sse_type: spec.sse_type,
390 kms_master_key_arn: spec.kms_master_key_id,
391 })
392 });
393
394 let table_class_summary = meta.table_class.as_ref().map(|tc| TableClassSummary {
395 table_class: tc.clone(),
396 });
397
398 let deletion_protection_enabled = Some(meta.deletion_protection_enabled);
399
400 TableDescription {
401 table_name: meta.table_name.clone(),
402 table_id: Some(generate_table_id()),
403 table_arn: streams::table_arn(table_name),
404 table_status: meta.table_status.clone(),
405 key_schema,
406 attribute_definitions,
407 creation_date_time: Some(meta.created_at as f64),
408 item_count,
409 table_size_bytes,
410 provisioned_throughput,
411 billing_mode_summary: match billing_mode.as_deref() {
412 Some("PAY_PER_REQUEST") => Some(BillingModeSummary {
413 billing_mode: "PAY_PER_REQUEST".to_string(),
414 last_update_to_pay_per_request_date_time: None,
415 }),
416 _ => None,
417 },
418 table_throughput_mode_summary: match billing_mode.as_deref() {
419 Some("PAY_PER_REQUEST") => Some(TableThroughputModeSummary {
420 table_throughput_mode: "PAY_PER_REQUEST".to_string(),
421 last_update_to_pay_per_request_date_time: None,
422 }),
423 _ => None,
424 },
425 global_secondary_indexes,
426 local_secondary_indexes,
427 stream_specification,
428 latest_stream_arn,
429 latest_stream_label,
430 sse_description,
431 table_class_summary,
432 deletion_protection_enabled,
433 }
434}