1use chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime, Utc};
11use rust_decimal::Decimal;
12use serde::{Deserialize, Serialize};
13use std::collections::HashMap;
14use uuid::Uuid;
15
16#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
18#[allow(missing_docs)]
19pub enum Value {
20 Null,
22 Bool(bool),
24 Int8(i8),
26 Int16(i16),
28 Int32(i32),
30 Int64(i64),
32 Float32(f32),
34 Float64(f64),
36 Decimal(Decimal),
38 String(String),
40 Bytes(Vec<u8>),
42 Date(NaiveDate),
44 Time(NaiveTime),
46 DateTime(NaiveDateTime),
48 DateTimeTz(DateTime<Utc>),
50 Uuid(Uuid),
52 Json(serde_json::Value),
54 Array(Vec<Value>),
56 Interval(i64),
58 Bit(Vec<u8>),
60 Enum(String),
62 Geometry(Vec<u8>),
64 Geography(Vec<u8>),
66 Range {
68 lower: Option<Box<Value>>,
69 upper: Option<Box<Value>>,
70 lower_inclusive: bool,
71 upper_inclusive: bool,
72 },
73 Composite(HashMap<String, Value>),
75 Custom { type_name: String, data: Vec<u8> },
77}
78
79impl Value {
80 #[inline]
82 pub const fn is_null(&self) -> bool {
83 matches!(self, Self::Null)
84 }
85
86 pub fn sql_type(&self) -> &'static str {
88 match self {
89 Self::Null => "NULL",
90 Self::Bool(_) => "BOOLEAN",
91 Self::Int8(_) => "TINYINT",
92 Self::Int16(_) => "SMALLINT",
93 Self::Int32(_) => "INTEGER",
94 Self::Int64(_) => "BIGINT",
95 Self::Float32(_) => "REAL",
96 Self::Float64(_) => "DOUBLE PRECISION",
97 Self::Decimal(_) => "DECIMAL",
98 Self::String(_) => "VARCHAR",
99 Self::Bytes(_) => "BYTEA",
100 Self::Date(_) => "DATE",
101 Self::Time(_) => "TIME",
102 Self::DateTime(_) => "TIMESTAMP",
103 Self::DateTimeTz(_) => "TIMESTAMPTZ",
104 Self::Uuid(_) => "UUID",
105 Self::Json(_) => "JSONB",
106 Self::Array(_) => "ARRAY",
107 Self::Interval(_) => "INTERVAL",
108 Self::Bit(_) => "BIT",
109 Self::Enum(_) => "ENUM",
110 Self::Geometry(_) => "GEOMETRY",
111 Self::Geography(_) => "GEOGRAPHY",
112 Self::Range { .. } => "RANGE",
113 Self::Composite(_) => "COMPOSITE",
114 Self::Custom { .. } => "CUSTOM",
115 }
116 }
117
118 pub fn as_bool(&self) -> Option<bool> {
120 match self {
121 Self::Bool(b) => Some(*b),
122 Self::Int8(n) => Some(*n != 0),
123 Self::Int16(n) => Some(*n != 0),
124 Self::Int32(n) => Some(*n != 0),
125 Self::Int64(n) => Some(*n != 0),
126 Self::String(s) => match s.to_lowercase().as_str() {
127 "true" | "t" | "yes" | "y" | "1" => Some(true),
128 "false" | "f" | "no" | "n" | "0" => Some(false),
129 _ => None,
130 },
131 _ => None,
132 }
133 }
134
135 pub fn as_i64(&self) -> Option<i64> {
137 match self {
138 Self::Int8(n) => Some(i64::from(*n)),
139 Self::Int16(n) => Some(i64::from(*n)),
140 Self::Int32(n) => Some(i64::from(*n)),
141 Self::Int64(n) => Some(*n),
142 Self::Float32(n) => {
143 if n.is_finite() {
144 Some(*n as i64)
145 } else {
146 None
147 }
148 }
149 Self::Float64(n) => {
150 if n.is_finite() {
151 Some(*n as i64)
152 } else {
153 None
154 }
155 }
156 Self::Decimal(d) => d.to_string().parse().ok(),
157 Self::String(s) => s.parse().ok(),
158 _ => None,
159 }
160 }
161
162 pub fn as_f64(&self) -> Option<f64> {
164 match self {
165 Self::Int8(n) => Some(f64::from(*n)),
166 Self::Int16(n) => Some(f64::from(*n)),
167 Self::Int32(n) => Some(f64::from(*n)),
168 Self::Int64(n) => Some(*n as f64),
169 Self::Float32(n) => Some(f64::from(*n)),
170 Self::Float64(n) => Some(*n),
171 Self::Decimal(d) => d.to_string().parse().ok(),
172 Self::String(s) => s.parse().ok(),
173 _ => None,
174 }
175 }
176
177 pub fn as_str(&self) -> Option<&str> {
179 match self {
180 Self::String(s) => Some(s.as_str()),
181 Self::Enum(s) => Some(s.as_str()),
182 _ => None,
183 }
184 }
185
186 pub fn as_bytes(&self) -> Option<&[u8]> {
188 match self {
189 Self::Bytes(b) => Some(b.as_slice()),
190 Self::String(s) => Some(s.as_bytes()),
191 Self::Geometry(b) | Self::Geography(b) => Some(b.as_slice()),
192 Self::Custom { data, .. } => Some(data.as_slice()),
193 _ => None,
194 }
195 }
196
197 pub fn as_uuid(&self) -> Option<Uuid> {
199 match self {
200 Self::Uuid(u) => Some(*u),
201 Self::String(s) => Uuid::parse_str(s).ok(),
202 Self::Bytes(b) if b.len() == 16 => Uuid::from_slice(b).ok(),
203 _ => None,
204 }
205 }
206
207 pub fn as_json(&self) -> Option<&serde_json::Value> {
209 match self {
210 Self::Json(j) => Some(j),
211 _ => None,
212 }
213 }
214
215 pub fn as_string(&self) -> Option<String> {
217 match self {
218 Self::String(s) => Some(s.clone()),
219 Self::Enum(s) => Some(s.clone()),
220 Self::Int8(n) => Some(n.to_string()),
221 Self::Int16(n) => Some(n.to_string()),
222 Self::Int32(n) => Some(n.to_string()),
223 Self::Int64(n) => Some(n.to_string()),
224 Self::Float32(n) => Some(n.to_string()),
225 Self::Float64(n) => Some(n.to_string()),
226 Self::Decimal(d) => Some(d.to_string()),
227 Self::Bool(b) => Some(b.to_string()),
228 Self::Uuid(u) => Some(u.to_string()),
229 _ => None,
230 }
231 }
232}
233
234impl From<bool> for Value {
236 fn from(v: bool) -> Self {
237 Self::Bool(v)
238 }
239}
240
241impl From<i8> for Value {
242 fn from(v: i8) -> Self {
243 Self::Int8(v)
244 }
245}
246
247impl From<i16> for Value {
248 fn from(v: i16) -> Self {
249 Self::Int16(v)
250 }
251}
252
253impl From<i32> for Value {
254 fn from(v: i32) -> Self {
255 Self::Int32(v)
256 }
257}
258
259impl From<i64> for Value {
260 fn from(v: i64) -> Self {
261 Self::Int64(v)
262 }
263}
264
265impl From<f32> for Value {
266 fn from(v: f32) -> Self {
267 Self::Float32(v)
268 }
269}
270
271impl From<f64> for Value {
272 fn from(v: f64) -> Self {
273 Self::Float64(v)
274 }
275}
276
277impl From<Decimal> for Value {
278 fn from(v: Decimal) -> Self {
279 Self::Decimal(v)
280 }
281}
282
283impl From<String> for Value {
284 fn from(v: String) -> Self {
285 Self::String(v)
286 }
287}
288
289impl From<&str> for Value {
290 fn from(v: &str) -> Self {
291 Self::String(v.to_owned())
292 }
293}
294
295impl From<Vec<u8>> for Value {
296 fn from(v: Vec<u8>) -> Self {
297 Self::Bytes(v)
298 }
299}
300
301impl From<NaiveDate> for Value {
302 fn from(v: NaiveDate) -> Self {
303 Self::Date(v)
304 }
305}
306
307impl From<NaiveTime> for Value {
308 fn from(v: NaiveTime) -> Self {
309 Self::Time(v)
310 }
311}
312
313impl From<NaiveDateTime> for Value {
314 fn from(v: NaiveDateTime) -> Self {
315 Self::DateTime(v)
316 }
317}
318
319impl From<DateTime<Utc>> for Value {
320 fn from(v: DateTime<Utc>) -> Self {
321 Self::DateTimeTz(v)
322 }
323}
324
325impl From<Uuid> for Value {
326 fn from(v: Uuid) -> Self {
327 Self::Uuid(v)
328 }
329}
330
331impl From<serde_json::Value> for Value {
332 fn from(v: serde_json::Value) -> Self {
333 Self::Json(v)
334 }
335}
336
337impl<T: Into<Value>> From<Option<T>> for Value {
338 fn from(v: Option<T>) -> Self {
339 match v {
340 Some(val) => val.into(),
341 None => Self::Null,
342 }
343 }
344}
345
346impl<T: Into<Value>> From<Vec<T>> for Value {
347 fn from(v: Vec<T>) -> Self {
348 Self::Array(v.into_iter().map(Into::into).collect())
349 }
350}
351
352#[derive(Debug, Clone)]
354pub struct Row {
355 columns: Vec<String>,
357 values: Vec<Value>,
359}
360
361impl Row {
362 pub fn new(columns: Vec<String>, values: Vec<Value>) -> Self {
364 debug_assert_eq!(columns.len(), values.len());
365 Self { columns, values }
366 }
367
368 #[inline]
370 pub fn len(&self) -> usize {
371 self.columns.len()
372 }
373
374 #[inline]
376 pub fn is_empty(&self) -> bool {
377 self.columns.is_empty()
378 }
379
380 #[inline]
382 pub fn columns(&self) -> &[String] {
383 &self.columns
384 }
385
386 #[inline]
388 pub fn values(&self) -> &[Value] {
389 &self.values
390 }
391
392 #[inline]
394 pub fn get(&self, idx: usize) -> Option<&Value> {
395 self.values.get(idx)
396 }
397
398 #[inline]
400 pub fn get_index(&self, idx: usize) -> Option<&Value> {
401 self.values.get(idx)
402 }
403
404 pub fn get_by_name(&self, name: &str) -> Option<&Value> {
406 self.columns
407 .iter()
408 .position(|c| c.eq_ignore_ascii_case(name))
409 .and_then(|idx| self.values.get(idx))
410 }
411
412 pub fn into_map(self) -> HashMap<String, Value> {
414 self.columns.into_iter().zip(self.values).collect()
415 }
416}
417
418#[derive(Debug, Clone)]
420pub struct ColumnMetadata {
421 pub name: String,
423 pub type_name: String,
425 pub nullable: bool,
427 pub primary_key_ordinal: Option<u32>,
429 pub ordinal: u32,
431 pub max_length: Option<u32>,
433 pub precision: Option<u32>,
435 pub scale: Option<u32>,
437 pub default_value: Option<String>,
439 pub auto_increment: bool,
441 pub comment: Option<String>,
443}
444
445impl ColumnMetadata {
446 pub fn new(name: impl Into<String>, type_name: impl Into<String>) -> Self {
448 Self {
449 name: name.into(),
450 type_name: type_name.into(),
451 nullable: true,
452 primary_key_ordinal: None,
453 ordinal: 0,
454 max_length: None,
455 precision: None,
456 scale: None,
457 default_value: None,
458 auto_increment: false,
459 comment: None,
460 }
461 }
462
463 #[inline]
465 pub fn is_primary_key(&self) -> bool {
466 self.primary_key_ordinal.is_some()
467 }
468}
469
470#[derive(Debug, Clone)]
472pub struct TableMetadata {
473 pub schema: Option<String>,
475 pub name: String,
477 pub columns: Vec<ColumnMetadata>,
479 pub comment: Option<String>,
481}
482
483impl TableMetadata {
484 pub fn new(name: impl Into<String>) -> Self {
486 Self {
487 schema: None,
488 name: name.into(),
489 columns: Vec::new(),
490 comment: None,
491 }
492 }
493
494 pub fn qualified_name(&self) -> String {
496 match &self.schema {
497 Some(s) => format!("{}.{}", s, self.name),
498 None => self.name.clone(),
499 }
500 }
501
502 pub fn column(&self, name: &str) -> Option<&ColumnMetadata> {
504 self.columns
505 .iter()
506 .find(|c| c.name.eq_ignore_ascii_case(name))
507 }
508
509 pub fn primary_key_columns(&self) -> Vec<&ColumnMetadata> {
511 let mut pk_cols: Vec<_> = self.columns.iter().filter(|c| c.is_primary_key()).collect();
512 pk_cols.sort_by_key(|c| c.primary_key_ordinal);
513 pk_cols
514 }
515
516 pub fn column_names(&self) -> Vec<&str> {
518 self.columns.iter().map(|c| c.name.as_str()).collect()
519 }
520}
521
522#[cfg(test)]
523mod tests {
524 use super::*;
525
526 #[test]
527 fn test_value_null() {
528 assert!(Value::Null.is_null());
529 assert!(!Value::Int32(0).is_null());
530 }
531
532 #[test]
533 fn test_value_conversions() {
534 assert_eq!(Value::Bool(true).as_bool(), Some(true));
535 assert_eq!(Value::String("yes".into()).as_bool(), Some(true));
536 assert_eq!(Value::String("false".into()).as_bool(), Some(false));
537
538 assert_eq!(Value::Int32(42).as_i64(), Some(42));
539 assert_eq!(Value::Float64(1.5).as_f64(), Some(1.5));
540 }
541
542 #[test]
543 fn test_value_from_impl() {
544 let v: Value = 42_i32.into();
545 assert!(matches!(v, Value::Int32(42)));
546
547 let v: Value = "hello".into();
548 assert!(matches!(v, Value::String(s) if s == "hello"));
549
550 let v: Value = None::<i32>.into();
551 assert!(v.is_null());
552 }
553
554 #[test]
555 fn test_row_operations() {
556 let row = Row::new(
557 vec!["id".into(), "name".into()],
558 vec![Value::Int32(1), Value::String("Alice".into())],
559 );
560
561 assert_eq!(row.len(), 2);
562 assert_eq!(row.get(0), Some(&Value::Int32(1)));
563 assert_eq!(
564 row.get_by_name("name"),
565 Some(&Value::String("Alice".into()))
566 );
567 assert_eq!(
568 row.get_by_name("NAME"),
569 Some(&Value::String("Alice".into()))
570 ); }
572
573 #[test]
574 fn test_table_metadata() {
575 let mut table = TableMetadata::new("users");
576 table.schema = Some("public".into());
577 table.columns.push(ColumnMetadata {
578 name: "id".into(),
579 type_name: "integer".into(),
580 nullable: false,
581 primary_key_ordinal: Some(1),
582 ordinal: 1,
583 max_length: None,
584 precision: None,
585 scale: None,
586 default_value: None,
587 auto_increment: true,
588 comment: None,
589 });
590
591 assert_eq!(table.qualified_name(), "public.users");
592 assert_eq!(table.primary_key_columns().len(), 1);
593 assert!(table.column("id").unwrap().is_primary_key());
594 }
595}