mssql_types/
value.rs

1//! SQL value representation.
2
3use bytes::Bytes;
4
5/// A SQL value that can represent any SQL Server data type.
6///
7/// This enum provides a type-safe way to handle SQL values that may be
8/// of various types, including NULL.
9#[derive(Debug, Clone, PartialEq)]
10pub enum SqlValue {
11    /// NULL value.
12    Null,
13    /// Boolean value (BIT).
14    Bool(bool),
15    /// 8-bit unsigned integer (TINYINT).
16    TinyInt(u8),
17    /// 16-bit signed integer (SMALLINT).
18    SmallInt(i16),
19    /// 32-bit signed integer (INT).
20    Int(i32),
21    /// 64-bit signed integer (BIGINT).
22    BigInt(i64),
23    /// 32-bit floating point (REAL).
24    Float(f32),
25    /// 64-bit floating point (FLOAT).
26    Double(f64),
27    /// String value (CHAR, VARCHAR, NCHAR, NVARCHAR, TEXT, NTEXT).
28    String(String),
29    /// Binary value (BINARY, VARBINARY, IMAGE).
30    Binary(Bytes),
31    /// Decimal value (DECIMAL, NUMERIC, MONEY, SMALLMONEY).
32    #[cfg(feature = "decimal")]
33    Decimal(rust_decimal::Decimal),
34    /// UUID value (UNIQUEIDENTIFIER).
35    #[cfg(feature = "uuid")]
36    Uuid(uuid::Uuid),
37    /// Date value (DATE).
38    #[cfg(feature = "chrono")]
39    Date(chrono::NaiveDate),
40    /// Time value (TIME).
41    #[cfg(feature = "chrono")]
42    Time(chrono::NaiveTime),
43    /// DateTime value (DATETIME, DATETIME2, SMALLDATETIME).
44    #[cfg(feature = "chrono")]
45    DateTime(chrono::NaiveDateTime),
46    /// DateTimeOffset value (DATETIMEOFFSET).
47    #[cfg(feature = "chrono")]
48    DateTimeOffset(chrono::DateTime<chrono::FixedOffset>),
49    /// JSON value (JSON type in SQL Server 2016+).
50    #[cfg(feature = "json")]
51    Json(serde_json::Value),
52    /// XML value (XML type).
53    Xml(String),
54}
55
56impl SqlValue {
57    /// Check if the value is NULL.
58    #[must_use]
59    pub fn is_null(&self) -> bool {
60        matches!(self, Self::Null)
61    }
62
63    /// Get the value as a bool, if it is one.
64    #[must_use]
65    pub fn as_bool(&self) -> Option<bool> {
66        match self {
67            Self::Bool(v) => Some(*v),
68            _ => None,
69        }
70    }
71
72    /// Get the value as an i32, if it is one.
73    #[must_use]
74    pub fn as_i32(&self) -> Option<i32> {
75        match self {
76            Self::Int(v) => Some(*v),
77            Self::SmallInt(v) => Some(*v as i32),
78            Self::TinyInt(v) => Some(*v as i32),
79            _ => None,
80        }
81    }
82
83    /// Get the value as an i64, if it is one.
84    #[must_use]
85    pub fn as_i64(&self) -> Option<i64> {
86        match self {
87            Self::BigInt(v) => Some(*v),
88            Self::Int(v) => Some(*v as i64),
89            Self::SmallInt(v) => Some(*v as i64),
90            Self::TinyInt(v) => Some(*v as i64),
91            _ => None,
92        }
93    }
94
95    /// Get the value as an f64, if it is one.
96    #[must_use]
97    pub fn as_f64(&self) -> Option<f64> {
98        match self {
99            Self::Double(v) => Some(*v),
100            Self::Float(v) => Some(*v as f64),
101            _ => None,
102        }
103    }
104
105    /// Get the value as a string slice, if it is one.
106    #[must_use]
107    pub fn as_str(&self) -> Option<&str> {
108        match self {
109            Self::String(v) => Some(v),
110            Self::Xml(v) => Some(v),
111            _ => None,
112        }
113    }
114
115    /// Get the value as bytes, if it is binary.
116    #[must_use]
117    pub fn as_bytes(&self) -> Option<&[u8]> {
118        match self {
119            Self::Binary(v) => Some(v),
120            _ => None,
121        }
122    }
123
124    /// Get the type name as a string.
125    #[must_use]
126    pub fn type_name(&self) -> &'static str {
127        match self {
128            Self::Null => "NULL",
129            Self::Bool(_) => "BIT",
130            Self::TinyInt(_) => "TINYINT",
131            Self::SmallInt(_) => "SMALLINT",
132            Self::Int(_) => "INT",
133            Self::BigInt(_) => "BIGINT",
134            Self::Float(_) => "REAL",
135            Self::Double(_) => "FLOAT",
136            Self::String(_) => "NVARCHAR",
137            Self::Binary(_) => "VARBINARY",
138            #[cfg(feature = "decimal")]
139            Self::Decimal(_) => "DECIMAL",
140            #[cfg(feature = "uuid")]
141            Self::Uuid(_) => "UNIQUEIDENTIFIER",
142            #[cfg(feature = "chrono")]
143            Self::Date(_) => "DATE",
144            #[cfg(feature = "chrono")]
145            Self::Time(_) => "TIME",
146            #[cfg(feature = "chrono")]
147            Self::DateTime(_) => "DATETIME2",
148            #[cfg(feature = "chrono")]
149            Self::DateTimeOffset(_) => "DATETIMEOFFSET",
150            #[cfg(feature = "json")]
151            Self::Json(_) => "JSON",
152            Self::Xml(_) => "XML",
153        }
154    }
155}
156
157impl Default for SqlValue {
158    fn default() -> Self {
159        Self::Null
160    }
161}
162
163impl From<bool> for SqlValue {
164    fn from(v: bool) -> Self {
165        Self::Bool(v)
166    }
167}
168
169impl From<i32> for SqlValue {
170    fn from(v: i32) -> Self {
171        Self::Int(v)
172    }
173}
174
175impl From<i64> for SqlValue {
176    fn from(v: i64) -> Self {
177        Self::BigInt(v)
178    }
179}
180
181impl From<f32> for SqlValue {
182    fn from(v: f32) -> Self {
183        Self::Float(v)
184    }
185}
186
187impl From<f64> for SqlValue {
188    fn from(v: f64) -> Self {
189        Self::Double(v)
190    }
191}
192
193impl From<String> for SqlValue {
194    fn from(v: String) -> Self {
195        Self::String(v)
196    }
197}
198
199impl From<&str> for SqlValue {
200    fn from(v: &str) -> Self {
201        Self::String(v.to_owned())
202    }
203}
204
205impl<T> From<Option<T>> for SqlValue
206where
207    T: Into<SqlValue>,
208{
209    fn from(v: Option<T>) -> Self {
210        match v {
211            Some(v) => v.into(),
212            None => Self::Null,
213        }
214    }
215}
216
217#[cfg(feature = "uuid")]
218impl From<uuid::Uuid> for SqlValue {
219    fn from(v: uuid::Uuid) -> Self {
220        Self::Uuid(v)
221    }
222}
223
224#[cfg(feature = "decimal")]
225impl From<rust_decimal::Decimal> for SqlValue {
226    fn from(v: rust_decimal::Decimal) -> Self {
227        Self::Decimal(v)
228    }
229}
230
231#[cfg(feature = "chrono")]
232impl From<chrono::NaiveDate> for SqlValue {
233    fn from(v: chrono::NaiveDate) -> Self {
234        Self::Date(v)
235    }
236}
237
238#[cfg(feature = "chrono")]
239impl From<chrono::NaiveDateTime> for SqlValue {
240    fn from(v: chrono::NaiveDateTime) -> Self {
241        Self::DateTime(v)
242    }
243}
244
245#[cfg(feature = "json")]
246impl From<serde_json::Value> for SqlValue {
247    fn from(v: serde_json::Value) -> Self {
248        Self::Json(v)
249    }
250}