use super::err;
use crate::parse::deps::*;
pub use crate::parse::{OrderBy, OrderByVariant};
use heck::ToPascalCase;
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::{
Ident,
parse::{Parse, Parser},
};
type Result = syn::Result<TokenStream>;
impl crate::codegen::CodegenWrapper<OrderBy> {
pub fn generate_order_by(&self) -> syn::Result<TokenStream> {
crate::parse::embed_generated_code(
self.embed_generated_code,
&self.name,
self.generate_order_by_inner()?,
"order_by",
)
}
fn generate_order_by_inner(&self) -> Result {
let vis = &self.vis;
let order_by_ident = &self.name;
let order_by_ident_name = order_by_ident.to_string();
let short_order_by_ident = format_ident!("{order_by_ident}Short");
let mut to_value_match_exprs = vec![];
let mut parse_match_exprs = vec![];
let mut short_parse_match_exprs = vec![];
let mut display_exprs = vec![];
let mut short_display_exprs = vec![];
let mut variants = vec![];
let mut short_variants = vec![];
let mut alias_consts_exprs = vec![];
let mut db_field_name_matcher = vec![];
let mut field_name_strs = vec![];
let mut enum_value_generator = vec![];
let num_default = self.variants.iter().filter(|v| v.default).count();
if num_default != 1 {
return Err(err(
&self.name,
"OrderBy enum must have a default variant, use #[orderby(default)] to mark it",
));
}
let num_id = self.variants.iter().filter(|v| v.id).count();
if num_id > 1 {
return Err(err(
&self.name,
"OrderBy enum must have at most one field marked as an id",
));
}
let id = self
.variants
.iter()
.find(|v| v.id)
.map(|v| {
let name = format_ident!("{}", v.name.to_string().to_pascal_case());
quote! {
#order_by_ident::#name
}
})
.unwrap_or(quote! { <Self as ::core::default::Default>::default() });
for v in self.variants.iter() {
if !v.hidden {
let field_name_str = v.graphql_name.to_string();
let variant_name = v.name.clone();
let variant_name_str = v.name.to_string();
let sql_name = v.sql_name.to_string();
v.aliases.iter().for_each(|alias| {
let alias_field_name_str = alias.to_string();
field_name_strs.push(alias_field_name_str.clone());
let alias_variant_name = format_ident!("{}", alias.to_string().to_pascal_case());
alias_consts_exprs.push(quote! {
pub const #alias_variant_name: Self = Self::#variant_name;
});
parse_match_exprs.push(quote! {
#alias_field_name_str => ::core::result::Result::Ok(#order_by_ident::#variant_name),
});
enum_value_generator.push(quote! { insert(#alias_field_name_str); });
});
to_value_match_exprs.push(quote! {
#order_by_ident::#variant_name => ::cido::__internal::async_graphql::Value::Enum(::cido::__internal::async_graphql::Name::new(#field_name_str)),
});
let parse_match_quote = quote! {
#field_name_str => ::core::result::Result::Ok(Self::#variant_name),
};
parse_match_exprs.push(parse_match_quote.clone());
let display_quote = quote! {
Self::#variant_name => ::core::fmt::Formatter::write_str(f, #variant_name_str),
};
display_exprs.push(display_quote.clone());
if v.default {
let default = quote! { #[default] };
variants.push(default.clone());
short_variants.push(default);
}
let variant_quote = quote! { #variant_name, };
variants.push(variant_quote.clone());
db_field_name_matcher.push(quote! {
Self::#variant_name => #sql_name,
});
enum_value_generator.push(quote! { insert(#field_name_str); });
if let Some(graphql_type) = &v.graphql_type {
let ee_order_by = format_ident!(
"{}OrderByShort",
Ident::parse.parse2(quote!(#graphql_type)).unwrap()
);
let ee_name = format_ident!("{}__", variant_name);
let enum_format = format!("{field_name_str}__{{t}}");
let prefix = format!("{field_name_str}__");
to_value_match_exprs.push(quote! {
Self::#ee_name(t) => ::cido::__internal::async_graphql::Value::Enum(
::cido::__internal::async_graphql::Name::new(format!(#enum_format))
),
});
parse_match_exprs.push(quote! {
s if s.starts_with(#prefix) => ::core::result::Result::Ok(Self::#ee_name(s.strip_prefix(#prefix).unwrap().parse()?)),
});
display_exprs.push(quote! {
Self::#ee_name(t) => ::core::fmt::Formatter::write_str(f, #enum_format),
});
variants.push(quote! { #ee_name(#ee_order_by), });
db_field_name_matcher.push(quote! {
Self::#ee_name(_) => #sql_name,
});
enum_value_generator.push(quote! {
{
static VALUES: ::std::sync::OnceLock<Vec<String>> = ::std::sync::OnceLock::new();
let values = VALUES.get_or_init(||#ee_order_by::iter().map(|t|format!(#enum_format)).collect());
values.iter().map(|s|s.as_str()).for_each(|v|insert(v));
}
});
} else {
field_name_strs.push(field_name_str.clone());
short_parse_match_exprs.push(parse_match_quote);
short_display_exprs.push(display_quote);
short_variants.push(variant_quote);
}
}
}
let parse_match_exprs_quote = quote! {
x => ::core::result::Result::Err(::std::format!("unknown {} field: {}", #order_by_ident_name, x).into())
};
parse_match_exprs.push(parse_match_exprs_quote.clone());
short_parse_match_exprs.push(parse_match_exprs_quote);
let attrs = self.attrs.outer_attributes();
let fn_is_block_number = self
.variants
.iter()
.find(|v| v.is_block_number)
.map(|v| {
let block_number_name = &v.name;
quote! {
fn is_block_number(&self) -> bool {
matches!(self, Self::#block_number_name)
}
}
})
.unwrap_or_else(|| {
quote! {
fn is_block_number(&self) -> bool {
false
}
}
});
let expect_non_camel_case = order_by_ident
.to_string()
.contains("_")
.then(|| quote! {#[expect(non_camel_case_types)]});
let expect_non_camel_case_short = short_order_by_ident
.to_string()
.contains("_")
.then(|| quote! {#[expect(non_camel_case_types)]});
Ok(quote! {
#(#attrs)*
#[derive(::core::default::Default, ::core::marker::Copy, ::core::clone::Clone, ::core::cmp::Eq, ::core::cmp::PartialEq, ::core::cmp::Ord, ::core::cmp::PartialOrd, ::core::hash::Hash, ::core::fmt::Debug)]
#expect_non_camel_case
#vis enum #order_by_ident {
#(#variants)*
}
#(#attrs)*
#[derive(::core::default::Default, ::core::marker::Copy, ::core::clone::Clone, ::core::cmp::Eq, ::core::cmp::PartialEq, ::core::cmp::Ord, ::core::cmp::PartialOrd, ::core::hash::Hash, ::core::fmt::Debug)]
#expect_non_camel_case_short
#vis enum #short_order_by_ident {
#(#short_variants)*
}
impl #short_order_by_ident {
pub fn iter() -> impl Iterator<Item = &'static str> {
[#(#field_name_strs),*].into_iter()
}
}
#[automatically_derived]
impl ::core::fmt::Display for #order_by_ident {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
match self {
#(#display_exprs)*
}
}
}
#[automatically_derived]
impl ::core::fmt::Display for #short_order_by_ident {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
match self {
#(#short_display_exprs)*
}
}
}
#[automatically_derived]
impl ::cido::__internal::GraphqlOrderByVariant for #order_by_ident {
fn id() -> Self {
#id
}
fn order_by_type_name() -> &'static str {
#order_by_ident_name
}
fn enum_values() -> ::cido::__internal::async_graphql::indexmap::IndexMap<&'static str, ::cido::__internal::async_graphql::registry::MetaEnumValue> {
let mut map = ::cido::__internal::async_graphql::indexmap::IndexMap::new();
let mut insert = |field: &'static str| {
map.insert(field, ::cido::__internal::async_graphql::registry::MetaEnumValue {
name: field.to_string(),
description: ::core::option::Option::None,
deprecation: ::cido::__internal::async_graphql::registry::Deprecation::NoDeprecated,
visible: if field.ends_with("__BlockNumber") {::std::option::Option::Some(|_|false)} else {::std::option::Option::None},
inaccessible: false,
tags: vec![], directive_invocations: vec![],
});
};
#(#enum_value_generator)*
map
}
fn db_field_name(&self) -> &'static str {
match self {
#(#db_field_name_matcher)*
}
}
#fn_is_block_number
}
#[automatically_derived]
impl ::std::str::FromStr for #order_by_ident {
type Err = ::std::boxed::Box<dyn ::std::error::Error + ::core::marker::Send + ::core::marker::Sync + 'static>;
fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> {
match s.trim() {
#(#parse_match_exprs)*
}
}
}
#[automatically_derived]
impl ::std::str::FromStr for #short_order_by_ident {
type Err = ::std::boxed::Box<dyn ::std::error::Error + ::core::marker::Send + ::core::marker::Sync + 'static>;
fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> {
match s.trim() {
#(#short_parse_match_exprs)*
}
}
}
#[automatically_derived]
impl ::cido::__internal::async_graphql::InputType for #order_by_ident {
type RawValueType = Self;
fn type_name() -> ::std::borrow::Cow<'static, str> {
::std::borrow::Cow::Borrowed(#order_by_ident_name)
}
fn create_type_info(registry: &mut ::cido::__internal::async_graphql::registry::Registry) -> ::std::string::String {
registry.create_input_type::<Self, _>(
::cido::__internal::async_graphql::registry::MetaTypeId::Enum,
|_| ::cido::__internal::async_graphql::registry::MetaType::Enum {
name: <Self as ::cido::__internal::async_graphql::InputType>::type_name().into_owned(),
description: ::core::option::Option::None,
enum_values: <Self as ::cido::__internal::GraphqlOrderByVariant>::enum_values().into_iter().map(|(k, v)|(k.to_string(), v)).collect(),
visible: ::std::option::Option::None,
inaccessible: false,
tags: vec![], directive_invocations: vec![],
rust_typename: ::std::option::Option::Some(::std::any::type_name::<Self>()),
}
)
}
fn parse(
value: ::std::option::Option<::cido::__internal::async_graphql::Value>,
) -> ::cido::__internal::async_graphql::InputValueResult<Self> {
match value {
::std::option::Option::None => ::cido::__internal::async_graphql::InputValueResult::Err(::cido::__internal::async_graphql::InputValueError::expected_type(value.unwrap_or_default())),
::std::option::Option::Some(val) => match val {
::cido::__internal::async_graphql::Value::String(val) => val.trim().parse().map_err(::cido::__internal::async_graphql::InputValueError::custom),
::cido::__internal::async_graphql::Value::Enum(val) => val.trim().parse().map_err(::cido::__internal::async_graphql::InputValueError::custom),
x => ::cido::__internal::async_graphql::InputValueResult::Err(::cido::__internal::async_graphql::InputValueError::expected_type(x)),
},
}
}
fn to_value(&self) -> ::cido::__internal::async_graphql::Value {
match self {
#(#to_value_match_exprs)*
}
}
fn as_raw_value(&self) -> ::std::option::Option<&Self::RawValueType> {
::std::option::Option::Some(self)
}
}
#[automatically_derived]
impl ::cido::__internal::async_graphql::InputObjectType for #order_by_ident {}
})
}
}