1pub mod numeric;
6pub mod temporal;
7
8pub use numeric::Numeric;
9pub use temporal::{Date, Time, Timestamp};
10
11use crate::protocol::types::{decode_json, decode_jsonb, decode_text_array, decode_uuid, oid};
12
13#[derive(Debug, Clone)]
15pub enum TypeError {
16 UnexpectedOid { expected: &'static str, got: u32 },
18 InvalidData(String),
20 UnexpectedNull,
22}
23
24impl std::fmt::Display for TypeError {
25 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26 match self {
27 TypeError::UnexpectedOid { expected, got } => {
28 write!(f, "Expected {} type, got OID {}", expected, got)
29 }
30 TypeError::InvalidData(msg) => write!(f, "Invalid data: {}", msg),
31 TypeError::UnexpectedNull => write!(f, "Unexpected NULL value"),
32 }
33 }
34}
35
36impl std::error::Error for TypeError {}
37
38pub trait FromPg: Sized {
40 fn from_pg(bytes: &[u8], oid: u32, format: i16) -> Result<Self, TypeError>;
46}
47
48pub trait ToPg {
50 fn to_pg(&self) -> (Vec<u8>, u32, i16);
53}
54
55impl FromPg for String {
58 fn from_pg(bytes: &[u8], _oid: u32, _format: i16) -> Result<Self, TypeError> {
59 String::from_utf8(bytes.to_vec())
60 .map_err(|e| TypeError::InvalidData(format!("Invalid UTF-8: {}", e)))
61 }
62}
63
64impl ToPg for String {
65 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
66 (self.as_bytes().to_vec(), oid::TEXT, 0)
67 }
68}
69
70impl ToPg for &str {
71 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
72 (self.as_bytes().to_vec(), oid::TEXT, 0)
73 }
74}
75
76impl FromPg for i32 {
79 fn from_pg(bytes: &[u8], _oid: u32, format: i16) -> Result<Self, TypeError> {
80 if format == 1 {
81 if bytes.len() != 4 {
83 return Err(TypeError::InvalidData(
84 "Expected 4 bytes for i32".to_string(),
85 ));
86 }
87 Ok(i32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
88 } else {
89 std::str::from_utf8(bytes)
91 .map_err(|e| TypeError::InvalidData(e.to_string()))?
92 .parse()
93 .map_err(|e| TypeError::InvalidData(format!("Invalid i32: {}", e)))
94 }
95 }
96}
97
98impl ToPg for i32 {
99 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
100 (self.to_be_bytes().to_vec(), oid::INT4, 1)
101 }
102}
103
104impl FromPg for i64 {
105 fn from_pg(bytes: &[u8], _oid: u32, format: i16) -> Result<Self, TypeError> {
106 if format == 1 {
107 if bytes.len() != 8 {
109 return Err(TypeError::InvalidData(
110 "Expected 8 bytes for i64".to_string(),
111 ));
112 }
113 Ok(i64::from_be_bytes([
114 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
115 ]))
116 } else {
117 std::str::from_utf8(bytes)
119 .map_err(|e| TypeError::InvalidData(e.to_string()))?
120 .parse()
121 .map_err(|e| TypeError::InvalidData(format!("Invalid i64: {}", e)))
122 }
123 }
124}
125
126impl ToPg for i64 {
127 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
128 (self.to_be_bytes().to_vec(), oid::INT8, 1)
129 }
130}
131
132impl FromPg for f64 {
135 fn from_pg(bytes: &[u8], _oid: u32, format: i16) -> Result<Self, TypeError> {
136 if format == 1 {
137 if bytes.len() != 8 {
139 return Err(TypeError::InvalidData(
140 "Expected 8 bytes for f64".to_string(),
141 ));
142 }
143 Ok(f64::from_be_bytes([
144 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
145 ]))
146 } else {
147 std::str::from_utf8(bytes)
149 .map_err(|e| TypeError::InvalidData(e.to_string()))?
150 .parse()
151 .map_err(|e| TypeError::InvalidData(format!("Invalid f64: {}", e)))
152 }
153 }
154}
155
156impl ToPg for f64 {
157 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
158 (self.to_be_bytes().to_vec(), oid::FLOAT8, 1)
159 }
160}
161
162impl FromPg for bool {
165 fn from_pg(bytes: &[u8], _oid: u32, format: i16) -> Result<Self, TypeError> {
166 if format == 1 {
167 Ok(bytes.first().map(|b| *b != 0).unwrap_or(false))
169 } else {
170 match bytes.first() {
172 Some(b't') | Some(b'T') | Some(b'1') => Ok(true),
173 Some(b'f') | Some(b'F') | Some(b'0') => Ok(false),
174 _ => Err(TypeError::InvalidData("Invalid boolean".to_string())),
175 }
176 }
177 }
178}
179
180impl ToPg for bool {
181 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
182 (vec![if *self { 1 } else { 0 }], oid::BOOL, 1)
183 }
184}
185
186#[derive(Debug, Clone, PartialEq)]
190pub struct Uuid(pub String);
191
192impl FromPg for Uuid {
193 fn from_pg(bytes: &[u8], oid_val: u32, format: i16) -> Result<Self, TypeError> {
194 if oid_val != oid::UUID {
195 return Err(TypeError::UnexpectedOid {
196 expected: "uuid",
197 got: oid_val,
198 });
199 }
200
201 if format == 1 && bytes.len() == 16 {
202 decode_uuid(bytes).map(Uuid).map_err(TypeError::InvalidData)
204 } else {
205 String::from_utf8(bytes.to_vec())
207 .map(Uuid)
208 .map_err(|e| TypeError::InvalidData(e.to_string()))
209 }
210 }
211}
212
213impl ToPg for Uuid {
214 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
215 (self.0.as_bytes().to_vec(), oid::UUID, 0)
217 }
218}
219
220#[derive(Debug, Clone, PartialEq)]
224pub struct Json(pub String);
225
226impl FromPg for Json {
227 fn from_pg(bytes: &[u8], oid_val: u32, _format: i16) -> Result<Self, TypeError> {
228 let json_str = if oid_val == oid::JSONB {
229 decode_jsonb(bytes).map_err(TypeError::InvalidData)?
230 } else {
231 decode_json(bytes).map_err(TypeError::InvalidData)?
232 };
233 Ok(Json(json_str))
234 }
235}
236
237impl ToPg for Json {
238 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
239 let mut buf = Vec::with_capacity(1 + self.0.len());
241 buf.push(1); buf.extend_from_slice(self.0.as_bytes());
243 (buf, oid::JSONB, 1)
244 }
245}
246
247impl FromPg for Vec<String> {
250 fn from_pg(bytes: &[u8], _oid: u32, _format: i16) -> Result<Self, TypeError> {
251 let s = std::str::from_utf8(bytes).map_err(|e| TypeError::InvalidData(e.to_string()))?;
252 Ok(decode_text_array(s))
253 }
254}
255
256impl FromPg for Vec<i64> {
257 fn from_pg(bytes: &[u8], _oid: u32, _format: i16) -> Result<Self, TypeError> {
258 let s = std::str::from_utf8(bytes).map_err(|e| TypeError::InvalidData(e.to_string()))?;
259 crate::protocol::types::decode_int_array(s).map_err(TypeError::InvalidData)
260 }
261}
262
263impl<T: FromPg> FromPg for Option<T> {
266 fn from_pg(bytes: &[u8], oid_val: u32, format: i16) -> Result<Self, TypeError> {
267 Ok(Some(T::from_pg(bytes, oid_val, format)?))
269 }
270}
271
272impl FromPg for Vec<u8> {
275 fn from_pg(bytes: &[u8], _oid: u32, _format: i16) -> Result<Self, TypeError> {
276 Ok(bytes.to_vec())
277 }
278}
279
280impl ToPg for Vec<u8> {
281 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
282 (self.clone(), oid::BYTEA, 1)
283 }
284}
285
286impl ToPg for &[u8] {
287 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
288 (self.to_vec(), oid::BYTEA, 1)
289 }
290}
291
292#[cfg(test)]
293mod tests {
294 use super::*;
295
296 #[test]
297 fn test_string_from_pg() {
298 let result = String::from_pg(b"hello", oid::TEXT, 0).unwrap();
299 assert_eq!(result, "hello");
300 }
301
302 #[test]
303 fn test_i32_from_pg_text() {
304 let result = i32::from_pg(b"42", oid::INT4, 0).unwrap();
305 assert_eq!(result, 42);
306 }
307
308 #[test]
309 fn test_i32_from_pg_binary() {
310 let bytes = 42i32.to_be_bytes();
311 let result = i32::from_pg(&bytes, oid::INT4, 1).unwrap();
312 assert_eq!(result, 42);
313 }
314
315 #[test]
316 fn test_bool_from_pg() {
317 assert!(bool::from_pg(b"t", oid::BOOL, 0).unwrap());
318 assert!(!bool::from_pg(b"f", oid::BOOL, 0).unwrap());
319 assert!(bool::from_pg(&[1], oid::BOOL, 1).unwrap());
320 assert!(!bool::from_pg(&[0], oid::BOOL, 1).unwrap());
321 }
322
323 #[test]
324 fn test_uuid_from_pg_binary() {
325 let uuid_bytes: [u8; 16] = [
326 0x55, 0x0e, 0x84, 0x00, 0xe2, 0x9b, 0x41, 0xd4, 0xa7, 0x16, 0x44, 0x66, 0x55, 0x44,
327 0x00, 0x00,
328 ];
329 let result = Uuid::from_pg(&uuid_bytes, oid::UUID, 1).unwrap();
330 assert_eq!(result.0, "550e8400-e29b-41d4-a716-446655440000");
331 }
332}