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}