pgrx_sql_entity_graph/
pgrx_attribute.rs

1//LICENSE Portions Copyright 2019-2021 ZomboDB, LLC.
2//LICENSE
3//LICENSE Portions Copyright 2021-2023 Technology Concepts & Design, Inc.
4//LICENSE
5//LICENSE Portions Copyright 2023-2023 PgCentral Foundation, Inc. <contact@pgcentral.org>
6//LICENSE
7//LICENSE All rights reserved.
8//LICENSE
9//LICENSE Use of this source code is governed by the MIT license that can be found in the LICENSE file.
10/*!
11
12`#[pgrx]` attribute for Rust to SQL mapping support.
13
14> Like all of the [`sql_entity_graph`][crate] APIs, this is considered **internal**
15> to the `pgrx` framework and very subject to change between versions. While you may use this, please do it with caution.
16
17*/
18use syn::parse::{Parse, ParseStream};
19use syn::punctuated::Punctuated;
20use syn::Token;
21
22/// This struct is intended to represent the contents of the `#[pgrx]` attribute when parsed.
23///
24/// The intended usage is to parse an `Attribute`, then use `attr.parse_args::<PgrxAttribute>()?` to
25/// parse the contents of the attribute into this struct.
26///
27/// We use this rather than `Attribute::parse_meta` because it is not supported to parse bare paths
28/// as values of a `NameValueMeta`, and we want to support that to avoid conflating SQL strings with
29/// paths-as-strings. We re-use as much of the standard `parse_meta` structure types as possible though.
30pub struct PgrxAttribute {
31    pub args: Vec<PgrxArg>,
32}
33
34impl Parse for PgrxAttribute {
35    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
36        let parser = Punctuated::<PgrxArg, Token![,]>::parse_terminated;
37        let punctuated = input.call(parser)?;
38        let args = punctuated.into_pairs().map(|p| p.into_value()).collect::<Vec<_>>();
39        Ok(Self { args })
40    }
41}
42
43/// This enum is akin to `syn::Meta`, but supports a custom `NameValue` variant which allows
44/// for bare paths in the value position.
45#[derive(Debug)]
46pub enum PgrxArg {
47    NameValue(NameValueArg),
48}
49
50impl Parse for PgrxArg {
51    /// Parse `name = val` in `#[pgrx(name = val)]`
52    ///
53    /// It may seem like we leave this unhandled:
54    /// ```rust,ignore
55    /// #[pg_aggregate]
56    /// impl Aggregate for Aggregated {
57    ///     #[pgrx(immutable, parallel_safe)]
58    ///     fn state(current: _, args: _, fcinfo: _) -> Self::State {
59    ///         todo!()
60    ///     }
61    /// }
62    /// ```
63    /// However, that actually never reaches this point!
64    /// This parser only handles the direct attributes.
65    #[track_caller]
66    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
67        let path = input.parse::<syn::Path>()?;
68        if input.parse::<Token![=]>().is_ok() {
69            Ok(Self::NameValue(NameValueArg { path, value: input.parse()? }))
70        } else {
71            Err(input.error("unsupported argument to #[pgrx] in this context"))
72        }
73    }
74}
75
76/// This struct is akin to `syn::NameValueMeta`, but allows for more than just `syn::Lit` as a value.
77#[derive(Debug)]
78pub struct NameValueArg {
79    pub path: syn::Path,
80    pub value: ArgValue,
81}
82
83/// This is the type of a value that can be used in the value position of a `name = value` attribute argument.
84#[derive(Debug)]
85pub enum ArgValue {
86    Path(syn::Path),
87    Lit(syn::Lit),
88}
89
90impl Parse for ArgValue {
91    /// Parse `val` in `#[pgrx(name = val)]`
92    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
93        if input.peek(syn::Lit) {
94            return Ok(Self::Lit(input.parse()?));
95        }
96
97        Ok(Self::Path(input.parse()?))
98    }
99}