dynamodb_expression/value/
value_or_ref.rs

1use core::fmt::{self, Write};
2
3use super::Value;
4
5/// A DynamoDB value, or a reference to one stored in the collected expression values.
6#[derive(Debug, Clone, PartialEq, Eq, Hash)]
7pub(crate) enum ValueOrRef {
8    Value(Value),
9    Ref(Ref),
10}
11
12impl fmt::Display for ValueOrRef {
13    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
14        match self {
15            ValueOrRef::Value(value) => value.fmt(f),
16            ValueOrRef::Ref(value) => value.fmt(f),
17        }
18    }
19}
20
21impl<T> From<T> for ValueOrRef
22where
23    T: Into<Value>,
24{
25    fn from(value: T) -> Self {
26        Self::Value(value.into())
27    }
28}
29
30impl From<Ref> for ValueOrRef {
31    fn from(value: Ref) -> Self {
32        Self::Ref(value)
33    }
34}
35
36/// A reference to a DynamoDB value stored in expression attribute values.
37/// Automatically prefixed with `:`.
38///
39/// ```
40/// use dynamodb_expression::value::Ref;
41/// # use pretty_assertions::assert_eq;
42///
43/// let value = Ref::new("expression_value");
44/// assert_eq!(":expression_value", value.to_string())
45/// ```
46#[derive(Debug, Clone, PartialEq, Eq, Hash)]
47pub struct Ref(String);
48
49impl Ref {
50    pub fn new<T>(value_ref: T) -> Self
51    where
52        T: Into<String>,
53    {
54        Self(value_ref.into())
55    }
56}
57
58impl From<String> for Ref {
59    fn from(value: String) -> Self {
60        Self(value)
61    }
62}
63
64impl From<&String> for Ref {
65    fn from(value: &String) -> Self {
66        Self(value.to_owned())
67    }
68}
69
70impl From<&str> for Ref {
71    fn from(value: &str) -> Self {
72        Self(value.to_owned())
73    }
74}
75
76impl From<&&str> for Ref {
77    fn from(value: &&str) -> Self {
78        Self((*value).to_owned())
79    }
80}
81
82impl fmt::Display for Ref {
83    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84        f.write_char(':')?;
85        self.0.fmt(f)
86    }
87}
88
89impl From<Ref> for String {
90    fn from(mut value: Ref) -> Self {
91        value.0.insert(0, ':');
92
93        value.0
94    }
95}
96
97/// Represents a value that is either a string, or a reference to a value
98/// already in the expression attribute values.
99///
100/// ```
101/// use dynamodb_expression::value::{StringOrRef, Ref};
102///
103/// let value: StringOrRef = "a string value".into();
104/// let value: StringOrRef = Ref::new("expression_value").into();
105/// ```
106///
107/// For example, the [`BeginsWith`] operator can take a string or a reference to
108/// an extended attribute value. Here's how [`StringOrRef`] works with that.
109///
110/// ```
111/// # fn string_or_ref() -> Result<(), Box<dyn std::error::Error>> {
112/// use dynamodb_expression::{condition::BeginsWith, value::Ref, Path};
113/// # use pretty_assertions::assert_eq;
114///
115/// let begins_with = BeginsWith::new("foo".parse::<Path>()?, "T");
116/// assert_eq!(r#"begins_with(foo, "T")"#, begins_with.to_string());
117///
118/// let begins_with = BeginsWith::new("foo".parse::<Path>()?, Ref::new("prefix"));
119/// assert_eq!(r#"begins_with(foo, :prefix)"#, begins_with.to_string());
120/// #
121/// # Ok(())
122/// # }
123/// ```
124///
125/// See also: [`Ref`]
126///
127/// [`BeginsWith`]: crate::condition::BeginsWith
128pub enum StringOrRef {
129    String(String),
130    Ref(Ref),
131}
132
133impl From<String> for StringOrRef {
134    fn from(value: String) -> Self {
135        Self::String(value)
136    }
137}
138
139impl From<&String> for StringOrRef {
140    fn from(value: &String) -> Self {
141        Self::String(value.to_owned())
142    }
143}
144
145impl From<&str> for StringOrRef {
146    fn from(value: &str) -> Self {
147        Self::String(value.to_owned())
148    }
149}
150
151impl From<&&str> for StringOrRef {
152    fn from(value: &&str) -> Self {
153        Self::String((*value).to_owned())
154    }
155}
156
157impl From<Ref> for StringOrRef {
158    fn from(value: Ref) -> Self {
159        Self::Ref(value)
160    }
161}
162
163impl From<StringOrRef> for ValueOrRef {
164    fn from(value: StringOrRef) -> Self {
165        match value {
166            StringOrRef::String(value) => value.into(),
167            StringOrRef::Ref(value) => value.into(),
168        }
169    }
170}
171
172#[cfg(test)]
173mod test {
174    use pretty_assertions::assert_str_eq;
175
176    use crate::Scalar;
177
178    use super::{Ref, ValueOrRef};
179
180    #[test]
181    fn display_value() {
182        let vr = ValueOrRef::from(Scalar::new_string("foo"));
183        assert_str_eq!("\"foo\"", vr.to_string());
184    }
185
186    #[test]
187    fn display_ref() {
188        let vr = ValueOrRef::Ref(Ref("foo".into()));
189        assert_str_eq!(":foo", vr.to_string());
190    }
191}
192
193#[cfg(test)]
194mod examples {
195    #[ignore = "This is just here for formatting."]
196    #[test]
197    fn string_or_ref() -> Result<(), Box<dyn std::error::Error>> {
198        use crate::{condition::BeginsWith, value::Ref, Path};
199        use pretty_assertions::assert_eq;
200
201        let begins_with = BeginsWith::new("foo".parse::<Path>()?, "T");
202        assert_eq!(r#"begins_with(foo, "T")"#, begins_with.to_string());
203
204        let begins_with = BeginsWith::new("foo".parse::<Path>()?, Ref::new("prefix"));
205        assert_eq!(r#"begins_with(foo, :prefix)"#, begins_with.to_string());
206
207        Ok(())
208    }
209}