use async_graphql::{InputType, indexmap::IndexSet};
use crate::__internal::{Direction, Transformer};
use super::{GraphqlOrderByVariant, GraphqlOrderDirection};
#[derive(Clone)]
pub enum GraphqlOrderBy<V: GraphqlOrderByVariant> {
Single(V),
Multiple(IndexSet<V>),
}
impl<V: GraphqlOrderByVariant> Default for GraphqlOrderBy<V> {
fn default() -> Self {
Self::Single(V::id())
}
}
impl<V: GraphqlOrderByVariant> GraphqlOrderBy<V> {
pub fn pair_with_direction(
&self,
direction: &super::GraphqlOrderDirection,
) -> impl Iterator<Item = (V, Direction)> {
match self {
Self::Single(order) => match direction {
GraphqlOrderDirection::Single(direction) => {
vec![(*order, *direction)]
}
GraphqlOrderDirection::Multiple(directions) => {
let direction = directions.get(0).copied().unwrap_or_default();
vec![(*order, direction)]
}
},
Self::Multiple(orders) => match direction {
GraphqlOrderDirection::Single(direction) => {
orders.iter().map(|o| (*o, *direction)).collect()
}
GraphqlOrderDirection::Multiple(directions) => orders
.iter()
.enumerate()
.map(|(i, o)| (*o, directions.get(i).copied().unwrap_or_default()))
.collect(),
},
}
.into_iter()
}
pub fn apply<'qb, T: Transformer>(
&self,
mut builder: crate::sql::QueryBuilder<
'qb,
crate::sql::OrderByClause,
sqlx::QueryBuilder<'qb, sqlx::Postgres>,
>,
direction: &super::GraphqlOrderDirection,
) -> crate::sql::QueryBuilder<
'qb,
crate::sql::OrderByClause,
sqlx::QueryBuilder<'qb, sqlx::Postgres>,
> {
match self {
Self::Single(order) => {
let field_name = order.db_field_name();
match direction {
GraphqlOrderDirection::Single(direction) => {
builder = builder.order_by::<T>(field_name, *direction);
}
GraphqlOrderDirection::Multiple(directions) => {
let direction = directions.get(0).copied().unwrap_or_default();
builder = builder.order_by::<T>(field_name, direction);
}
}
}
Self::Multiple(orders) => match direction {
GraphqlOrderDirection::Single(direction) => {
for order in orders {
builder = builder.order_by::<T>(order.db_field_name(), *direction);
}
}
GraphqlOrderDirection::Multiple(directions) => {
for (i, order) in orders.iter().enumerate() {
builder = builder.order_by::<T>(
order.db_field_name(),
directions.get(i).copied().unwrap_or_default(),
)
}
}
},
}
builder
}
pub fn id() -> Self {
Self::default()
}
pub fn with_id(mut self) -> Self {
self.push(V::id());
self
}
pub fn contains(&self, variant: &V) -> bool {
match self {
Self::Single(v) => v == variant,
Self::Multiple(set) => set.contains(variant),
}
}
pub fn push(&mut self, variant: V) {
if !self.contains(&variant) {
match self {
Self::Single(v) => {
let mut set = IndexSet::with_capacity(2);
set.insert(*v);
set.insert(variant);
*self = Self::Multiple(set);
}
Self::Multiple(set) => {
set.insert(variant);
}
}
}
}
pub fn starts_with_block_number(&self) -> bool {
match self {
Self::Single(v) => v.is_block_number(),
Self::Multiple(v) => v.iter().next().map_or(false, |v| v.is_block_number()),
}
}
}
impl<V: GraphqlOrderByVariant> InputType for GraphqlOrderBy<V> {
type RawValueType = Self;
fn type_name() -> ::std::borrow::Cow<'static, str> {
::std::borrow::Cow::Borrowed(<V as GraphqlOrderByVariant>::order_by_type_name())
}
fn create_type_info(registry: &mut async_graphql::registry::Registry) -> ::std::string::String {
registry.create_input_type::<Self, _>(async_graphql::registry::MetaTypeId::Enum, |_| {
async_graphql::registry::MetaType::Enum {
name: Self::type_name().into_owned(),
description: ::core::option::Option::None,
enum_values: V::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: Some(::std::any::type_name::<Self>()),
}
})
}
fn parse(value: Option<async_graphql::Value>) -> async_graphql::InputValueResult<Self> {
match value {
Some(val) => match val {
async_graphql::Value::String(inner) => V::_from_str(&inner)
.map(Self::Single)
.map_err(async_graphql::InputValueError::custom),
async_graphql::Value::Enum(inner) => V::_from_str(&inner)
.map(Self::Single)
.map_err(async_graphql::InputValueError::custom),
async_graphql::Value::List(mut inners) => match inners.len() {
0 => Err(async_graphql::InputValueError::custom(format_args!(
"empty {} list not allowed",
Self::type_name()
))),
1 => <V as async_graphql::InputType>::parse(Some(inners.pop().unwrap()))
.map(Self::Single)
.map_err(async_graphql::InputValueError::propagate),
_ => {
let mut fields = async_graphql::indexmap::IndexSet::with_capacity(inners.len());
for inner in inners {
fields.insert(
<V as async_graphql::InputType>::parse(Some(inner))
.map_err(async_graphql::InputValueError::propagate)?,
);
}
Ok(Self::Multiple(fields))
}
},
x => async_graphql::Result::Err(async_graphql::InputValueError::expected_type(x)),
},
None => async_graphql::Result::Err(async_graphql::InputValueError::expected_type(
value.unwrap_or_default(),
)),
}
}
fn to_value(&self) -> async_graphql::Value {
match self {
Self::Single(field) => async_graphql::InputType::to_value(field),
Self::Multiple(fields) => async_graphql::Value::List(
fields
.iter()
.map(async_graphql::InputType::to_value)
.collect(),
),
}
}
fn as_raw_value(&self) -> ::std::option::Option<&Self::RawValueType> {
::std::option::Option::Some(self)
}
}
impl<V: GraphqlOrderByVariant> async_graphql::InputObjectType for GraphqlOrderBy<V> {}