1use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64};
2use serde::de;
3use serde::ser::SerializeMap;
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5use std::collections::{BTreeSet, HashMap, HashSet};
6use std::fmt;
7
8#[derive(Debug, Clone, PartialEq)]
15pub enum AttributeValue {
16 S(String),
18 N(String),
20 B(Vec<u8>),
22 BOOL(bool),
24 NULL(bool),
26 SS(Vec<String>),
28 NS(Vec<String>),
30 BS(Vec<Vec<u8>>),
32 L(Vec<AttributeValue>),
34 M(HashMap<String, AttributeValue>),
36}
37
38impl AttributeValue {
39 pub fn size(&self) -> usize {
45 match self {
46 AttributeValue::S(s) => s.len(),
47 AttributeValue::N(n) => {
48 let significant = n.chars().filter(|c| c.is_ascii_digit()).count();
50 let significant = significant.max(1);
51 (significant / 2) + 1
52 }
53 AttributeValue::B(b) => b.len(),
54 AttributeValue::BOOL(_) => 1,
55 AttributeValue::NULL(_) => 1,
56 AttributeValue::SS(ss) => ss.iter().map(|s| s.len()).sum(),
57 AttributeValue::NS(ns) => ns
58 .iter()
59 .map(|n| {
60 let significant = n.chars().filter(|c| c.is_ascii_digit()).count().max(1);
61 (significant / 2) + 1
62 })
63 .sum(),
64 AttributeValue::BS(bs) => bs.iter().map(|b| b.len()).sum(),
65 AttributeValue::L(items) => {
66 3 + items.len() + items.iter().map(|v| v.size()).sum::<usize>()
68 }
69 AttributeValue::M(map) => {
70 3 + map
72 .iter()
73 .map(|(k, v)| k.len() + 1 + v.size())
74 .sum::<usize>()
75 }
76 }
77 }
78
79 pub fn type_name(&self) -> &'static str {
81 match self {
82 AttributeValue::S(_) => "S",
83 AttributeValue::N(_) => "N",
84 AttributeValue::B(_) => "B",
85 AttributeValue::BOOL(_) => "BOOL",
86 AttributeValue::NULL(_) => "NULL",
87 AttributeValue::SS(_) => "SS",
88 AttributeValue::NS(_) => "NS",
89 AttributeValue::BS(_) => "BS",
90 AttributeValue::L(_) => "L",
91 AttributeValue::M(_) => "M",
92 }
93 }
94
95 pub fn is_scalar(&self) -> bool {
97 matches!(
98 self,
99 AttributeValue::S(_)
100 | AttributeValue::N(_)
101 | AttributeValue::B(_)
102 | AttributeValue::BOOL(_)
103 | AttributeValue::NULL(_)
104 )
105 }
106
107 pub fn is_set(&self) -> bool {
109 matches!(
110 self,
111 AttributeValue::SS(_) | AttributeValue::NS(_) | AttributeValue::BS(_)
112 )
113 }
114
115 pub fn to_key_string(&self) -> Option<String> {
122 match self {
123 AttributeValue::S(s) => Some(format!("S:{s}")),
124 AttributeValue::N(n) => Some(format!("N:{}", normalize_number_for_sort(n))),
125 AttributeValue::B(b) => Some(format!("B:{}", hex_encode(b))),
126 _ => None, }
128 }
129}
130
131impl fmt::Display for AttributeValue {
132 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133 match self {
134 AttributeValue::S(s) => write!(f, "\"{s}\""),
135 AttributeValue::N(n) => write!(f, "{n}"),
136 AttributeValue::B(b) => write!(f, "<binary {} bytes>", b.len()),
137 AttributeValue::BOOL(b) => write!(f, "{b}"),
138 AttributeValue::NULL(_) => write!(f, "null"),
139 AttributeValue::SS(ss) => write!(f, "{ss:?}"),
140 AttributeValue::NS(ns) => write!(f, "{ns:?}"),
141 AttributeValue::BS(bs) => write!(f, "<binary set {} items>", bs.len()),
142 AttributeValue::L(items) => write!(f, "<list {} items>", items.len()),
143 AttributeValue::M(map) => write!(f, "<map {} keys>", map.len()),
144 }
145 }
146}
147
148impl Serialize for AttributeValue {
153 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
154 where
155 S: Serializer,
156 {
157 let mut map = serializer.serialize_map(Some(1))?;
158 match self {
159 AttributeValue::S(s) => map.serialize_entry("S", s)?,
160 AttributeValue::N(n) => map.serialize_entry("N", n)?,
161 AttributeValue::B(b) => {
162 map.serialize_entry("B", &BASE64.encode(b))?;
163 }
164 AttributeValue::BOOL(b) => map.serialize_entry("BOOL", b)?,
165 AttributeValue::NULL(n) => map.serialize_entry("NULL", n)?,
166 AttributeValue::SS(ss) => map.serialize_entry("SS", ss)?,
167 AttributeValue::NS(ns) => map.serialize_entry("NS", ns)?,
168 AttributeValue::BS(bs) => {
169 let encoded: Vec<String> = bs.iter().map(|b| BASE64.encode(b)).collect();
170 map.serialize_entry("BS", &encoded)?;
171 }
172 AttributeValue::L(items) => map.serialize_entry("L", items)?,
173 AttributeValue::M(m) => map.serialize_entry("M", m)?,
174 }
175 map.end()
176 }
177}
178
179impl<'de> Deserialize<'de> for AttributeValue {
180 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
181 where
182 D: Deserializer<'de>,
183 {
184 let raw = serde_json::Value::deserialize(deserializer)?;
186
187 let obj = raw
188 .as_object()
189 .ok_or_else(|| de::Error::custom("empty AttributeValue object"))?;
190
191 if obj.is_empty() {
192 return Err(de::Error::custom("empty AttributeValue object"));
193 }
194
195 let known_types = ["S", "N", "B", "BOOL", "NULL", "SS", "NS", "BS", "L", "M"];
197 let present: Vec<&str> = obj
198 .keys()
199 .filter(|k| known_types.contains(&k.as_str()))
200 .map(|k| k.as_str())
201 .collect();
202
203 if present.is_empty() {
204 return Err(de::Error::custom(
205 "Supplied AttributeValue is empty, must contain exactly one of the supported datatypes",
206 ));
207 }
208
209 for &type_key in &present {
212 match type_key {
213 "N" => {
214 if let Some(n) = obj.get("N").and_then(|v| v.as_str()) {
215 validate_number_in_deser(n).map_err(de::Error::custom)?;
216 }
217 }
218 "NS" => {
219 if let Some(arr) = obj.get("NS").and_then(|v| v.as_array()) {
220 for item in arr {
221 if let Some(n) = item.as_str() {
222 validate_number_in_deser(n).map_err(de::Error::custom)?;
223 }
224 }
225 }
226 }
227 _ => {}
228 }
229 }
230
231 if present.len() > 1 {
233 return Err(de::Error::custom(
234 "VALIDATION:Supplied AttributeValue has more than one datatypes set, \
235 must contain exactly one of the supported datatypes",
236 ));
237 }
238
239 let type_key = present[0];
240 let val = &obj[type_key];
241
242 match type_key {
243 "S" => {
244 let s = val
245 .as_str()
246 .ok_or_else(|| de::Error::custom("expected string for S"))?;
247 Ok(AttributeValue::S(s.to_string()))
248 }
249 "N" => {
250 let n = val
251 .as_str()
252 .ok_or_else(|| de::Error::custom("expected string for N"))?;
253 Ok(AttributeValue::N(n.to_string()))
254 }
255 "B" => {
256 let encoded = val
257 .as_str()
258 .ok_or_else(|| de::Error::custom("expected string for B"))?;
259 let bytes = BASE64
260 .decode(encoded)
261 .map_err(|e| de::Error::custom(format!("invalid base64: {e}")))?;
262 Ok(AttributeValue::B(bytes))
263 }
264 "BOOL" => {
265 let b = val
266 .as_bool()
267 .ok_or_else(|| de::Error::custom("expected boolean for BOOL"))?;
268 Ok(AttributeValue::BOOL(b))
269 }
270 "NULL" => {
271 let n = val.as_bool().unwrap_or(false);
274 Ok(AttributeValue::NULL(n))
275 }
276 "SS" => {
277 let arr = val
278 .as_array()
279 .ok_or_else(|| de::Error::custom("expected array for SS"))?;
280 let ss: Result<Vec<String>, _> = arr
281 .iter()
282 .map(|v| {
283 v.as_str()
284 .map(|s| s.to_string())
285 .ok_or_else(|| de::Error::custom("expected string in SS"))
286 })
287 .collect();
288 Ok(AttributeValue::SS(ss?))
289 }
290 "NS" => {
291 let arr = val
292 .as_array()
293 .ok_or_else(|| de::Error::custom("expected array for NS"))?;
294 let ns: Result<Vec<String>, _> = arr
295 .iter()
296 .map(|v| {
297 v.as_str()
298 .map(|s| s.to_string())
299 .ok_or_else(|| de::Error::custom("expected string in NS"))
300 })
301 .collect();
302 Ok(AttributeValue::NS(ns?))
303 }
304 "BS" => {
305 let arr = val
306 .as_array()
307 .ok_or_else(|| de::Error::custom("expected array for BS"))?;
308 let mut decoded = Vec::with_capacity(arr.len());
309 for item in arr {
310 let encoded = item
311 .as_str()
312 .ok_or_else(|| de::Error::custom("expected string in BS"))?;
313 decoded.push(
314 BASE64
315 .decode(encoded)
316 .map_err(|e| de::Error::custom(format!("invalid base64: {e}")))?,
317 );
318 }
319 Ok(AttributeValue::BS(decoded))
320 }
321 "L" => {
322 let arr = val
323 .as_array()
324 .ok_or_else(|| de::Error::custom("expected array for L"))?;
325 let list: Result<Vec<AttributeValue>, _> = arr
326 .iter()
327 .map(|v| serde_json::from_value(v.clone()).map_err(de::Error::custom))
328 .collect();
329 Ok(AttributeValue::L(list?))
330 }
331 "M" => {
332 let map_val = val
333 .as_object()
334 .ok_or_else(|| de::Error::custom("expected object for M"))?;
335 let mut result = std::collections::HashMap::new();
336 for (k, v) in map_val {
337 let av: AttributeValue =
338 serde_json::from_value(v.clone()).map_err(de::Error::custom)?;
339 result.insert(k.clone(), av);
340 }
341 Ok(AttributeValue::M(result))
342 }
343 _ => unreachable!(),
344 }
345 }
346}
347
348fn validate_number_in_deser(n: &str) -> Result<(), String> {
355 if n.is_empty() {
356 return Err("VALIDATION:The parameter cannot be converted to a numeric value".to_string());
357 }
358 let trimmed = n.trim();
360 let is_valid = trimmed.parse::<f64>().is_ok()
361 || trimmed
362 .to_lowercase()
363 .contains('e')
364 .then(|| trimmed.parse::<f64>().ok())
365 .is_some();
366 if !is_valid {
367 return Err(format!(
368 "VALIDATION:The parameter cannot be converted to a numeric value: {n}"
369 ));
370 }
371 if let Err(e) = validate_dynamo_number(n) {
373 let msg = match e {
374 crate::errors::DynoxideError::ValidationException(m) => format!("VALIDATION:{m}"),
375 _ => format!("VALIDATION:{}", e),
376 };
377 return Err(msg);
378 }
379 Ok(())
380}
381
382pub fn normalize_number_for_sort(num_str: &str) -> String {
396 let trimmed = num_str.trim();
397
398 if trimmed.is_empty() || trimmed == "0" || trimmed == "-0" || trimmed == "0.0" {
399 return zero_encoding();
400 }
401
402 let negative = trimmed.starts_with('-');
403 let abs_str = if negative { &trimmed[1..] } else { trimmed };
404
405 let (mantissa_digits, exponent) = parse_number_parts(abs_str);
407
408 if mantissa_digits.is_empty() || mantissa_digits.iter().all(|&d| d == 0) {
409 return zero_encoding();
410 }
411
412 if negative {
413 encode_negative(&mantissa_digits, exponent)
414 } else {
415 encode_positive(&mantissa_digits, exponent)
416 }
417}
418
419pub fn validate_dynamo_number(
425 num_str: &str,
426) -> std::result::Result<(), crate::errors::DynoxideError> {
427 let trimmed = num_str.trim();
428
429 if trimmed.is_empty() {
430 return Err(crate::errors::DynoxideError::ValidationException(
431 "The parameter cannot be converted to a numeric value".to_string(),
432 ));
433 }
434
435 let negative = trimmed.starts_with('-');
436 let abs_str = if negative { &trimmed[1..] } else { trimmed };
437
438 if abs_str.is_empty() || !abs_str.chars().any(|c| c.is_ascii_digit()) {
442 return Err(crate::errors::DynoxideError::ValidationException(format!(
443 "The parameter cannot be converted to a numeric value: {}",
444 trimmed
445 )));
446 }
447 let valid = abs_str.chars().enumerate().all(|(i, c)| {
448 c.is_ascii_digit() || c == '.' || c == 'e' || c == 'E' || ((c == '+' || c == '-') && i > 0) });
450 if !valid {
451 return Err(crate::errors::DynoxideError::ValidationException(format!(
452 "The parameter cannot be converted to a numeric value: {}",
453 trimmed
454 )));
455 }
456
457 let (mantissa_digits, exponent) = parse_number_parts(abs_str);
458
459 if mantissa_digits.is_empty() || mantissa_digits.iter().all(|&d| d == 0) {
461 return Ok(());
462 }
463
464 if mantissa_digits.len() > 38 {
466 return Err(crate::errors::DynoxideError::ValidationException(
467 "Attempting to store more than 38 significant digits in a Number".to_string(),
468 ));
469 }
470
471 if exponent > 126 {
474 return Err(crate::errors::DynoxideError::ValidationException(
475 "Number overflow. Attempting to store a number with magnitude larger than supported range"
476 .to_string(),
477 ));
478 }
479
480 if exponent < -129 {
487 return Err(crate::errors::DynoxideError::ValidationException(
488 "Number underflow. Attempting to store a number with magnitude smaller than supported range"
489 .to_string(),
490 ));
491 }
492
493 Ok(())
494}
495
496pub fn normalize_dynamo_number(num_str: &str) -> String {
504 let trimmed = num_str.trim();
505 if trimmed.is_empty() {
506 return "0".to_string();
507 }
508
509 let negative = trimmed.starts_with('-');
510 let abs_str = if negative {
511 &trimmed[1..]
512 } else {
513 trimmed.trim_start_matches('+')
514 };
515
516 let (mantissa_digits, exponent) = parse_number_parts(abs_str);
517
518 if mantissa_digits.is_empty() {
520 return "0".to_string();
521 }
522
523 let num_digits = mantissa_digits.len() as i32;
528 let int_digits = exponent; let mut result = String::new();
531 if negative {
532 result.push('-');
533 }
534
535 if int_digits <= 0 {
536 result.push_str("0.");
538 for _ in 0..(-int_digits) {
539 result.push('0');
540 }
541 for &d in &mantissa_digits {
542 result.push((b'0' + d) as char);
543 }
544 } else if int_digits >= num_digits {
545 for &d in &mantissa_digits {
547 result.push((b'0' + d) as char);
548 }
549 for _ in 0..(int_digits - num_digits) {
550 result.push('0');
551 }
552 } else {
553 let int_part = int_digits as usize;
555 for &d in &mantissa_digits[..int_part] {
556 result.push((b'0' + d) as char);
557 }
558 result.push('.');
559 for &d in &mantissa_digits[int_part..] {
560 result.push((b'0' + d) as char);
561 }
562 }
563
564 result
565}
566
567fn zero_encoding() -> String {
568 format!("1{}{}", "0".repeat(4), "0".repeat(40))
570}
571
572fn encode_positive(mantissa: &[u8], exponent: i32) -> String {
573 let exp_encoded = (exponent + 5000) as u16;
574 let mantissa_str = mantissa_to_string(mantissa, 40);
575 format!("2{exp_encoded:04}{mantissa_str}")
576}
577
578fn encode_negative(mantissa: &[u8], exponent: i32) -> String {
579 let exp_encoded = 9999 - (exponent + 5000) as u16;
581 let mantissa_str = complement_mantissa(mantissa, 40);
582 format!("0{exp_encoded:04}{mantissa_str}")
583}
584
585pub(crate) fn parse_number_parts(s: &str) -> (Vec<u8>, i32) {
589 let (coeff, exp_part) = if let Some(pos) = s.to_ascii_lowercase().find('e') {
591 let coeff = &s[..pos];
592 let exp: i32 = s[pos + 1..].parse().unwrap_or(0);
593 (coeff, exp)
594 } else {
595 (s, 0)
596 };
597
598 let (int_part, frac_part) = if let Some(dot) = coeff.find('.') {
600 (&coeff[..dot], &coeff[dot + 1..])
601 } else {
602 (coeff, "")
603 };
604
605 let mut digits: Vec<u8> = Vec::new();
607 for ch in int_part.chars().chain(frac_part.chars()) {
608 if ch.is_ascii_digit() {
609 digits.push(ch as u8 - b'0');
610 }
611 }
612
613 if digits.is_empty() {
614 return (vec![], 0);
615 }
616
617 let int_len = int_part.chars().filter(|c| c.is_ascii_digit()).count() as i32;
619
620 let leading_zeros = digits.iter().take_while(|&&d| d == 0).count();
622 digits.drain(..leading_zeros);
623
624 while digits.last() == Some(&0) {
626 digits.pop();
627 }
628
629 if digits.is_empty() {
630 return (vec![], 0);
631 }
632
633 let exponent = int_len - leading_zeros as i32 + exp_part;
636
637 (digits, exponent)
638}
639
640fn mantissa_to_string(digits: &[u8], width: usize) -> String {
641 let mut s = String::with_capacity(width);
642 for &d in digits.iter().take(width) {
643 s.push((b'0' + d) as char);
644 }
645 while s.len() < width {
646 s.push('0');
647 }
648 s
649}
650
651fn complement_mantissa(digits: &[u8], width: usize) -> String {
652 let mut s = String::with_capacity(width);
653 for i in 0..width {
654 let d = if i < digits.len() { digits[i] } else { 0 };
655 s.push((b'0' + (9 - d)) as char);
656 }
657 s
658}
659
660fn hex_encode(bytes: &[u8]) -> String {
662 let mut s = String::with_capacity(bytes.len() * 2);
663 for &b in bytes {
664 s.push_str(&format!("{b:02x}"));
665 }
666 s
667}
668
669pub type Item = HashMap<String, AttributeValue>;
675
676#[derive(Debug, Clone, Default, Serialize, Deserialize)]
678pub struct SseSpecification {
679 #[serde(rename = "Enabled", default)]
680 pub enabled: Option<bool>,
681 #[serde(rename = "SSEType", default)]
682 pub sse_type: Option<String>,
683 #[serde(rename = "KMSMasterKeyId", default)]
684 pub kms_master_key_id: Option<String>,
685}
686
687#[derive(Debug, Clone, Default, Serialize, Deserialize)]
689pub struct Tag {
690 #[serde(rename = "Key")]
691 pub key: String,
692 #[serde(rename = "Value")]
693 pub value: String,
694}
695
696pub fn item_size(item: &Item) -> usize {
698 item.iter()
699 .map(|(name, value)| name.len() + value.size())
700 .sum()
701}
702
703pub const MAX_ITEM_SIZE: usize = 400 * 1024;
705
706#[derive(Debug, Clone, Serialize, Deserialize)]
709pub struct ItemCollectionMetrics {
710 #[serde(rename = "ItemCollectionKey")]
711 pub item_collection_key: HashMap<String, AttributeValue>,
712 #[serde(rename = "SizeEstimateRangeGB")]
713 pub size_estimate_range_gb: Vec<f64>,
714}
715
716#[derive(Debug, Clone, Default, Serialize, Deserialize)]
718pub struct ConsumedCapacity {
719 #[serde(rename = "TableName")]
720 pub table_name: String,
721 #[serde(rename = "CapacityUnits")]
722 pub capacity_units: f64,
723 #[serde(rename = "Table", skip_serializing_if = "Option::is_none")]
724 pub table: Option<CapacityDetail>,
725 #[serde(
726 rename = "GlobalSecondaryIndexes",
727 skip_serializing_if = "Option::is_none"
728 )]
729 pub global_secondary_indexes: Option<HashMap<String, CapacityDetail>>,
730 #[serde(
731 rename = "LocalSecondaryIndexes",
732 skip_serializing_if = "Option::is_none"
733 )]
734 pub local_secondary_indexes: Option<HashMap<String, CapacityDetail>>,
735}
736
737#[derive(Debug, Clone, Default, Serialize, Deserialize)]
739pub struct CapacityDetail {
740 #[serde(rename = "CapacityUnits")]
741 pub capacity_units: f64,
742}
743
744pub fn write_capacity_units(item_size_bytes: usize) -> f64 {
746 ((item_size_bytes as f64) / 1024.0).ceil().max(1.0)
747}
748
749pub fn read_capacity_units(item_size_bytes: usize) -> f64 {
753 ((item_size_bytes as f64) / 4096.0).ceil().max(1.0)
754}
755
756pub fn read_capacity_units_with_consistency(item_size_bytes: usize, consistent: bool) -> f64 {
761 let strongly = read_capacity_units(item_size_bytes);
762 if consistent { strongly } else { strongly / 2.0 }
763}
764
765pub fn consumed_capacity(
767 table_name: &str,
768 capacity_units: f64,
769 mode: &Option<String>,
770) -> Option<ConsumedCapacity> {
771 let mode = mode.as_deref().unwrap_or("NONE");
772 match mode {
773 "TOTAL" => Some(ConsumedCapacity {
774 table_name: table_name.to_string(),
775 capacity_units,
776 table: None,
777 global_secondary_indexes: None,
778 local_secondary_indexes: None,
779 }),
780 "INDEXES" => Some(ConsumedCapacity {
781 table_name: table_name.to_string(),
782 capacity_units,
783 table: Some(CapacityDetail { capacity_units }),
784 global_secondary_indexes: None,
785 local_secondary_indexes: None,
786 }),
787 _ => None,
788 }
789}
790
791pub fn consumed_capacity_with_indexes(
793 table_name: &str,
794 table_units: f64,
795 gsi_units: &HashMap<String, f64>,
796 mode: &Option<String>,
797) -> Option<ConsumedCapacity> {
798 consumed_capacity_with_secondary_indexes(
799 table_name,
800 table_units,
801 gsi_units,
802 &HashMap::new(),
803 mode,
804 )
805}
806
807pub fn consumed_capacity_with_secondary_indexes(
809 table_name: &str,
810 table_units: f64,
811 gsi_units: &HashMap<String, f64>,
812 lsi_units: &HashMap<String, f64>,
813 mode: &Option<String>,
814) -> Option<ConsumedCapacity> {
815 let units_to_map = |units: &HashMap<String, f64>| -> Option<HashMap<String, CapacityDetail>> {
816 if units.is_empty() {
817 None
818 } else {
819 Some(
820 units
821 .iter()
822 .map(|(name, &u)| (name.clone(), CapacityDetail { capacity_units: u }))
823 .collect(),
824 )
825 }
826 };
827
828 match mode.as_deref().unwrap_or("NONE") {
829 "INDEXES" => {
830 let gsi_total: f64 = gsi_units.values().sum();
831 let lsi_total: f64 = lsi_units.values().sum();
832 Some(ConsumedCapacity {
833 table_name: table_name.to_string(),
834 capacity_units: table_units + gsi_total + lsi_total,
835 table: Some(CapacityDetail {
836 capacity_units: table_units,
837 }),
838 global_secondary_indexes: units_to_map(gsi_units),
839 local_secondary_indexes: units_to_map(lsi_units),
840 })
841 }
842 "TOTAL" => {
843 let gsi_total: f64 = gsi_units.values().sum();
844 let lsi_total: f64 = lsi_units.values().sum();
845 Some(ConsumedCapacity {
846 table_name: table_name.to_string(),
847 capacity_units: table_units + gsi_total + lsi_total,
848 table: None,
849 global_secondary_indexes: None,
850 local_secondary_indexes: None,
851 })
852 }
853 _ => None,
854 }
855}
856
857#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
859pub struct KeySchemaElement {
860 #[serde(rename = "AttributeName", alias = "attribute_name")]
861 pub attribute_name: String,
862 #[serde(rename = "KeyType", alias = "key_type")]
863 pub key_type: KeyType,
864}
865
866#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
868pub enum KeyType {
869 #[default]
870 HASH,
871 RANGE,
872}
873
874#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
876pub struct AttributeDefinition {
877 #[serde(rename = "AttributeName", alias = "attribute_name")]
878 pub attribute_name: String,
879 #[serde(rename = "AttributeType", alias = "attribute_type")]
880 pub attribute_type: ScalarAttributeType,
881}
882
883#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
885pub enum ScalarAttributeType {
886 #[default]
887 S,
888 N,
889 B,
890}
891
892#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
894pub struct Projection {
895 #[serde(
896 rename = "ProjectionType",
897 alias = "projection_type",
898 default,
899 skip_serializing_if = "Option::is_none"
900 )]
901 pub projection_type: Option<ProjectionType>,
902 #[serde(
903 rename = "NonKeyAttributes",
904 alias = "non_key_attributes",
905 skip_serializing_if = "Option::is_none"
906 )]
907 pub non_key_attributes: Option<Vec<String>>,
908}
909
910#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
912#[allow(non_camel_case_types)]
913pub enum ProjectionType {
914 #[default]
915 ALL,
916 KEYS_ONLY,
917 INCLUDE,
918}
919
920#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
922pub struct GlobalSecondaryIndex {
923 #[serde(rename = "IndexName", alias = "index_name")]
924 pub index_name: String,
925 #[serde(rename = "KeySchema", alias = "key_schema")]
926 pub key_schema: Vec<KeySchemaElement>,
927 #[serde(rename = "Projection", alias = "projection")]
928 pub projection: Projection,
929 #[serde(
930 rename = "ProvisionedThroughput",
931 alias = "provisioned_throughput",
932 skip_serializing_if = "Option::is_none"
933 )]
934 pub provisioned_throughput: Option<ProvisionedThroughput>,
935}
936
937#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
939pub struct LocalSecondaryIndex {
940 #[serde(rename = "IndexName", alias = "index_name")]
941 pub index_name: String,
942 #[serde(rename = "KeySchema", alias = "key_schema")]
943 pub key_schema: Vec<KeySchemaElement>,
944 #[serde(rename = "Projection", alias = "projection")]
945 pub projection: Projection,
946}
947
948#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
950pub struct ProvisionedThroughput {
951 #[serde(rename = "ReadCapacityUnits", default)]
952 pub read_capacity_units: Option<i64>,
953 #[serde(rename = "WriteCapacityUnits", default)]
954 pub write_capacity_units: Option<i64>,
955}
956
957#[derive(Debug, Clone, PartialEq)]
963pub struct ConversionError {
964 pub expected: &'static str,
966 pub actual: &'static str,
968}
969
970impl fmt::Display for ConversionError {
971 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
972 write!(f, "expected {}, got {}", self.expected, self.actual)
973 }
974}
975
976impl std::error::Error for ConversionError {}
977
978impl From<String> for AttributeValue {
981 fn from(value: String) -> Self {
982 AttributeValue::S(value)
983 }
984}
985
986impl From<&str> for AttributeValue {
987 fn from(value: &str) -> Self {
988 AttributeValue::S(value.to_string())
989 }
990}
991
992impl From<bool> for AttributeValue {
993 fn from(value: bool) -> Self {
994 AttributeValue::BOOL(value)
995 }
996}
997
998impl From<Vec<u8>> for AttributeValue {
999 fn from(value: Vec<u8>) -> Self {
1000 AttributeValue::B(value)
1001 }
1002}
1003
1004impl From<&[u8]> for AttributeValue {
1005 fn from(value: &[u8]) -> Self {
1006 AttributeValue::B(value.to_vec())
1007 }
1008}
1009
1010macro_rules! impl_from_integer {
1012 ($($t:ty),+) => {
1013 $(
1014 impl From<$t> for AttributeValue {
1015 fn from(value: $t) -> Self {
1016 AttributeValue::N(value.to_string())
1017 }
1018 }
1019 )+
1020 };
1021}
1022
1023impl_from_integer!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128);
1024
1025impl From<HashMap<String, AttributeValue>> for AttributeValue {
1027 fn from(value: HashMap<String, AttributeValue>) -> Self {
1028 AttributeValue::M(value)
1029 }
1030}
1031
1032impl From<Vec<AttributeValue>> for AttributeValue {
1033 fn from(value: Vec<AttributeValue>) -> Self {
1034 AttributeValue::L(value)
1035 }
1036}
1037
1038impl From<HashSet<String>> for AttributeValue {
1039 fn from(value: HashSet<String>) -> Self {
1040 AttributeValue::SS(value.into_iter().collect())
1041 }
1042}
1043
1044impl From<BTreeSet<String>> for AttributeValue {
1045 fn from(value: BTreeSet<String>) -> Self {
1046 AttributeValue::SS(value.into_iter().collect())
1047 }
1048}
1049
1050impl TryFrom<f64> for AttributeValue {
1053 type Error = ConversionError;
1054
1055 fn try_from(value: f64) -> std::result::Result<Self, Self::Error> {
1056 if value.is_finite() {
1057 Ok(AttributeValue::N(value.to_string()))
1058 } else {
1059 Err(ConversionError {
1060 expected: "finite f64",
1061 actual: "NaN or Infinity",
1062 })
1063 }
1064 }
1065}
1066
1067impl TryFrom<f32> for AttributeValue {
1068 type Error = ConversionError;
1069
1070 fn try_from(value: f32) -> std::result::Result<Self, Self::Error> {
1071 if value.is_finite() {
1072 Ok(AttributeValue::N(value.to_string()))
1073 } else {
1074 Err(ConversionError {
1075 expected: "finite f32",
1076 actual: "NaN or Infinity",
1077 })
1078 }
1079 }
1080}
1081
1082impl TryFrom<AttributeValue> for String {
1085 type Error = ConversionError;
1086
1087 fn try_from(value: AttributeValue) -> std::result::Result<Self, ConversionError> {
1088 match value {
1089 AttributeValue::S(s) => Ok(s),
1090 other => Err(ConversionError {
1091 expected: "S",
1092 actual: other.type_name(),
1093 }),
1094 }
1095 }
1096}
1097
1098impl TryFrom<AttributeValue> for bool {
1099 type Error = ConversionError;
1100
1101 fn try_from(value: AttributeValue) -> std::result::Result<Self, ConversionError> {
1102 match value {
1103 AttributeValue::BOOL(b) => Ok(b),
1104 other => Err(ConversionError {
1105 expected: "BOOL",
1106 actual: other.type_name(),
1107 }),
1108 }
1109 }
1110}
1111
1112impl TryFrom<AttributeValue> for Vec<u8> {
1113 type Error = ConversionError;
1114
1115 fn try_from(value: AttributeValue) -> std::result::Result<Self, ConversionError> {
1116 match value {
1117 AttributeValue::B(b) => Ok(b),
1118 other => Err(ConversionError {
1119 expected: "B",
1120 actual: other.type_name(),
1121 }),
1122 }
1123 }
1124}
1125
1126macro_rules! impl_try_from_av_integer {
1127 ($($t:ty),+) => {
1128 $(
1129 impl TryFrom<AttributeValue> for $t {
1130 type Error = ConversionError;
1131
1132 fn try_from(value: AttributeValue) -> std::result::Result<Self, ConversionError> {
1133 match value {
1134 AttributeValue::N(n) => n.parse::<$t>().map_err(|_| ConversionError {
1135 expected: stringify!($t),
1136 actual: "N (parse failed)",
1137 }),
1138 other => Err(ConversionError {
1139 expected: "N",
1140 actual: other.type_name(),
1141 }),
1142 }
1143 }
1144 }
1145 )+
1146 };
1147}
1148
1149impl_try_from_av_integer!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128);
1150
1151impl TryFrom<AttributeValue> for f64 {
1152 type Error = ConversionError;
1153
1154 fn try_from(value: AttributeValue) -> std::result::Result<Self, ConversionError> {
1155 match value {
1156 AttributeValue::N(n) => n.parse::<f64>().map_err(|_| ConversionError {
1157 expected: "f64",
1158 actual: "N (parse failed)",
1159 }),
1160 other => Err(ConversionError {
1161 expected: "N",
1162 actual: other.type_name(),
1163 }),
1164 }
1165 }
1166}
1167
1168impl TryFrom<AttributeValue> for f32 {
1169 type Error = ConversionError;
1170
1171 fn try_from(value: AttributeValue) -> std::result::Result<Self, ConversionError> {
1172 match value {
1173 AttributeValue::N(n) => n.parse::<f32>().map_err(|_| ConversionError {
1174 expected: "f32",
1175 actual: "N (parse failed)",
1176 }),
1177 other => Err(ConversionError {
1178 expected: "N",
1179 actual: other.type_name(),
1180 }),
1181 }
1182 }
1183}
1184
1185impl TryFrom<AttributeValue> for HashMap<String, AttributeValue> {
1186 type Error = ConversionError;
1187
1188 fn try_from(value: AttributeValue) -> std::result::Result<Self, ConversionError> {
1189 match value {
1190 AttributeValue::M(m) => Ok(m),
1191 other => Err(ConversionError {
1192 expected: "M",
1193 actual: other.type_name(),
1194 }),
1195 }
1196 }
1197}
1198
1199impl TryFrom<AttributeValue> for Vec<AttributeValue> {
1200 type Error = ConversionError;
1201
1202 fn try_from(value: AttributeValue) -> std::result::Result<Self, ConversionError> {
1203 match value {
1204 AttributeValue::L(l) => Ok(l),
1205 other => Err(ConversionError {
1206 expected: "L",
1207 actual: other.type_name(),
1208 }),
1209 }
1210 }
1211}
1212
1213impl TryFrom<AttributeValue> for Vec<String> {
1214 type Error = ConversionError;
1215
1216 fn try_from(value: AttributeValue) -> std::result::Result<Self, ConversionError> {
1217 match value {
1218 AttributeValue::SS(ss) => Ok(ss),
1219 AttributeValue::L(l) => {
1220 l.into_iter()
1222 .map(|av| match av {
1223 AttributeValue::S(s) => Ok(s),
1224 other => Err(ConversionError {
1225 expected: "S (within L)",
1226 actual: other.type_name(),
1227 }),
1228 })
1229 .collect()
1230 }
1231 other => Err(ConversionError {
1232 expected: "SS or L",
1233 actual: other.type_name(),
1234 }),
1235 }
1236 }
1237}
1238
1239#[cfg(test)]
1240mod tests {
1241 use super::*;
1242
1243 #[test]
1244 fn test_serialize_string() {
1245 let val = AttributeValue::S("hello".to_string());
1246 let json = serde_json::to_string(&val).unwrap();
1247 assert_eq!(json, r#"{"S":"hello"}"#);
1248 }
1249
1250 #[test]
1251 fn test_serialize_number() {
1252 let val = AttributeValue::N("42".to_string());
1253 let json = serde_json::to_string(&val).unwrap();
1254 assert_eq!(json, r#"{"N":"42"}"#);
1255 }
1256
1257 #[test]
1258 fn test_serialize_binary() {
1259 let val = AttributeValue::B(vec![1, 2, 3]);
1260 let json = serde_json::to_string(&val).unwrap();
1261 assert_eq!(json, r#"{"B":"AQID"}"#);
1262 }
1263
1264 #[test]
1265 fn test_serialize_bool() {
1266 let val = AttributeValue::BOOL(true);
1267 let json = serde_json::to_string(&val).unwrap();
1268 assert_eq!(json, r#"{"BOOL":true}"#);
1269 }
1270
1271 #[test]
1272 fn test_serialize_null() {
1273 let val = AttributeValue::NULL(true);
1274 let json = serde_json::to_string(&val).unwrap();
1275 assert_eq!(json, r#"{"NULL":true}"#);
1276 }
1277
1278 #[test]
1279 fn test_serialize_string_set() {
1280 let val = AttributeValue::SS(vec!["a".to_string(), "b".to_string()]);
1281 let json = serde_json::to_string(&val).unwrap();
1282 assert_eq!(json, r#"{"SS":["a","b"]}"#);
1283 }
1284
1285 #[test]
1286 fn test_serialize_list() {
1287 let val = AttributeValue::L(vec![
1288 AttributeValue::S("hello".to_string()),
1289 AttributeValue::N("42".to_string()),
1290 ]);
1291 let json = serde_json::to_string(&val).unwrap();
1292 assert_eq!(json, r#"{"L":[{"S":"hello"},{"N":"42"}]}"#);
1293 }
1294
1295 #[test]
1296 fn test_serialize_map() {
1297 let mut m = HashMap::new();
1298 m.insert("key".to_string(), AttributeValue::S("value".to_string()));
1299 let val = AttributeValue::M(m);
1300 let json = serde_json::to_string(&val).unwrap();
1301 assert_eq!(json, r#"{"M":{"key":{"S":"value"}}}"#);
1302 }
1303
1304 #[test]
1305 fn test_round_trip_all_types() {
1306 let values = vec![
1307 AttributeValue::S("hello".to_string()),
1308 AttributeValue::N("42.5".to_string()),
1309 AttributeValue::B(vec![0, 255, 128]),
1310 AttributeValue::BOOL(false),
1311 AttributeValue::NULL(true),
1312 AttributeValue::SS(vec!["x".to_string(), "y".to_string()]),
1313 AttributeValue::NS(vec!["1".to_string(), "2.5".to_string()]),
1314 AttributeValue::BS(vec![vec![1], vec![2, 3]]),
1315 AttributeValue::L(vec![
1316 AttributeValue::S("nested".to_string()),
1317 AttributeValue::N("99".to_string()),
1318 ]),
1319 ];
1320
1321 for val in values {
1322 let json = serde_json::to_string(&val).unwrap();
1323 let deserialized: AttributeValue = serde_json::from_str(&json).unwrap();
1324 assert_eq!(val, deserialized, "Round-trip failed for {json}");
1325 }
1326 }
1327
1328 #[test]
1329 fn test_size_string() {
1330 let val = AttributeValue::S("hello".to_string());
1331 assert_eq!(val.size(), 5);
1332 }
1333
1334 #[test]
1335 fn test_size_number() {
1336 let val = AttributeValue::N("42".to_string());
1338 assert_eq!(val.size(), 2);
1339 }
1340
1341 #[test]
1342 fn test_size_bool() {
1343 assert_eq!(AttributeValue::BOOL(true).size(), 1);
1344 }
1345
1346 #[test]
1347 fn test_size_null() {
1348 assert_eq!(AttributeValue::NULL(true).size(), 1);
1349 }
1350
1351 #[test]
1352 fn test_key_string_s() {
1353 let val = AttributeValue::S("hello".to_string());
1354 assert_eq!(val.to_key_string(), Some("S:hello".to_string()));
1355 }
1356
1357 #[test]
1358 fn test_key_string_n() {
1359 let val = AttributeValue::N("42".to_string());
1360 let key = val.to_key_string().unwrap();
1361 assert!(key.starts_with("N:"));
1362 }
1363
1364 #[test]
1365 fn test_key_string_b() {
1366 let val = AttributeValue::B(vec![0xff, 0x00, 0xab]);
1367 assert_eq!(val.to_key_string(), Some("B:ff00ab".to_string()));
1368 }
1369
1370 #[test]
1371 fn test_key_string_non_key_type_returns_none() {
1372 assert_eq!(AttributeValue::BOOL(true).to_key_string(), None);
1373 assert_eq!(AttributeValue::L(vec![]).to_key_string(), None);
1374 }
1375
1376 #[test]
1378 fn test_number_sort_ordering() {
1379 let numbers = vec![
1380 "-1000", "-100", "-10", "-1", "-0.5", "-0.001", "0", "0.001", "0.5", "1", "10", "100",
1381 "1000",
1382 ];
1383 let encoded: Vec<String> = numbers
1384 .iter()
1385 .map(|n| normalize_number_for_sort(n))
1386 .collect();
1387
1388 for i in 0..encoded.len() - 1 {
1389 assert!(
1390 encoded[i] < encoded[i + 1],
1391 "Sort order broken: {} ({}) should be < {} ({})",
1392 numbers[i],
1393 encoded[i],
1394 numbers[i + 1],
1395 encoded[i + 1]
1396 );
1397 }
1398 }
1399
1400 #[test]
1401 fn test_number_sort_zero_variants() {
1402 let z1 = normalize_number_for_sort("0");
1403 let z2 = normalize_number_for_sort("-0");
1404 let z3 = normalize_number_for_sort("0.0");
1405 assert_eq!(z1, z2);
1406 assert_eq!(z2, z3);
1407 }
1408
1409 #[test]
1410 fn test_number_sort_decimals() {
1411 let a = normalize_number_for_sort("1.5");
1412 let b = normalize_number_for_sort("2.5");
1413 assert!(a < b);
1414
1415 let c = normalize_number_for_sort("0.001");
1416 let d = normalize_number_for_sort("0.01");
1417 assert!(c < d);
1418 }
1419
1420 #[test]
1421 fn test_number_sort_scientific() {
1422 let a = normalize_number_for_sort("1e10");
1423 let b = normalize_number_for_sort("1e11");
1424 assert!(a < b);
1425
1426 let c = normalize_number_for_sort("-1e11");
1427 let d = normalize_number_for_sort("-1e10");
1428 assert!(c < d);
1429 }
1430
1431 #[test]
1432 fn test_type_name() {
1433 assert_eq!(AttributeValue::S("".to_string()).type_name(), "S");
1434 assert_eq!(AttributeValue::N("0".to_string()).type_name(), "N");
1435 assert_eq!(AttributeValue::B(vec![]).type_name(), "B");
1436 assert_eq!(AttributeValue::BOOL(true).type_name(), "BOOL");
1437 assert_eq!(AttributeValue::NULL(true).type_name(), "NULL");
1438 assert_eq!(AttributeValue::SS(vec![]).type_name(), "SS");
1439 assert_eq!(AttributeValue::NS(vec![]).type_name(), "NS");
1440 assert_eq!(AttributeValue::BS(vec![]).type_name(), "BS");
1441 assert_eq!(AttributeValue::L(vec![]).type_name(), "L");
1442 assert_eq!(AttributeValue::M(HashMap::new()).type_name(), "M");
1443 }
1444}