pgrx_sql_entity_graph/to_sql/
mod.rs1pub mod entity;
19
20use std::hash::Hash;
21
22use proc_macro2::TokenStream as TokenStream2;
23use quote::{quote, ToTokens, TokenStreamExt};
24use syn::spanned::Spanned;
25use syn::{AttrStyle, Attribute, Lit};
26
27use crate::pgrx_attribute::{ArgValue, PgrxArg, PgrxAttribute};
28use crate::pgrx_sql::PgrxSql;
29use crate::SqlGraphEntity;
30
31pub trait ToSql {
33 fn to_sql(&self, context: &PgrxSql) -> eyre::Result<String>;
38}
39
40pub type ToSqlFn =
49 fn(
50 &SqlGraphEntity,
51 &PgrxSql,
52 ) -> std::result::Result<String, Box<dyn std::error::Error + Send + Sync + 'static>>;
53
54#[derive(Debug, Clone, PartialEq, Eq, Hash)]
56pub struct ToSqlConfig {
57 pub enabled: bool,
58 pub callback: Option<syn::Path>,
59 pub content: Option<syn::LitStr>,
60}
61impl From<bool> for ToSqlConfig {
62 fn from(enabled: bool) -> Self {
63 Self { enabled, callback: None, content: None }
64 }
65}
66impl From<syn::Path> for ToSqlConfig {
67 fn from(path: syn::Path) -> Self {
68 Self { enabled: true, callback: Some(path), content: None }
69 }
70}
71impl From<syn::LitStr> for ToSqlConfig {
72 fn from(content: syn::LitStr) -> Self {
73 Self { enabled: true, callback: None, content: Some(content) }
74 }
75}
76impl Default for ToSqlConfig {
77 fn default() -> Self {
78 Self { enabled: true, callback: None, content: None }
79 }
80}
81
82const INVALID_ATTR_CONTENT: &str =
83 "expected `#[pgrx(sql = content)]`, where `content` is a boolean, string, or path to a function";
84
85impl ToSqlConfig {
86 pub fn from_attribute(attr: &Attribute) -> Result<Option<Self>, syn::Error> {
88 if attr.style != AttrStyle::Outer {
89 return Err(syn::Error::new(
90 attr.span(),
91 "#[pgrx(sql = ..)] is only valid in an outer context",
92 ));
93 }
94
95 let attr = attr.parse_args::<PgrxAttribute>()?;
96 for arg in attr.args.iter() {
97 let PgrxArg::NameValue(ref nv) = arg;
98 if !nv.path.is_ident("sql") {
99 continue;
100 }
101
102 return match nv.value {
103 ArgValue::Path(ref callback_path) => Ok(Some(Self {
104 enabled: true,
105 callback: Some(callback_path.clone()),
106 content: None,
107 })),
108 ArgValue::Lit(Lit::Bool(ref b)) => {
109 Ok(Some(Self { enabled: b.value, callback: None, content: None }))
110 }
111 ArgValue::Lit(Lit::Str(ref s)) => {
112 Ok(Some(Self { enabled: true, callback: None, content: Some(s.clone()) }))
113 }
114 ArgValue::Lit(ref other) => {
115 Err(syn::Error::new(other.span(), INVALID_ATTR_CONTENT))
116 }
117 };
118 }
119
120 Ok(None)
121 }
122
123 pub fn from_attributes(attrs: &[Attribute]) -> Result<Option<Self>, syn::Error> {
125 if let Some(attr) = attrs.iter().find(|attr| attr.path().is_ident("pgrx")) {
126 Self::from_attribute(attr)
127 } else {
128 Ok(None)
129 }
130 }
131
132 pub fn overrides_default(&self) -> bool {
133 !self.enabled || self.callback.is_some() || self.content.is_some()
134 }
135}
136
137impl ToTokens for ToSqlConfig {
138 fn to_tokens(&self, tokens: &mut TokenStream2) {
139 let enabled = self.enabled;
140 let callback = &self.callback;
141 let content = &self.content;
142 if let Some(callback_path) = callback {
143 tokens.append_all(quote! {
144 ::pgrx::pgrx_sql_entity_graph::ToSqlConfigEntity {
145 enabled: #enabled,
146 callback: Some(#callback_path),
147 content: None,
148 }
149 });
150 return;
151 }
152 if let Some(sql) = content {
153 tokens.append_all(quote! {
154 ::pgrx::pgrx_sql_entity_graph::ToSqlConfigEntity {
155 enabled: #enabled,
156 callback: None,
157 content: Some(#sql),
158 }
159 });
160 return;
161 }
162 tokens.append_all(quote! {
163 ::pgrx::pgrx_sql_entity_graph::ToSqlConfigEntity {
164 enabled: #enabled,
165 callback: None,
166 content: None,
167 }
168 });
169 }
170}