1use prax_query::filter::FilterValue;
4use tokio_postgres::types::{ToSql, Type};
5
6use crate::error::{PgError, PgResult};
7
8pub fn filter_value_to_sql(value: &FilterValue) -> PgResult<Box<dyn ToSql + Sync + Send>> {
10 match value {
11 FilterValue::Null => Ok(Box::new(Option::<String>::None)),
12 FilterValue::Bool(b) => Ok(Box::new(*b)),
13 FilterValue::Int(i) => Ok(Box::new(*i)),
14 FilterValue::Float(f) => Ok(Box::new(*f)),
15 FilterValue::String(s) => Ok(Box::new(s.clone())),
16 FilterValue::Json(j) => Ok(Box::new(j.clone())),
17 FilterValue::List(_) => {
18 Err(PgError::type_conversion(
21 "list values should be handled specially",
22 ))
23 }
24 }
25}
26
27pub fn filter_values_to_params(
29 values: &[FilterValue],
30) -> PgResult<Vec<Box<dyn ToSql + Sync + Send>>> {
31 values.iter().map(filter_value_to_sql).collect()
32}
33
34pub mod pg_types {
36 use super::*;
37
38 pub fn rust_type_to_pg(rust_type: &str) -> Option<Type> {
40 match rust_type {
41 "i16" => Some(Type::INT2),
42 "i32" => Some(Type::INT4),
43 "i64" => Some(Type::INT8),
44 "f32" => Some(Type::FLOAT4),
45 "f64" => Some(Type::FLOAT8),
46 "bool" => Some(Type::BOOL),
47 "String" | "&str" => Some(Type::TEXT),
48 "Vec<u8>" | "&[u8]" => Some(Type::BYTEA),
49 "chrono::NaiveDate" => Some(Type::DATE),
50 "chrono::NaiveTime" => Some(Type::TIME),
51 "chrono::NaiveDateTime" => Some(Type::TIMESTAMP),
52 "chrono::DateTime<chrono::Utc>" => Some(Type::TIMESTAMPTZ),
53 "uuid::Uuid" => Some(Type::UUID),
54 "serde_json::Value" => Some(Type::JSONB),
55 _ => None,
56 }
57 }
58
59 pub fn pg_type_to_rust(pg_type: &Type) -> &'static str {
61 match *pg_type {
62 Type::BOOL => "bool",
63 Type::INT2 => "i16",
64 Type::INT4 => "i32",
65 Type::INT8 => "i64",
66 Type::FLOAT4 => "f32",
67 Type::FLOAT8 => "f64",
68 Type::TEXT | Type::VARCHAR | Type::CHAR | Type::NAME => "String",
69 Type::BYTEA => "Vec<u8>",
70 Type::DATE => "chrono::NaiveDate",
71 Type::TIME => "chrono::NaiveTime",
72 Type::TIMESTAMP => "chrono::NaiveDateTime",
73 Type::TIMESTAMPTZ => "chrono::DateTime<chrono::Utc>",
74 Type::UUID => "uuid::Uuid",
75 Type::JSON | Type::JSONB => "serde_json::Value",
76 _ => "unknown",
77 }
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84
85 #[test]
86 fn test_filter_value_to_sql() {
87 let result = filter_value_to_sql(&FilterValue::Int(42));
88 assert!(result.is_ok());
89
90 let result = filter_value_to_sql(&FilterValue::String("test".to_string()));
91 assert!(result.is_ok());
92
93 let result = filter_value_to_sql(&FilterValue::Bool(true));
94 assert!(result.is_ok());
95 }
96
97 #[test]
98 fn test_pg_type_mapping() {
99 use pg_types::*;
100
101 assert_eq!(rust_type_to_pg("i32"), Some(Type::INT4));
102 assert_eq!(rust_type_to_pg("String"), Some(Type::TEXT));
103 assert_eq!(rust_type_to_pg("bool"), Some(Type::BOOL));
104
105 assert_eq!(pg_type_to_rust(&Type::INT4), "i32");
106 assert_eq!(pg_type_to_rust(&Type::TEXT), "String");
107 }
108}