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