pgrx_sql_entity_graph/to_sql/
mod.rs1pub mod entity;
19
20use std::hash::Hash;
21
22use proc_macro2::TokenStream as TokenStream2;
23use quote::{ToTokens, TokenStreamExt, quote};
24use syn::spanned::Spanned;
25use syn::{AttrStyle, Attribute, Lit};
26
27use crate::SqlGraphEntity;
28use crate::pgrx_attribute::{ArgValue, PgrxArg, PgrxAttribute};
29use crate::pgrx_sql::PgrxSql;
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 = "expected `#[pgrx(sql = content)]`, where `content` is a boolean, string, or path to a function";
83
84impl ToSqlConfig {
85 pub fn from_attribute(attr: &Attribute) -> Result<Option<Self>, syn::Error> {
87 if attr.style != AttrStyle::Outer {
88 return Err(syn::Error::new(
89 attr.span(),
90 "#[pgrx(sql = ..)] is only valid in an outer context",
91 ));
92 }
93
94 let attr = attr.parse_args::<PgrxAttribute>()?;
95 for arg in attr.args.iter() {
96 let PgrxArg::NameValue(nv) = arg;
97 if !nv.path.is_ident("sql") {
98 continue;
99 }
100
101 return match nv.value {
102 ArgValue::Path(ref callback_path) => Ok(Some(Self {
103 enabled: true,
104 callback: Some(callback_path.clone()),
105 content: None,
106 })),
107 ArgValue::Lit(Lit::Bool(ref b)) => {
108 Ok(Some(Self { enabled: b.value, callback: None, content: None }))
109 }
110 ArgValue::Lit(Lit::Str(ref s)) => {
111 Ok(Some(Self { enabled: true, callback: None, content: Some(s.clone()) }))
112 }
113 ArgValue::Lit(ref other) => {
114 Err(syn::Error::new(other.span(), INVALID_ATTR_CONTENT))
115 }
116 };
117 }
118
119 Ok(None)
120 }
121
122 pub fn from_attributes(attrs: &[Attribute]) -> Result<Option<Self>, syn::Error> {
124 if let Some(attr) = attrs.iter().find(|attr| attr.path().is_ident("pgrx")) {
125 Self::from_attribute(attr)
126 } else {
127 Ok(None)
128 }
129 }
130
131 pub fn overrides_default(&self) -> bool {
132 !self.enabled || self.callback.is_some() || self.content.is_some()
133 }
134}
135
136impl ToTokens for ToSqlConfig {
137 fn to_tokens(&self, tokens: &mut TokenStream2) {
138 let enabled = self.enabled;
139 let callback = &self.callback;
140 let content = &self.content;
141 if let Some(callback_path) = callback {
142 tokens.append_all(quote! {
143 ::pgrx::pgrx_sql_entity_graph::ToSqlConfigEntity {
144 enabled: #enabled,
145 callback: Some(#callback_path),
146 content: None,
147 }
148 });
149 return;
150 }
151 if let Some(sql) = content {
152 tokens.append_all(quote! {
153 ::pgrx::pgrx_sql_entity_graph::ToSqlConfigEntity {
154 enabled: #enabled,
155 callback: None,
156 content: Some(#sql),
157 }
158 });
159 return;
160 }
161 tokens.append_all(quote! {
162 ::pgrx::pgrx_sql_entity_graph::ToSqlConfigEntity {
163 enabled: #enabled,
164 callback: None,
165 content: None,
166 }
167 });
168 }
169}