Skip to main content

haste_fhir_model/r4/
sqlx.rs

1use crate::r4::generated::resources::ResourceType;
2use sqlx::{
3    Database, Decode, Encode, Postgres,
4    encode::IsNull,
5    error::BoxDynError,
6    postgres::{PgArgumentBuffer, PgTypeInfo, PgValueRef},
7};
8use std::io::Write;
9
10#[derive(Debug, Clone)]
11pub struct FHIRJson<T: ?Sized>(pub T);
12
13impl<T> sqlx::Type<Postgres> for FHIRJson<T>
14where
15    T: serde::Serialize + serde::de::DeserializeOwned,
16{
17    fn type_info() -> PgTypeInfo {
18        PgTypeInfo::with_name("jsonb")
19    }
20
21    fn compatible(ty: &PgTypeInfo) -> bool {
22        *ty == PgTypeInfo::with_name("json") || *ty == PgTypeInfo::with_name("jsonb")
23    }
24}
25
26impl<'r, T: 'r> Decode<'r, Postgres> for FHIRJson<T>
27where
28    T: serde::Serialize + serde::Deserialize<'r>,
29{
30    fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
31        let buf = value.as_bytes()?;
32        // Need to remove first byte which is a marker for JSONB binary.
33        let resource = serde_json::from_slice::<T>(&buf[1..]);
34        Ok(FHIRJson::<T>(resource?))
35    }
36}
37
38// More effecient impl to avoid cloning the value. No need to own as writing bytes and non mutating.
39pub struct FHIRJsonRef<'a, T: ?Sized>(pub &'a T);
40impl<'a, T> sqlx::Type<Postgres> for FHIRJsonRef<'a, T>
41where
42    T: serde::Serialize + serde::de::DeserializeOwned,
43{
44    fn type_info() -> PgTypeInfo {
45        PgTypeInfo::with_name("jsonb")
46    }
47
48    fn compatible(ty: &PgTypeInfo) -> bool {
49        *ty == PgTypeInfo::with_name("json") || *ty == PgTypeInfo::with_name("jsonb")
50    }
51}
52
53impl<'q, T> Encode<'q, Postgres> for FHIRJsonRef<'q, T>
54where
55    T: serde::Serialize + serde::de::DeserializeOwned,
56{
57    fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
58        // we have a tiny amount of dynamic behavior depending if we are resolved to be JSON
59        // instead of JSONB
60
61        // buf.patch(|buf, ty: &PgTypeInfo| {
62        //     if *ty == PgTypeInfo::JSON || *ty == PgTypeInfo::JSON_ARRAY {
63        //         buf[0] = b' ';
64        //     }
65        // });
66
67        // JSONB version (as of 2020-03-20)
68        buf.push(1);
69
70        // the JSON data written to the buffer is the same regardless of parameter type
71        serde_json::to_writer(&mut **buf, &*self.0)?;
72
73        Ok(IsNull::No)
74    }
75}
76
77impl<'r, DB: Database> Decode<'r, DB> for ResourceType
78where
79    &'r str: Decode<'r, DB>,
80{
81    fn decode(
82        value: <DB as Database>::ValueRef<'r>,
83    ) -> Result<ResourceType, Box<dyn std::error::Error + 'static + Send + Sync>> {
84        let value = <&str as Decode<DB>>::decode(value)?;
85        Ok(ResourceType::try_from(value).unwrap())
86    }
87}
88
89impl<'r> Encode<'r, Postgres> for ResourceType {
90    fn encode_by_ref(
91        &self,
92        buf: &mut PgArgumentBuffer,
93    ) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
94        buf.write(self.as_ref().as_bytes())?;
95        Ok(sqlx::encode::IsNull::No)
96    }
97}