1mod jsonb;
25use jsonb::{OptionalRawJsonb, RawJsonb};
26
27pub use dibs_jsonb::Jsonb;
28
29extern crate alloc;
30
31use alloc::string::{String, ToString};
32use alloc::vec::Vec;
33
34use facet_core::{Facet, Shape, StructKind, Type, UserType};
35use facet_reflect::{AllocError, Partial, ReflectError, ShapeMismatchError};
36use tokio_postgres::Row;
37
38#[derive(Debug)]
40pub enum Error {
41 MissingColumn {
43 column: String,
45 },
46 TypeMismatch {
48 column: String,
50 expected: &'static Shape,
52 source: tokio_postgres::Error,
54 },
55 Reflect(ReflectError),
57 Alloc(AllocError),
59 ShapeMismatch(ShapeMismatchError),
61 NotAStruct {
63 shape: &'static Shape,
65 },
66 UnsupportedType {
68 field: String,
70 shape: &'static Shape,
72 },
73 Jsonb {
75 column: String,
77 message: String,
79 },
80}
81
82impl core::fmt::Display for Error {
83 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
84 match self {
85 Error::MissingColumn { column } => write!(f, "missing column: {column}"),
86 Error::TypeMismatch {
87 column, expected, ..
88 } => {
89 write!(
90 f,
91 "type mismatch for column '{column}': expected {expected}"
92 )
93 }
94 Error::Reflect(e) => write!(f, "reflection error: {e}"),
95 Error::Alloc(e) => write!(f, "allocation error: {e}"),
96 Error::ShapeMismatch(e) => write!(f, "shape mismatch: {e}"),
97 Error::NotAStruct { shape } => {
98 write!(f, "cannot deserialize row into non-struct type: {shape}")
99 }
100 Error::UnsupportedType { field, shape } => {
101 write!(f, "unsupported type for field '{field}': {shape}")
102 }
103 Error::Jsonb { column, message } => {
104 write!(f, "JSONB error for column '{column}': {message}")
105 }
106 }
107 }
108}
109
110impl std::error::Error for Error {
111 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
112 match self {
113 Error::TypeMismatch { source, .. } => Some(source),
114 Error::Reflect(e) => Some(e),
115 Error::Alloc(e) => Some(e),
116 Error::ShapeMismatch(e) => Some(e),
117 _ => None,
118 }
119 }
120}
121
122impl From<ReflectError> for Error {
123 fn from(e: ReflectError) -> Self {
124 Error::Reflect(e)
125 }
126}
127
128impl From<AllocError> for Error {
129 fn from(e: AllocError) -> Self {
130 Error::Alloc(e)
131 }
132}
133
134impl From<ShapeMismatchError> for Error {
135 fn from(e: ShapeMismatchError) -> Self {
136 Error::ShapeMismatch(e)
137 }
138}
139
140pub type Result<T> = core::result::Result<T, Error>;
142
143pub fn from_row<T: Facet<'static>>(row: &Row) -> Result<T> {
165 let partial = Partial::alloc_owned::<T>()?;
166 let partial = deserialize_row_into(row, partial, T::SHAPE)?;
167 let heap_value = partial.build()?;
168 Ok(heap_value.materialize()?)
169}
170
171fn deserialize_row_into(
173 row: &Row,
174 partial: Partial<'static, false>,
175 shape: &'static Shape,
176) -> Result<Partial<'static, false>> {
177 let struct_def = match &shape.ty {
178 Type::User(UserType::Struct(s)) if s.kind == StructKind::Struct => s,
179 _ => {
180 return Err(Error::NotAStruct { shape });
181 }
182 };
183
184 let mut partial = partial;
185 let num_fields = struct_def.fields.len();
186 let mut fields_set = alloc::vec![false; num_fields];
187
188 for (idx, field) in struct_def.fields.iter().enumerate() {
189 let column_name = field.rename.unwrap_or(field.name);
190
191 let column_idx = match row.columns().iter().position(|c| c.name() == column_name) {
193 Some(idx) => idx,
194 None => {
195 partial =
197 partial
198 .set_nth_field_to_default(idx)
199 .map_err(|_| Error::MissingColumn {
200 column: column_name.to_string(),
201 })?;
202 fields_set[idx] = true;
203 continue;
204 }
205 };
206
207 partial = partial.begin_field(field.name)?;
208 partial = deserialize_column(row, column_idx, column_name, partial, field.shape())?;
209 partial = partial.end()?;
210 fields_set[idx] = true;
211 }
212
213 Ok(partial)
214}
215
216fn deserialize_column(
218 row: &Row,
219 column_idx: usize,
220 column_name: &str,
221 partial: Partial<'static, false>,
222 shape: &'static Shape,
223) -> Result<Partial<'static, false>> {
224 let mut partial = partial;
225
226 if shape.decl_id == Option::<()>::SHAPE.decl_id {
228 return deserialize_option_column(row, column_idx, column_name, partial, shape);
229 }
230
231 match &shape.ty {
233 _ if shape == i8::SHAPE => {
235 let val: i8 = get_column(row, column_idx, column_name, shape)?;
236 partial = partial.set(val)?;
237 }
238 _ if shape == i16::SHAPE => {
239 let val: i16 = get_column(row, column_idx, column_name, shape)?;
240 partial = partial.set(val)?;
241 }
242 _ if shape == i32::SHAPE => {
243 let val: i32 = get_column(row, column_idx, column_name, shape)?;
244 partial = partial.set(val)?;
245 }
246 _ if shape == i64::SHAPE => {
247 let val: i64 = get_column(row, column_idx, column_name, shape)?;
248 partial = partial.set(val)?;
249 }
250
251 _ if shape == u8::SHAPE => {
254 let val: i16 = get_column(row, column_idx, column_name, shape)?;
255 partial = partial.set(val as u8)?;
256 }
257 _ if shape == u16::SHAPE => {
258 let val: i32 = get_column(row, column_idx, column_name, shape)?;
259 partial = partial.set(val as u16)?;
260 }
261 _ if shape == u32::SHAPE => {
262 let val: i64 = get_column(row, column_idx, column_name, shape)?;
263 partial = partial.set(val as u32)?;
264 }
265 _ if shape == u64::SHAPE => {
266 let val: i64 = get_column(row, column_idx, column_name, shape)?;
268 partial = partial.set(val as u64)?;
269 }
270
271 _ if shape == f32::SHAPE => {
273 let val: f32 = get_column(row, column_idx, column_name, shape)?;
274 partial = partial.set(val)?;
275 }
276 _ if shape == f64::SHAPE => {
277 let val: f64 = get_column(row, column_idx, column_name, shape)?;
278 partial = partial.set(val)?;
279 }
280
281 _ if shape == bool::SHAPE => {
283 let val: bool = get_column(row, column_idx, column_name, shape)?;
284 partial = partial.set(val)?;
285 }
286
287 _ if shape == String::SHAPE => {
289 let val: String = get_column(row, column_idx, column_name, shape)?;
290 partial = partial.set(val)?;
291 }
292
293 _ if shape == <Vec<u8>>::SHAPE => {
295 let val: Vec<u8> = get_column(row, column_idx, column_name, shape)?;
296 partial = partial.set(val)?;
297 }
298
299 _ if shape == <Vec<String>>::SHAPE => {
301 let val: Vec<String> = get_column(row, column_idx, column_name, shape)?;
302 partial = partial.set(val)?;
303 }
304
305 _ if shape == <Vec<i64>>::SHAPE => {
307 let val: Vec<i64> = get_column(row, column_idx, column_name, shape)?;
308 partial = partial.set(val)?;
309 }
310
311 _ if shape == <Vec<i32>>::SHAPE => {
313 let val: Vec<i32> = get_column(row, column_idx, column_name, shape)?;
314 partial = partial.set(val)?;
315 }
316
317 #[cfg(feature = "rust_decimal")]
319 _ if shape == rust_decimal::Decimal::SHAPE => {
320 let val: rust_decimal::Decimal = get_column(row, column_idx, column_name, shape)?;
321 partial = partial.set(val)?;
322 }
323
324 #[cfg(feature = "jiff02")]
326 _ if shape == jiff::Timestamp::SHAPE => {
327 let val: jiff::Timestamp = get_column(row, column_idx, column_name, shape)?;
328 partial = partial.set(val)?;
329 }
330
331 #[cfg(feature = "jiff02")]
333 _ if shape == jiff::civil::DateTime::SHAPE => {
334 let val: jiff::civil::DateTime = get_column(row, column_idx, column_name, shape)?;
335 partial = partial.set(val)?;
336 }
337
338 _ if shape.decl_id == Jsonb::<()>::SHAPE.decl_id => {
340 partial = deserialize_jsonb_column(row, column_idx, column_name, partial, shape)?;
341 }
342
343 _ => {
345 if shape.vtable.has_parse() {
346 let val: String = get_column(row, column_idx, column_name, shape)?;
348 partial = partial.parse_from_str(&val)?;
349 } else {
350 return Err(Error::UnsupportedType {
351 field: column_name.to_string(),
352 shape,
353 });
354 }
355 }
356 }
357
358 Ok(partial)
359}
360
361fn deserialize_option_column(
363 row: &Row,
364 column_idx: usize,
365 column_name: &str,
366 partial: Partial<'static, false>,
367 shape: &'static Shape,
368) -> Result<Partial<'static, false>> {
369 let inner_shape = shape.inner.expect("Option must have inner shape");
370 let mut partial = partial;
371
372 macro_rules! try_option {
375 ($t:ty) => {{
376 let val: Option<$t> = get_column(row, column_idx, column_name, shape)?;
377 match val {
378 Some(v) => {
379 partial = partial.begin_some()?;
380 partial = partial.set(v)?;
381 partial = partial.end()?;
382 }
383 None => {
384 partial = partial.set_default()?;
385 }
386 }
387 return Ok(partial);
388 }};
389 }
390
391 macro_rules! try_option_unsigned {
393 ($signed:ty, $unsigned:ty) => {{
394 let val: Option<$signed> = get_column(row, column_idx, column_name, shape)?;
395 match val {
396 Some(v) => {
397 partial = partial.begin_some()?;
398 partial = partial.set(v as $unsigned)?;
399 partial = partial.end()?;
400 }
401 None => {
402 partial = partial.set_default()?;
403 }
404 }
405 return Ok(partial);
406 }};
407 }
408
409 if inner_shape == i8::SHAPE {
411 try_option!(i8);
412 } else if inner_shape == i16::SHAPE {
413 try_option!(i16);
414 } else if inner_shape == i32::SHAPE {
415 try_option!(i32);
416 } else if inner_shape == i64::SHAPE {
417 try_option!(i64);
418 } else if inner_shape == u8::SHAPE {
419 try_option_unsigned!(i16, u8);
420 } else if inner_shape == u16::SHAPE {
421 try_option_unsigned!(i32, u16);
422 } else if inner_shape == u32::SHAPE {
423 try_option_unsigned!(i64, u32);
424 } else if inner_shape == u64::SHAPE {
425 try_option_unsigned!(i64, u64);
426 } else if inner_shape == f32::SHAPE {
427 try_option!(f32);
428 } else if inner_shape == f64::SHAPE {
429 try_option!(f64);
430 } else if inner_shape == bool::SHAPE {
431 try_option!(bool);
432 } else if inner_shape == String::SHAPE {
433 try_option!(String);
434 }
435
436 #[cfg(feature = "rust_decimal")]
437 if inner_shape == rust_decimal::Decimal::SHAPE {
438 try_option!(rust_decimal::Decimal);
439 }
440
441 #[cfg(feature = "jiff02")]
442 if inner_shape == jiff::Timestamp::SHAPE {
443 try_option!(jiff::Timestamp);
444 }
445
446 #[cfg(feature = "jiff02")]
447 if inner_shape == jiff::civil::DateTime::SHAPE {
448 try_option!(jiff::civil::DateTime);
449 }
450
451 if inner_shape.decl_id == Jsonb::<()>::SHAPE.decl_id {
453 let val: OptionalRawJsonb = get_column(row, column_idx, column_name, shape)?;
455 match val.0 {
456 Some(raw_bytes) => {
457 partial = partial.begin_some()?;
458 partial = deserialize_jsonb_bytes(&raw_bytes, partial, inner_shape, column_name)?;
459 partial = partial.end()?;
460 }
461 None => {
462 partial = partial.set_default()?;
463 }
464 }
465 return Ok(partial);
466 }
467
468 if inner_shape.vtable.has_parse() {
470 let val: Option<String> = get_column(row, column_idx, column_name, shape)?;
471 match val {
472 Some(s) => {
473 partial = partial.begin_some()?;
474 partial = partial.parse_from_str(&s)?;
475 partial = partial.end()?;
476 }
477 None => {
478 partial = partial.set_default()?;
479 }
480 }
481 return Ok(partial);
482 }
483
484 Err(Error::UnsupportedType {
485 field: column_name.to_string(),
486 shape: inner_shape,
487 })
488}
489
490fn get_column<'a, T>(row: &'a Row, idx: usize, name: &str, shape: &'static Shape) -> Result<T>
492where
493 T: postgres_types::FromSql<'a>,
494{
495 row.try_get::<_, T>(idx).map_err(|e| Error::TypeMismatch {
496 column: name.to_string(),
497 expected: shape,
498 source: e,
499 })
500}
501
502fn deserialize_jsonb_column(
504 row: &Row,
505 column_idx: usize,
506 column_name: &str,
507 partial: Partial<'static, false>,
508 shape: &'static Shape,
509) -> Result<Partial<'static, false>> {
510 let raw_jsonb: RawJsonb = get_column(row, column_idx, column_name, shape)?;
512 deserialize_jsonb_bytes(&raw_jsonb.0, partial, shape, column_name)
513}
514
515fn deserialize_jsonb_bytes(
517 raw_bytes: &[u8],
518 mut partial: Partial<'static, false>,
519 _shape: &'static Shape,
520 column_name: &str,
521) -> Result<Partial<'static, false>> {
522 if raw_bytes.is_empty() {
523 return Err(Error::Jsonb {
524 column: column_name.to_string(),
525 message: "empty JSONB data".to_string(),
526 });
527 }
528
529 if raw_bytes[0] != 1 {
531 return Err(Error::Jsonb {
532 column: column_name.to_string(),
533 message: format!("unsupported JSONB version: {}", raw_bytes[0]),
534 });
535 }
536
537 let json_bytes = &raw_bytes[1..];
539
540 partial = partial.begin_nth_field(0)?;
542
543 partial = facet_json::from_slice_into(json_bytes, partial).map_err(|e| Error::Jsonb {
545 column: column_name.to_string(),
546 message: format!("{e}"),
547 })?;
548
549 partial = partial.end()?;
550
551 Ok(partial)
552}