Skip to main content

diesel_as_jsonb/
lib.rs

1#![recursion_limit = "256"]
2use heck::ToSnakeCase;
3use proc_macro::TokenStream;
4use proc_macro2::Span;
5use quote::quote;
6use syn::{DeriveInput, Ident};
7
8#[proc_macro_derive(AsJsonb)]
9pub fn asjsonb_macro_derive(input: TokenStream) -> TokenStream {
10    let ast: DeriveInput = syn::parse(input).unwrap();
11    let name = &ast.ident;
12    let scope = Ident::new(
13        &format!("{}_as_jsonb", name).to_snake_case(),
14        Span::call_site(),
15    );
16    let proxy = Ident::new(&format!("{}ValueProxy", name), Span::call_site());
17
18    let gen = quote! {
19        mod #scope {
20            use super::#name;
21            use std::io::Write;
22            use ::diesel::{AsExpression, FromSqlRow};
23            use ::diesel::sql_types::Jsonb;
24            use ::diesel::pg::{Pg, PgValue};
25            use ::diesel::serialize::{self, IsNull, Output, ToSql};
26            use ::diesel::deserialize::{self, FromSql};
27
28            #[derive(FromSqlRow, AsExpression)]
29            #[diesel(foreign_derive)]
30            #[diesel(sql_type = Jsonb)]
31            struct #proxy(#name);
32
33            impl FromSql<Jsonb, Pg> for #name {
34                fn from_sql(bytes: PgValue) -> deserialize::Result<Self> {
35                    let bytes = bytes.as_bytes();
36
37                    if bytes[0] != 1 {
38                        return Err("Unsupported JSONB encoding version".into());
39                    }
40                    serde_json::from_slice(&bytes[1..]).map_err(Into::into)
41                }
42            }
43
44            impl ToSql<Jsonb, Pg> for #name {
45                fn to_sql(&self, out: &mut Output<Pg>) -> serialize::Result {
46                    out.write_all(&[1])?;
47                    serde_json::to_writer(out, self)
48                        .map(|_| IsNull::No)
49                        .map_err(Into::into)
50                }
51            }
52        }
53    };
54    gen.into()
55}