cido 0.2.0

Core traits and implementations for indexing with cido
Documentation
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) {
    // have to do this, because we need to check if alias exist.
    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> {}