use crate::composite_type::{CompositeTypeMacro, handle_composite_type_macro};
use crate::lifetimes::anonymize_lifetimes;
use proc_macro2::TokenStream as TokenStream2;
use quote::{ToTokens, quote};
use syn::parse::{Parse, ParseStream};
use syn::spanned::Spanned;
use syn::{GenericArgument, Token};
use super::metadata::{FunctionMetadataTypeEntity, SqlMapping};
#[derive(Debug, Clone)]
pub struct UsedType {
pub original_ty: syn::Type,
pub resolved_ty: syn::Type,
pub resolved_ty_inner: Option<syn::Type>,
pub composite_type: Option<CompositeTypeMacro>,
pub variadic: bool,
pub default: Option<String>,
pub optional: bool,
pub result: bool,
}
impl UsedType {
pub fn new(ty: syn::Type) -> syn::Result<Self> {
let original_ty = ty.clone();
let (resolved_ty, default) = match ty.clone() {
syn::Type::Macro(macro_pat) => {
let mac = ¯o_pat.mac;
let archetype = mac.path.segments.last().expect("No last segment");
match archetype.ident.to_string().as_str() {
"default" => {
let (maybe_resolved_ty, default) = handle_default_macro(mac)?;
(maybe_resolved_ty, default)
}
_ => (syn::Type::Macro(macro_pat), None),
}
}
original => (original, None),
};
let (resolved_ty, composite_type) = match resolved_ty {
syn::Type::Macro(macro_pat) => {
let mac = ¯o_pat.mac;
match &*mac.path.segments.last().expect("No last segment").ident.to_string() {
"default" => {
return Err(syn::Error::new(
mac.span(),
"default!(default!()) not supported, use it only once",
))?;
}
"composite_type" => {
let composite_macro = handle_composite_type_macro(mac)?;
let ty = composite_macro.expand_with_lifetime();
(ty, Some(composite_macro))
}
_ => (syn::Type::Macro(macro_pat), None),
}
}
syn::Type::Path(path) => {
let segments = &path.path;
let last = segments
.segments
.last()
.ok_or(syn::Error::new(path.span(), "Could not read last segment of path"))?;
match last.ident.to_string().as_str() {
"Option" => resolve_option_inner(path)?,
"Result" => resolve_result_inner(path)?,
"Vec" => resolve_vec_inner(path)?,
"VariadicArray" => resolve_variadic_array_inner(path)?,
"Array" => resolve_array_inner(path)?,
_ => (syn::Type::Path(path), None),
}
}
original => (original, None),
};
let (resolved_ty, variadic, optional, result) = match resolved_ty {
syn::Type::Path(type_path) => {
let path = &type_path.path;
let last_segment = path.segments.last().ok_or(syn::Error::new(
path.span(),
"No last segment found while scanning path",
))?;
let ident_string = last_segment.ident.to_string();
match ident_string.as_str() {
"Result" => {
if let syn::PathArguments::AngleBracketed(angles) = &last_segment.arguments
&& let syn::GenericArgument::Type(inner_ty) =
angles.args.first().ok_or(syn::Error::new(
angles.span(),
"No inner arg for Result<T, E> found",
))?
{
match inner_ty {
syn::Type::Path(inner_type_path) => {
let path = &inner_type_path.path;
let last_segment = inner_type_path.path.segments.last().ok_or(
syn::Error::new(
path.span(),
"No last segment found while scanning path",
),
)?;
let ident_string = last_segment.ident.to_string();
match ident_string.as_str() {
"VariadicArray" => {
(syn::Type::Path(type_path.clone()), true, true, false)
}
"Option" => {
(syn::Type::Path(type_path.clone()), false, true, true)
}
"Nullable" | "Internal" => {
(syn::Type::Path(type_path.clone()), false, true, false)
}
_ => {
(syn::Type::Path(type_path.clone()), false, false, true)
}
}
}
_ => (syn::Type::Path(type_path.clone()), false, false, true),
}
} else {
return Err(syn::Error::new(
type_path.span(),
"Unexpected Item found inside `Result` (expected `<T>`)",
));
}
}
"Option" => {
if let syn::PathArguments::AngleBracketed(angles) = &last_segment.arguments
&& let syn::GenericArgument::Type(inner_ty) =
angles.args.first().ok_or(syn::Error::new(
angles.span(),
"No inner arg for Option<T> found",
))?
{
match inner_ty {
syn::Type::Path(inner_type_path) => {
let path = &inner_type_path.path;
let last_segment =
path.segments.last().ok_or(syn::Error::new(
path.span(),
"No last segment found while scanning path",
))?;
let ident_string = last_segment.ident.to_string();
match ident_string.as_str() {
"VariadicArray" => {
(syn::Type::Path(type_path.clone()), true, true, false)
}
_ => {
(syn::Type::Path(type_path.clone()), false, true, false)
}
}
}
_ => (syn::Type::Path(type_path.clone()), false, true, false),
}
} else {
return Err(syn::Error::new(
type_path.span(),
"Unexpected Item found inside `Option` (expected `<T>`)",
));
}
}
"VariadicArray" => (syn::Type::Path(type_path), true, false, false),
"Nullable" | "Internal" => (syn::Type::Path(type_path), false, true, false),
_ => (syn::Type::Path(type_path), false, false, false),
}
}
original => (original, false, false, false),
};
let mut resolved_ty_inner: Option<syn::Type> = None;
if result
&& let syn::Type::Path(tp) = &resolved_ty
&& let Some(first_segment) = tp.path.segments.first()
&& let syn::PathArguments::AngleBracketed(ab) = &first_segment.arguments
&& let Some(syn::GenericArgument::Type(ty)) = ab.args.first()
{
resolved_ty_inner = Some(ty.clone());
}
Ok(Self {
original_ty,
resolved_ty,
resolved_ty_inner,
optional,
result,
variadic,
default,
composite_type,
})
}
pub fn entity_tokens(&self) -> syn::Expr {
let mut resolved_ty = self.resolved_ty.clone();
let mut resolved_ty_inner = self.resolved_ty_inner.clone().unwrap_or(resolved_ty.clone());
anonymize_lifetimes(&mut resolved_ty);
anonymize_lifetimes(&mut resolved_ty_inner);
let resolved_ty_string = resolved_ty.to_token_stream().to_string();
let composite_type = self.composite_type.clone().map(|v| v.expr);
let composite_type_iter = composite_type.iter();
let has_explicit_composite = composite_type.is_some();
let variadic = &self.variadic;
let optional = self.optional;
let default = self.default.iter();
let metadata: syn::Expr = if has_explicit_composite {
syn::parse_quote! {
::pgrx::pgrx_sql_entity_graph::metadata::FunctionMetadataTypeEntity::sql_only(
<#resolved_ty as ::pgrx::pgrx_sql_entity_graph::metadata::SqlTranslatable>::argument_sql(),
<#resolved_ty as ::pgrx::pgrx_sql_entity_graph::metadata::SqlTranslatable>::return_sql(),
)
}
} else {
syn::parse_quote! {
{
use ::pgrx::pgrx_sql_entity_graph::metadata::SqlTranslatable;
<#resolved_ty>::entity()
}
}
};
syn::parse_quote! {
::pgrx::pgrx_sql_entity_graph::UsedTypeEntity {
ty_source: #resolved_ty_string,
full_path: #resolved_ty_string,
composite_type: None #( .unwrap_or(Some(#composite_type_iter)) )*,
variadic: #variadic,
default: None #( .unwrap_or(Some(#default)) )*,
optional: #optional,
metadata: #metadata,
}
}
}
pub fn section_len_tokens(&self) -> TokenStream2 {
let mut resolved_ty = self.resolved_ty.clone();
anonymize_lifetimes(&mut resolved_ty);
let resolved_ty_string = resolved_ty.to_token_stream().to_string();
let composite_type = self.composite_type.clone().map(|v| v.expr);
let has_explicit_composite = composite_type.is_some();
let default = self.default.clone();
let composite_len = composite_type
.map(|expr| {
quote! {
::pgrx::pgrx_sql_entity_graph::section::bool_len()
+ ::pgrx::pgrx_sql_entity_graph::section::str_len(#expr)
}
})
.unwrap_or_else(|| quote! { ::pgrx::pgrx_sql_entity_graph::section::bool_len() });
let default_len = default
.as_ref()
.map(|value| {
quote! {
::pgrx::pgrx_sql_entity_graph::section::bool_len()
+ ::pgrx::pgrx_sql_entity_graph::section::str_len(#value)
}
})
.unwrap_or_else(|| quote! { ::pgrx::pgrx_sql_entity_graph::section::bool_len() });
let metadata_len = if has_explicit_composite {
quote! {
::pgrx::pgrx_sql_entity_graph::section::function_metadata_type_len(
None,
<#resolved_ty as ::pgrx::pgrx_sql_entity_graph::metadata::SqlTranslatable>::ARGUMENT_SQL,
<#resolved_ty as ::pgrx::pgrx_sql_entity_graph::metadata::SqlTranslatable>::RETURN_SQL,
)
}
} else {
quote! {
::pgrx::pgrx_sql_entity_graph::section::function_metadata_type_len(
Some(
<#resolved_ty as ::pgrx::pgrx_sql_entity_graph::metadata::SqlTranslatable>::TYPE_IDENT
),
<#resolved_ty as ::pgrx::pgrx_sql_entity_graph::metadata::SqlTranslatable>::ARGUMENT_SQL,
<#resolved_ty as ::pgrx::pgrx_sql_entity_graph::metadata::SqlTranslatable>::RETURN_SQL,
)
}
};
quote! {
::pgrx::pgrx_sql_entity_graph::section::str_len(#resolved_ty_string)
+ ::pgrx::pgrx_sql_entity_graph::section::str_len(#resolved_ty_string)
+ (#composite_len)
+ ::pgrx::pgrx_sql_entity_graph::section::bool_len()
+ (#default_len)
+ ::pgrx::pgrx_sql_entity_graph::section::bool_len()
+ #metadata_len
}
}
pub fn section_writer_tokens(&self, writer: TokenStream2) -> TokenStream2 {
let mut resolved_ty = self.resolved_ty.clone();
anonymize_lifetimes(&mut resolved_ty);
let resolved_ty_string = resolved_ty.to_token_stream().to_string();
let composite_type = self.composite_type.clone().map(|v| v.expr);
let has_explicit_composite = composite_type.is_some();
let variadic = self.variadic;
let optional = self.optional;
let default = self.default.clone();
let composite_writer = composite_type
.map(|expr| quote! { .bool(true).str(#expr) })
.unwrap_or_else(|| quote! { .bool(false) });
let default_writer = default
.as_ref()
.map(|value| quote! { .bool(true).str(#value) })
.unwrap_or_else(|| quote! { .bool(false) });
let metadata_writer = if has_explicit_composite {
quote! {
.function_metadata_type(
None,
<#resolved_ty as ::pgrx::pgrx_sql_entity_graph::metadata::SqlTranslatable>::ARGUMENT_SQL,
<#resolved_ty as ::pgrx::pgrx_sql_entity_graph::metadata::SqlTranslatable>::RETURN_SQL,
)
}
} else {
quote! {
.function_metadata_type(
Some((
<#resolved_ty as ::pgrx::pgrx_sql_entity_graph::metadata::SqlTranslatable>::TYPE_IDENT,
<#resolved_ty as ::pgrx::pgrx_sql_entity_graph::metadata::SqlTranslatable>::TYPE_ORIGIN,
)),
<#resolved_ty as ::pgrx::pgrx_sql_entity_graph::metadata::SqlTranslatable>::ARGUMENT_SQL,
<#resolved_ty as ::pgrx::pgrx_sql_entity_graph::metadata::SqlTranslatable>::RETURN_SQL,
)
}
};
quote! {
#writer
.str(#resolved_ty_string)
.str(#resolved_ty_string)
#composite_writer
.bool(#variadic)
#default_writer
.bool(#optional)
#metadata_writer
}
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct UsedTypeEntity<'a> {
pub ty_source: &'a str,
pub full_path: &'a str,
pub composite_type: Option<&'a str>,
pub variadic: bool,
pub default: Option<&'a str>,
pub optional: bool,
pub metadata: FunctionMetadataTypeEntity<'a>,
}
impl crate::TypeIdentifiable for UsedTypeEntity<'_> {
fn type_ident(&self) -> &str {
self.metadata
.type_ident()
.expect("explicit composite SQL doesn't participate in type-ident matching")
}
fn ty_name(&self) -> &str {
self.full_path
}
}
impl UsedTypeEntity<'_> {
pub(crate) fn resolution(&self) -> Option<(&str, crate::metadata::TypeOrigin)> {
match self.metadata.resolution {
Some(resolution) => Some((resolution.type_ident, resolution.type_origin)),
None => None,
}
}
pub(crate) fn needs_type_resolution(&self) -> bool {
self.metadata.needs_type_resolution()
}
pub(crate) fn emits_argument_sql(&self) -> bool {
!matches!(self.metadata.argument_sql, Ok(SqlMapping::Skip))
}
}
fn resolve_vec_inner(
original: syn::TypePath,
) -> syn::Result<(syn::Type, Option<CompositeTypeMacro>)> {
let segments = &original.path;
let last = segments
.segments
.last()
.ok_or(syn::Error::new(original.span(), "Could not read last segment of path"))?;
if let syn::PathArguments::AngleBracketed(path_arg) = &last.arguments
&& let Some(syn::GenericArgument::Type(ty)) = path_arg.args.last()
{
match ty.clone() {
syn::Type::Macro(macro_pat) => {
let mac = ¯o_pat.mac;
let archetype = mac.path.segments.last().expect("No last segment");
match archetype.ident.to_string().as_str() {
"default" => Err(syn::Error::new(
mac.span(),
"`Vec<default!(T, default)>` not supported, choose `default!(Vec<T>, ident)` instead",
)),
"composite_type" => {
let composite_mac = handle_composite_type_macro(mac)?;
let comp_ty = composite_mac.expand_with_lifetime();
let sql = Some(composite_mac);
let ty = syn::parse_quote! {
Vec<#comp_ty>
};
Ok((ty, sql))
}
_ => Ok((syn::Type::Path(original), None)),
}
}
syn::Type::Path(arg_type_path) => {
let last = arg_type_path
.path
.segments
.last()
.ok_or(syn::Error::new(arg_type_path.span(), "No last segment in type path"))?;
if last.ident == "Option" {
let (inner_ty, expr) = resolve_option_inner(arg_type_path)?;
let wrapped_ty = syn::parse_quote! {
Vec<#inner_ty>
};
Ok((wrapped_ty, expr))
} else {
Ok((syn::Type::Path(original), None))
}
}
_ => Ok((syn::Type::Path(original), None)),
}
} else {
Ok((syn::Type::Path(original), None))
}
}
#[cfg(test)]
mod tests {
use super::UsedType;
use quote::ToTokens;
use syn::parse_quote;
#[test]
fn internal_is_marked_optional() {
let used_ty = UsedType::new(parse_quote!(::pgrx::datum::Internal)).unwrap();
let tokens = used_ty.entity_tokens().into_token_stream().to_string();
assert!(tokens.contains("optional : true"));
}
#[test]
fn nullable_is_marked_optional() {
let used_ty = UsedType::new(parse_quote!(::pgrx::nullable::Nullable<i32>)).unwrap();
let tokens = used_ty.entity_tokens().into_token_stream().to_string();
assert!(tokens.contains("optional : true"));
}
}
fn resolve_variadic_array_inner(
mut original: syn::TypePath,
) -> syn::Result<(syn::Type, Option<CompositeTypeMacro>)> {
let original_span = original.span();
let last = original
.path
.segments
.last_mut()
.ok_or(syn::Error::new(original_span, "Could not read last segment of path"))?;
if let syn::PathArguments::AngleBracketed(ref mut path_arg) = last.arguments
&& let Some(syn::GenericArgument::Type(ty)) = path_arg.args.last()
{
match ty.clone() {
syn::Type::Macro(macro_pat) => {
let mac = ¯o_pat.mac;
let archetype = mac.path.segments.last().expect("No last segment");
match archetype.ident.to_string().as_str() {
"default" => Err(syn::Error::new(
mac.span(),
"`VariadicArray<default!(T, default)>` not supported, choose `default!(VariadicArray<T>, ident)` instead",
)),
"composite_type" => {
let composite_mac = handle_composite_type_macro(mac)?;
let comp_ty = composite_mac.expand_with_lifetime();
let sql = Some(composite_mac);
let ty = syn::parse_quote! {
::pgrx::datum::VariadicArray<'_, #comp_ty>
};
Ok((ty, sql))
}
_ => Ok((syn::Type::Path(original), None)),
}
}
syn::Type::Path(arg_type_path) => {
let last = arg_type_path
.path
.segments
.last()
.ok_or(syn::Error::new(arg_type_path.span(), "No last segment in type path"))?;
if last.ident == "Option" {
let (inner_ty, expr) = resolve_option_inner(arg_type_path)?;
let wrapped_ty = syn::parse_quote! {
::pgrx::datum::VariadicArray<'_, #inner_ty>
};
Ok((wrapped_ty, expr))
} else {
Ok((syn::Type::Path(original), None))
}
}
_ => Ok((syn::Type::Path(original), None)),
}
} else {
Ok((syn::Type::Path(original), None))
}
}
fn resolve_array_inner(
mut original: syn::TypePath,
) -> syn::Result<(syn::Type, Option<CompositeTypeMacro>)> {
let original_span = original.span();
let last = original
.path
.segments
.last_mut()
.ok_or(syn::Error::new(original_span, "Could not read last segment of path"))?;
if let syn::PathArguments::AngleBracketed(ref mut path_arg) = last.arguments
&& let Some(syn::GenericArgument::Type(ty)) = path_arg.args.last()
{
match ty.clone() {
syn::Type::Macro(macro_pat) => {
let mac = ¯o_pat.mac;
let archetype = mac.path.segments.last().expect("No last segment");
match archetype.ident.to_string().as_str() {
"default" => Err(syn::Error::new(
mac.span(),
"`VariadicArray<default!(T, default)>` not supported, choose `default!(VariadicArray<T>, ident)` instead",
)),
"composite_type" => {
let composite_mac = handle_composite_type_macro(mac)?;
let comp_ty = composite_mac.expand_with_lifetime();
let sql = Some(composite_mac);
let ty = syn::parse_quote! {
::pgrx::datum::Array<'_, #comp_ty>
};
Ok((ty, sql))
}
_ => Ok((syn::Type::Path(original), None)),
}
}
syn::Type::Path(arg_type_path) => {
let last = arg_type_path
.path
.segments
.last()
.ok_or(syn::Error::new(arg_type_path.span(), "No last segment in type path"))?;
match last.ident.to_string().as_str() {
"Option" => {
let (inner_ty, expr) = resolve_option_inner(arg_type_path)?;
let wrapped_ty = syn::parse_quote! {
::pgrx::datum::Array<'_, #inner_ty>
};
Ok((wrapped_ty, expr))
}
_ => Ok((syn::Type::Path(original), None)),
}
}
_ => Ok((syn::Type::Path(original), None)),
}
} else {
Ok((syn::Type::Path(original), None))
}
}
fn resolve_option_inner(
original: syn::TypePath,
) -> syn::Result<(syn::Type, Option<CompositeTypeMacro>)> {
let segments = &original.path;
let last = segments
.segments
.last()
.ok_or(syn::Error::new(original.span(), "Could not read last segment of path"))?;
if let syn::PathArguments::AngleBracketed(path_arg) = &last.arguments
&& let Some(syn::GenericArgument::Type(ty)) = path_arg.args.first()
{
match ty.clone() {
syn::Type::Macro(macro_pat) => {
let mac = ¯o_pat.mac;
let archetype = mac.path.segments.last().expect("No last segment");
match archetype.ident.to_string().as_str() {
"composite_type" => {
let composite_mac = handle_composite_type_macro(mac)?;
let comp_ty = composite_mac.expand_with_lifetime();
let sql = Some(composite_mac);
let ty = syn::parse_quote! {
Option<#comp_ty>
};
Ok((ty, sql))
}
"default" => Err(syn::Error::new(
mac.span(),
"`Option<default!(T, \"my_default\")>` not supported, choose `Option<T>` for a default of `NULL`, or `default!(T, default)` for a non-NULL default",
)),
_ => Ok((syn::Type::Path(original), None)),
}
}
syn::Type::Path(arg_type_path) => {
let last = arg_type_path
.path
.segments
.last()
.ok_or(syn::Error::new(arg_type_path.span(), "No last segment in type path"))?;
match last.ident.to_string().as_str() {
"Vec" => {
let (inner_ty, expr) = resolve_vec_inner(arg_type_path)?;
let wrapped_ty = syn::parse_quote! {
::std::option::Option<#inner_ty>
};
Ok((wrapped_ty, expr))
}
"VariadicArray" => {
let (inner_ty, expr) = resolve_variadic_array_inner(arg_type_path)?;
let wrapped_ty = syn::parse_quote! {
::std::option::Option<#inner_ty>
};
Ok((wrapped_ty, expr))
}
"Array" => {
let (inner_ty, expr) = resolve_array_inner(arg_type_path)?;
let wrapped_ty = syn::parse_quote! {
::std::option::Option<#inner_ty>
};
Ok((wrapped_ty, expr))
}
_ => Ok((syn::Type::Path(original), None)),
}
}
_ => Ok((syn::Type::Path(original), None)),
}
} else {
Ok((syn::Type::Path(original), None))
}
}
fn resolve_result_inner(
original: syn::TypePath,
) -> syn::Result<(syn::Type, Option<CompositeTypeMacro>)> {
let segments = &original.path;
let last = segments
.segments
.last()
.ok_or(syn::Error::new(original.span(), "Could not read last segment of path"))?;
let mut without_type_args = original.path.clone();
without_type_args.segments.last_mut().unwrap().arguments = syn::PathArguments::None;
let (ok_ty, err_ty) = {
if let syn::PathArguments::AngleBracketed(path_arg) = last.arguments.clone() {
let mut iter = path_arg.args.into_iter();
match (iter.next(), iter.next()) {
(None, _) => {
return Err(syn::Error::new(
last.arguments.span(),
"Cannot return a Result without type generic arguments.",
));
}
(Some(first_ty), None) => (first_ty, None),
(Some(first_ty), Some(second_ty)) => (first_ty, Some(second_ty)),
}
} else {
return Err(syn::Error::new(
last.arguments.span(),
"Cannot return a Result without type generic arguments.",
));
}
};
fn type_for_args(
no_args_path: syn::Path,
first_ty: syn::Type,
err_ty: Option<GenericArgument>,
) -> syn::Type {
match err_ty {
Some(e) => {
syn::parse_quote! {
#no_args_path<#first_ty, #e>
}
}
None => {
syn::parse_quote! {
#no_args_path<#first_ty>
}
}
}
}
if let syn::GenericArgument::Type(ty) = ok_ty {
match ty.clone() {
syn::Type::Macro(macro_pat) => {
let mac = ¯o_pat.mac;
let archetype = mac.path.segments.last().expect("No last segment");
match archetype.ident.to_string().as_str() {
"composite_type" => {
let composite_mac = handle_composite_type_macro(mac)?;
let comp_ty = composite_mac.expand_with_lifetime();
let sql = Some(composite_mac);
let ty = type_for_args(without_type_args, comp_ty, err_ty);
Ok((ty, sql))
}
"default" => Err(syn::Error::new(
mac.span(),
"`Result<default!(T, default), E>` not supported, choose `default!(Result<T, E>, ident)` instead",
)),
_ => Ok((syn::Type::Path(original), None)),
}
}
syn::Type::Path(arg_type_path) => {
let last = arg_type_path
.path
.segments
.last()
.ok_or(syn::Error::new(arg_type_path.span(), "No last segment in type path"))?;
match last.ident.to_string().as_str() {
"Option" => {
let (inner_ty, expr) = resolve_option_inner(arg_type_path)?;
let wrapped_ty = type_for_args(without_type_args, inner_ty, err_ty);
Ok((wrapped_ty, expr))
}
"Vec" => {
let (inner_ty, expr) = resolve_vec_inner(arg_type_path)?;
let wrapped_ty = type_for_args(without_type_args, inner_ty, err_ty);
Ok((wrapped_ty, expr))
}
"VariadicArray" => {
let (inner_ty, expr) = resolve_variadic_array_inner(arg_type_path)?;
let wrapped_ty = type_for_args(without_type_args, inner_ty, err_ty);
Ok((wrapped_ty, expr))
}
"Array" => {
let (inner_ty, expr) = resolve_array_inner(arg_type_path)?;
let wrapped_ty = type_for_args(without_type_args, inner_ty, err_ty);
Ok((wrapped_ty, expr))
}
_ => Ok((syn::Type::Path(original), None)),
}
}
_ => Ok((syn::Type::Path(original), None)),
}
} else {
Ok((syn::Type::Path(original), None))
}
}
fn handle_default_macro(mac: &syn::Macro) -> syn::Result<(syn::Type, Option<String>)> {
let out: DefaultMacro = mac.parse_body()?;
let true_ty = out.ty;
match out.expr {
syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(def), .. }) => {
let value = def.value();
Ok((true_ty, Some(value)))
}
syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Float(def), .. }) => {
let value = def.base10_digits();
Ok((true_ty, Some(value.to_string())))
}
syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Int(def), .. }) => {
let value = def.base10_digits();
Ok((true_ty, Some(value.to_string())))
}
syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Bool(def), .. }) => {
let value = def.value();
Ok((true_ty, Some(value.to_string())))
}
syn::Expr::Unary(syn::ExprUnary { op: syn::UnOp::Neg(_), ref expr, .. }) => match &**expr {
syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Int(def), .. }) => {
let value = def.base10_digits();
Ok((true_ty, Some("-".to_owned() + value)))
}
_ => Err(syn::Error::new(
mac.span(),
format!("Unrecognized UnaryExpr in `default!()` macro, got: {:?}", out.expr),
)),
},
syn::Expr::Path(syn::ExprPath { path: syn::Path { ref segments, .. }, .. }) => {
let last = segments.last().expect("No last segment");
let last_string = last.ident.to_string();
if last_string == "NULL" {
Ok((true_ty, Some(last_string)))
} else {
Err(syn::Error::new(
mac.span(),
format!(
"Unable to parse default value of `default!()` macro, got: {:?}",
out.expr
),
))
}
}
_ => Err(syn::Error::new(
mac.span(),
format!("Unable to parse default value of `default!()` macro, got: {:?}", out.expr),
)),
}
}
#[derive(Debug, Clone)]
pub(crate) struct DefaultMacro {
ty: syn::Type,
pub(crate) expr: syn::Expr,
}
impl Parse for DefaultMacro {
fn parse(input: ParseStream) -> Result<Self, syn::Error> {
let ty = input.parse()?;
let _comma: Token![,] = input.parse()?;
let expr = input.parse()?;
Ok(Self { ty, expr })
}
}