1use std::fmt;
18
19#[derive(Clone, Debug, PartialEq)]
26pub enum Value {
27 Null,
29 Integer(i64),
31 Real(f64),
33 Text(String),
35 Blob(Vec<u8>),
37}
38
39impl Value {
40 pub fn is_null(&self) -> bool {
42 matches!(self, Value::Null)
43 }
44
45 pub fn to_literal(&self) -> String {
51 match self {
52 Value::Null => "NULL".to_owned(),
53 Value::Integer(n) => n.to_string(),
54 Value::Real(f) => {
55 if f.fract() == 0.0 && f.is_finite() {
58 format!("{f:.1}")
59 } else {
60 f.to_string()
61 }
62 }
63 Value::Text(s) => format!("'{}'", s.replace('\'', "''")),
64 Value::Blob(b) => {
65 use std::fmt::Write as _;
66 let mut out = String::with_capacity(b.len() * 2 + 3);
67 out.push_str("X'");
68 for byte in b {
69 let _ = write!(out, "{byte:02X}");
70 }
71 out.push('\'');
72 out
73 }
74 }
75 }
76}
77
78impl fmt::Display for Value {
79 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80 f.write_str(&self.to_literal())
81 }
82}
83
84macro_rules! int_from {
87 ($($t:ty),*) => {$(
88 impl From<$t> for Value {
89 fn from(v: $t) -> Self {
90 Value::Integer(i64::from(v))
91 }
92 }
93 )*};
94}
95int_from!(i8, i16, i32, i64, u8, u16, u32);
96
97impl From<bool> for Value {
98 fn from(v: bool) -> Self {
99 Value::Integer(i64::from(v))
100 }
101}
102
103impl From<f32> for Value {
104 fn from(v: f32) -> Self {
105 Value::Real(f64::from(v))
106 }
107}
108
109impl From<f64> for Value {
110 fn from(v: f64) -> Self {
111 Value::Real(v)
112 }
113}
114
115impl From<&str> for Value {
116 fn from(v: &str) -> Self {
117 Value::Text(v.to_owned())
118 }
119}
120
121impl From<String> for Value {
122 fn from(v: String) -> Self {
123 Value::Text(v)
124 }
125}
126
127impl From<&String> for Value {
128 fn from(v: &String) -> Self {
129 Value::Text(v.clone())
130 }
131}
132
133impl From<char> for Value {
134 fn from(v: char) -> Self {
135 Value::Text(v.to_string())
136 }
137}
138
139impl From<Vec<u8>> for Value {
140 fn from(v: Vec<u8>) -> Self {
141 Value::Blob(v)
142 }
143}
144
145impl From<&[u8]> for Value {
146 fn from(v: &[u8]) -> Self {
147 Value::Blob(v.to_vec())
148 }
149}
150
151impl<T: Into<Value>> From<Option<T>> for Value {
152 fn from(v: Option<T>) -> Self {
153 v.map_or(Value::Null, Into::into)
154 }
155}
156
157impl<T: Into<Value> + Clone> From<&T> for Value
158where
159 T: ValueRefInto,
160{
161 fn from(v: &T) -> Self {
162 v.clone().into()
163 }
164}
165
166pub trait ValueRefInto {}
172impl ValueRefInto for bool {}
173impl ValueRefInto for i8 {}
174impl ValueRefInto for i16 {}
175impl ValueRefInto for i32 {}
176impl ValueRefInto for i64 {}
177impl ValueRefInto for u8 {}
178impl ValueRefInto for u16 {}
179impl ValueRefInto for u32 {}
180impl ValueRefInto for f32 {}
181impl ValueRefInto for f64 {}
182impl ValueRefInto for Vec<u8> {}
183impl<T: ValueRefInto> ValueRefInto for Option<T> {}
184
185#[cfg(feature = "with-chrono")]
191#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
192mod chrono_impls {
193 use chrono::{DateTime, FixedOffset, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc};
194
195 use super::Value;
196
197 pub const NAIVE_DATETIME_FORMAT: &str = "%Y-%m-%d %H:%M:%S%.f";
203
204 impl From<NaiveDate> for Value {
205 fn from(v: NaiveDate) -> Self {
206 Value::Text(v.format("%Y-%m-%d").to_string())
207 }
208 }
209 impl From<NaiveTime> for Value {
210 fn from(v: NaiveTime) -> Self {
211 Value::Text(v.format("%H:%M:%S%.f").to_string())
212 }
213 }
214 impl From<NaiveDateTime> for Value {
215 fn from(v: NaiveDateTime) -> Self {
216 Value::Text(v.format(NAIVE_DATETIME_FORMAT).to_string())
217 }
218 }
219 impl<Tz: TimeZone> From<DateTime<Tz>> for Value {
220 fn from(v: DateTime<Tz>) -> Self {
221 Value::Text(v.to_rfc3339())
222 }
223 }
224 impl super::ValueRefInto for NaiveDate {}
225 impl super::ValueRefInto for NaiveTime {}
226 impl super::ValueRefInto for NaiveDateTime {}
227 impl super::ValueRefInto for DateTime<Utc> {}
228 impl super::ValueRefInto for DateTime<FixedOffset> {}
229}
230#[cfg(feature = "with-chrono")]
231#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))]
232pub use chrono_impls::NAIVE_DATETIME_FORMAT;
233
234#[cfg(feature = "with-uuid")]
235#[cfg_attr(docsrs, doc(cfg(feature = "with-uuid")))]
236impl From<uuid::Uuid> for Value {
237 fn from(v: uuid::Uuid) -> Self {
238 Value::Text(v.hyphenated().to_string())
239 }
240}
241#[cfg(feature = "with-uuid")]
242impl ValueRefInto for uuid::Uuid {}
243
244#[cfg(feature = "with-json")]
245#[cfg_attr(docsrs, doc(cfg(feature = "with-json")))]
246impl From<serde_json::Value> for Value {
247 fn from(v: serde_json::Value) -> Self {
248 Value::Text(v.to_string())
249 }
250}
251#[cfg(feature = "with-json")]
252impl ValueRefInto for serde_json::Value {}
253
254#[cfg(feature = "with-rust_decimal")]
255#[cfg_attr(docsrs, doc(cfg(feature = "with-rust_decimal")))]
256impl From<rust_decimal::Decimal> for Value {
257 fn from(v: rust_decimal::Decimal) -> Self {
258 Value::Text(v.to_string())
259 }
260}
261#[cfg(feature = "with-rust_decimal")]
262impl ValueRefInto for rust_decimal::Decimal {}
263
264#[cfg(test)]
265mod tests {
266 use super::*;
267
268 #[test]
271 fn literals() {
272 assert_eq!(Value::from(true).to_literal(), "1");
273 assert_eq!(Value::from("it's").to_literal(), "'it''s'");
274 assert_eq!(Value::from(vec![0xAB, 0x01]).to_literal(), "X'AB01'");
275 assert_eq!(Value::from(Option::<i32>::None), Value::Null);
276 assert_eq!(Value::from(2.0f64).to_literal(), "2.0");
277 }
278}