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::pgrx_attribute::{ArgValue, PgrxArg, PgrxAttribute};
28use crate::pgrx_sql::PgrxSql;
29
30pub trait ToSql {
32 fn to_sql(&self, context: &PgrxSql) -> eyre::Result<String>;
37}
38
39#[derive(Debug, Clone, PartialEq, Eq, Hash)]
41pub struct ToSqlConfig {
42 pub enabled: bool,
43 pub content: Option<syn::LitStr>,
44}
45impl From<bool> for ToSqlConfig {
46 fn from(enabled: bool) -> Self {
47 Self { enabled, content: None }
48 }
49}
50impl From<syn::LitStr> for ToSqlConfig {
51 fn from(content: syn::LitStr) -> Self {
52 Self { enabled: true, content: Some(content) }
53 }
54}
55impl Default for ToSqlConfig {
56 fn default() -> Self {
57 Self { enabled: true, content: None }
58 }
59}
60
61const INVALID_ATTR_CONTENT: &str =
62 "expected `#[pgrx(sql = content)]`, where `content` is a boolean or string literal";
63
64impl ToSqlConfig {
65 pub fn from_attribute(attr: &Attribute) -> Result<Option<Self>, syn::Error> {
67 if attr.style != AttrStyle::Outer {
68 return Err(syn::Error::new(
69 attr.span(),
70 "#[pgrx(sql = ..)] is only valid in an outer context",
71 ));
72 }
73
74 let attr = attr.parse_args::<PgrxAttribute>()?;
75 for arg in attr.args.iter() {
76 let PgrxArg::NameValue(nv) = arg;
77 if !nv.path.is_ident("sql") {
78 continue;
79 }
80
81 return match nv.value {
82 ArgValue::Lit(Lit::Bool(ref b)) => {
83 Ok(Some(Self { enabled: b.value, content: None }))
84 }
85 ArgValue::Lit(Lit::Str(ref s)) => {
86 Ok(Some(Self { enabled: true, content: Some(s.clone()) }))
87 }
88 ArgValue::Path(ref path) => Err(syn::Error::new(path.span(), INVALID_ATTR_CONTENT)),
89 ArgValue::Lit(ref other) => {
90 Err(syn::Error::new(other.span(), INVALID_ATTR_CONTENT))
91 }
92 };
93 }
94
95 Ok(None)
96 }
97
98 pub fn from_attributes(attrs: &[Attribute]) -> Result<Option<Self>, syn::Error> {
100 if let Some(attr) = attrs.iter().find(|attr| attr.path().is_ident("pgrx")) {
101 Self::from_attribute(attr)
102 } else {
103 Ok(None)
104 }
105 }
106
107 pub fn overrides_default(&self) -> bool {
108 !self.enabled || self.content.is_some()
109 }
110
111 pub fn section_len_tokens(&self) -> TokenStream2 {
112 let content = &self.content;
113 match content {
114 Some(content) => quote! {
115 ::pgrx::pgrx_sql_entity_graph::section::bool_len()
116 + ::pgrx::pgrx_sql_entity_graph::section::bool_len()
117 + ::pgrx::pgrx_sql_entity_graph::section::str_len(#content)
118 },
119 None => quote! {
120 ::pgrx::pgrx_sql_entity_graph::section::bool_len()
121 + ::pgrx::pgrx_sql_entity_graph::section::bool_len()
122 },
123 }
124 }
125
126 pub fn section_writer_tokens(&self, writer: TokenStream2) -> TokenStream2 {
127 let enabled = self.enabled;
128 let content = &self.content;
129 match content {
130 Some(content) => quote! {
131 #writer
132 .bool(#enabled)
133 .bool(true)
134 .str(#content)
135 },
136 None => quote! {
137 #writer
138 .bool(#enabled)
139 .bool(false)
140 },
141 }
142 }
143}
144
145impl ToTokens for ToSqlConfig {
146 fn to_tokens(&self, tokens: &mut TokenStream2) {
147 let enabled = self.enabled;
148 let content = &self.content;
149 if let Some(sql) = content {
150 tokens.append_all(quote! {
151 ::pgrx::pgrx_sql_entity_graph::ToSqlConfigEntity {
152 enabled: #enabled,
153 content: Some(#sql),
154 }
155 });
156 return;
157 }
158 tokens.append_all(quote! {
159 ::pgrx::pgrx_sql_entity_graph::ToSqlConfigEntity {
160 enabled: #enabled,
161 content: None,
162 }
163 });
164 }
165}