1use syn::parse::{Parse, ParseStream, Result};
2use syn::punctuated::Punctuated;
3use syn::token::Comma;
4use syn::{Ident, LitInt, LitStr};
5
6use crate::util::{parse_eq, unknown_attribute, POSTGRES_TYPE_NOTE, POSTGRES_TYPE_NOTE_ID};
7
8enum Attr {
9 Oid(Ident, LitInt),
10 ArrayOid(Ident, LitInt),
11 Name(Ident, LitStr),
12 Schema(Ident, LitStr),
13}
14
15impl Parse for Attr {
16 fn parse(input: ParseStream) -> Result<Self> {
17 let name: Ident = input.parse()?;
18 let name_str = name.to_string();
19
20 match &*name_str {
21 "oid" => Ok(Attr::Oid(name, parse_eq(input, POSTGRES_TYPE_NOTE_ID)?)),
22 "array_oid" => Ok(Attr::ArrayOid(
23 name,
24 parse_eq(input, POSTGRES_TYPE_NOTE_ID)?,
25 )),
26 "name" => Ok(Attr::Name(name, parse_eq(input, POSTGRES_TYPE_NOTE)?)),
27 "schema" => Ok(Attr::Schema(name, parse_eq(input, POSTGRES_TYPE_NOTE)?)),
28
29 _ => Err(unknown_attribute(
30 &name,
31 &["oid", "array_oid", "name", "schema"],
32 )),
33 }
34 }
35}
36
37pub enum PostgresType {
38 Fixed(LitInt, LitInt),
39 Lookup(LitStr, Option<LitStr>),
40}
41
42impl Parse for PostgresType {
43 fn parse(input: ParseStream) -> Result<Self> {
44 let mut oid = None;
45 let mut array_oid = None;
46 let mut name = None;
47 let mut schema = None;
48
49 for attr in Punctuated::<Attr, Comma>::parse_terminated(input)? {
50 match attr {
51 Attr::Oid(ident, value) => oid = Some((ident, value)),
52 Attr::ArrayOid(ident, value) => array_oid = Some((ident, value)),
53 Attr::Name(ident, value) => name = Some((ident, value)),
54 Attr::Schema(ident, value) => schema = Some((ident, value)),
55 }
56 }
57
58 Self::validate_and_build(input, oid, array_oid, name, schema)
59 }
60}
61
62impl PostgresType {
63 pub fn validate_and_build(
64 input: ParseStream,
65 oid: Option<(Ident, LitInt)>,
66 array_oid: Option<(Ident, LitInt)>,
67 name: Option<(Ident, LitStr)>,
68 schema: Option<(Ident, LitStr)>,
69 ) -> Result<Self> {
70 let help = format!(
71 "The correct format looks like either `#[diesel({POSTGRES_TYPE_NOTE})]` or `#[diesel({POSTGRES_TYPE_NOTE_ID})]`"
72 );
73
74 if let Some((_, name)) = name {
75 if let Some((oid, _)) = oid {
76 Err(syn::Error::new(
77 oid.span(),
78 format!("unexpected `oid` when `name` is present\nhelp: {help}"),
79 ))
80 } else if let Some((array_oid, _)) = array_oid {
81 Err(syn::Error::new(
82 array_oid.span(),
83 format!("unexpected `array_oid` when `name` is present\nhelp: {help}"),
84 ))
85 } else {
86 Ok(PostgresType::Lookup(name, schema.map(|s| s.1)))
87 }
88 } else if let Some((schema, lit)) = schema {
89 Err(syn::Error::new(
90 schema.span(),
91 format!(
92 "expected `name` to be also present\n\
93 help: make sure `name` is present, `#[diesel(postgres_type(name = \"...\", schema = \"{}\"))]`", lit.value()
94 ),
95 ))
96 } else if let (Some((_, oid)), Some((_, array_oid))) = (oid, array_oid) {
97 Ok(PostgresType::Fixed(oid, array_oid))
98 } else {
99 Err(syn::Error::new(
100 input.span(),
101 format!(
102 "expected `oid` and `array_oid` attribute or `name` attribute\nhelp: {help}"
103 ),
104 ))
105 }
106 }
107}