1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
use super::AttributeValue;
use crate::{error::ErrorImpl, Error, Item, Result};
use serde::Serialize;

mod serializer;
mod serializer_map;
mod serializer_seq;
mod serializer_struct;
mod serializer_struct_variant;
mod serializer_tuple_variant;

#[cfg(test)]
mod tests;

pub use serializer::Serializer;
use serializer_map::SerializerMap;
use serializer_seq::SerializerSeq;
use serializer_struct::SerializerStruct;
use serializer_struct_variant::SerializerStructVariant;
use serializer_tuple_variant::SerializerTupleVariant;

/// Convert a `T` into an [`AttributeValue`].
///
/// In some circumstances, building [aws_sdk_dynamodb::model::AttributeValue]s directly is required.
///
/// For example, when generating a key to supply to [get_item].
///
/// ```no_run
/// use serde_dynamo::to_attribute_value;
/// # use __aws_sdk_dynamodb_1::client::Client;
/// # use std::collections::HashMap;
/// #
/// # async fn get(client: &Client) -> Result<(), Box<dyn std::error::Error>> {
/// #
/// # struct User { id: String };
/// # let user = User { id: "fSsgVtal8TpP".to_string() };
///
/// // Create the unique key of the record in DynamoDB in a way rusoto understands
/// let key = HashMap::from([
///     (String::from("id"), to_attribute_value(&user.id)?),
/// ]);
///
/// // And get the record
/// client.get_item().table_name("users").set_key(Some(key)).send().await?;
/// # Ok(())
/// # }
/// ```
///
/// Or when generating attribute values in a [query] call.
///
/// ```no_run
/// use serde_dynamo::to_attribute_value;
/// # use __aws_sdk_dynamodb_1::client::Client;
/// # use std::collections::HashMap;
/// #
/// # async fn query(client: &Client) -> Result<(), Box<dyn std::error::Error>> {
/// # let user_type = "user";
/// # let yesterday = "1985-04-21";
///
/// // Declare all of the expression inputs for a query call
/// let expression_attribute_values = HashMap::from([
///     (String::from(":user_type"), to_attribute_value(user_type)?),
///     (String::from(":last_login"), to_attribute_value(yesterday)?),
/// ]);
///
/// client.query()
///     .table_name("users")
///     .index_name("by_type_and_last_login")
///     .key_condition_expression("user_type = :user_type AND last_login > :last_login")
///     .set_expression_attribute_values(Some(expression_attribute_values))
///     .send()
///     .await?;
/// # Ok(())
/// # }
/// ```
///
/// [aws-sdk-dynamodb]: https://docs.rs/aws-sdk-dynamodb
/// [get_item]: https://docs.rs/aws-sdk-dynamodb/*/aws_sdk_dynamodb/client/struct.Client.html#method.get_item
/// [put_item]: https://docs.rs/aws-sdk-dynamodb/*/aws_sdk_dynamodb/client/struct.Client.html#method.put_item
/// [query]: https://docs.rs/aws-sdk-dynamodb/*/aws_sdk_dynamodb/client/struct.Client.html#method.query
/// [aws_sdk_dynamodb::model::AttributeValue]: https://docs.rs/aws-sdk-dynamodb/*/aws_sdk_dynamodb/types/enum.AttributeValue.html
pub fn to_attribute_value<T, AV>(value: T) -> Result<AV>
where
    T: Serialize,
    AV: From<AttributeValue>,
{
    let serializer = Serializer;
    let attribute_value = value.serialize(serializer)?;
    Ok(AV::from(attribute_value))
}

/// Convert a `T` into an [`Item`].
///
/// This is frequently used when serializing an entire data structure to be sent to DynamoDB.
///
/// ```no_run
/// # use __aws_sdk_dynamodb_1::client::Client;
/// # use serde_derive::{Serialize, Deserialize};
/// # use serde_dynamo::to_item;
/// #
/// # async fn put(client: &Client) -> Result<(), Box<dyn std::error::Error>> {
/// #[derive(Serialize, Deserialize)]
/// pub struct User {
///     id: String,
///     name: String,
///     age: u8,
/// };
///
/// // Create a user
/// let user = User {
///     id: "fSsgVtal8TpP".to_string(),
///     name: "Arthur Dent".to_string(),
///     age: 42,
/// };
///
/// // Turn it into an item that rusoto understands
/// let item = to_item(user)?;
///
/// // And write it!
/// client.put_item().table_name("users").set_item(Some(item)).send().await?;
/// # Ok(())
/// # }
/// ```
pub fn to_item<T, I>(value: T) -> Result<I>
where
    T: Serialize,
    I: From<Item>,
{
    let serializer = Serializer;
    let attribute_value = value.serialize(serializer)?;
    if let AttributeValue::M(item) = attribute_value {
        let item = Item::from(item);
        Ok(I::from(item))
    } else {
        Err(ErrorImpl::NotMaplike.into())
    }
}