cido 0.2.0

Core traits and implementations for indexing with cido
Documentation
use std::hash::Hash;

use sqlx::Postgres;

use crate::stable_hash;

/// Alias for a GenericSingleton
///
/// This type should generally be preferred as eventually a `Singleton` won't even need to be stored
/// to the database to take up space
pub type Singleton = GenericSingleton<bool>;

/// Represents an id that has a single value
pub struct GenericSingleton<T: Default>(T);

impl<T: Default> core::fmt::Debug for GenericSingleton<T> {
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    f.debug_tuple("Singleton").finish()
  }
}

impl<T: Clone + Default> Clone for GenericSingleton<T> {
  fn clone(&self) -> Self {
    Self::default()
  }
}

impl<T: Default> PartialEq for GenericSingleton<T> {
  fn eq(&self, _other: &Self) -> bool {
    true
  }
}

impl<T: Default> Eq for GenericSingleton<T> {}

impl<T: Default> Hash for GenericSingleton<T> {
  fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
    0_u8.hash(state)
  }
}

impl<T: Default> PartialOrd for GenericSingleton<T> {
  fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
    Some(self.cmp(&other))
  }
}

impl<T: Default> Ord for GenericSingleton<T> {
  fn cmp(&self, _other: &Self) -> std::cmp::Ordering {
    std::cmp::Ordering::Equal
  }
}

impl<T: Default> Default for GenericSingleton<T> {
  fn default() -> Self {
    Self(T::default())
  }
}

impl<'r, T: Default + sqlx::Decode<'r, Postgres>> sqlx::Decode<'r, Postgres>
  for GenericSingleton<T>
{
  fn decode(
    value: <Postgres as sqlx::Database>::ValueRef<'r>,
  ) -> Result<Self, sqlx::error::BoxDynError> {
    T::decode(value).map(Self)
  }
}

impl<'q, T: Default + sqlx::Encode<'q, Postgres>> sqlx::Encode<'q, Postgres>
  for GenericSingleton<T>
{
  fn encode_by_ref(
    &self,
    buf: &mut <Postgres as sqlx::Database>::ArgumentBuffer<'q>,
  ) -> Result<sqlx::encode::IsNull, Box<dyn std::error::Error + Send + Sync>> {
    self.0.encode_by_ref(buf)
  }
  fn encode(
    self,
    buf: &mut <Postgres as sqlx::Database>::ArgumentBuffer<'q>,
  ) -> Result<sqlx::encode::IsNull, Box<dyn std::error::Error + Send + Sync>> {
    self.0.encode(buf)
  }
  fn produces(&self) -> Option<<Postgres as sqlx::Database>::TypeInfo> {
    self.0.produces()
  }
  fn size_hint(&self) -> usize {
    self.0.size_hint()
  }
}

impl<T: Default + sqlx::Type<Postgres>> sqlx::Type<Postgres> for GenericSingleton<T> {
  fn type_info() -> <Postgres as sqlx::Database>::TypeInfo {
    <T>::type_info()
  }

  fn compatible(ty: &<Postgres as sqlx::Database>::TypeInfo) -> bool {
    <T>::compatible(ty)
  }
}

impl<T: Default + sqlx::postgres::PgHasArrayType> sqlx::postgres::PgHasArrayType
  for GenericSingleton<T>
{
  fn array_type_info() -> sqlx::postgres::PgTypeInfo {
    T::array_type_info()
  }
}

impl<T: Default + async_graphql::InputType> async_graphql::InputType for GenericSingleton<T> {
  type RawValueType = T::RawValueType;

  fn type_name() -> std::borrow::Cow<'static, str> {
    T::type_name()
  }
  fn create_type_info(registry: &mut async_graphql::registry::Registry) -> String {
    T::create_type_info(registry)
  }
  fn parse(value: Option<async_graphql::Value>) -> async_graphql::InputValueResult<Self> {
    T::parse(value)
      .map(|_| Self::default())
      .map_err(async_graphql::InputValueError::propagate)
  }
  fn to_value(&self) -> async_graphql::Value {
    self.0.to_value()
  }
  fn as_raw_value(&self) -> Option<&Self::RawValueType> {
    self.0.as_raw_value()
  }

  fn qualified_type_name() -> String {
    T::qualified_type_name()
  }
}

impl<T: Default + async_graphql::ScalarType + async_graphql::InputType> async_graphql::ScalarType
  for GenericSingleton<T>
{
  fn parse(value: async_graphql::Value) -> async_graphql::InputValueResult<Self> {
    <T as async_graphql::ScalarType>::parse(value)
      .map(|_| Self::default())
      .map_err(|e| async_graphql::InputValueError::propagate(e))
  }

  fn to_value(&self) -> async_graphql::Value {
    async_graphql::ScalarType::to_value(&self.0)
  }
}

impl<T: Default + async_graphql::OutputType> async_graphql::OutputType for GenericSingleton<T> {
  /// Type the name.
  fn type_name() -> std::borrow::Cow<'static, str> {
    T::type_name()
  }

  /// Qualified typename.
  fn qualified_type_name() -> String {
    T::qualified_type_name()
  }

  /// Introspection type name
  ///
  /// Is the return value of field `__typename`, the interface and union
  /// should return the current type, and the others return `Type::type_name`.
  fn introspection_type_name(&self) -> std::borrow::Cow<'static, str> {
    self.0.introspection_type_name()
  }

  /// Create type information in the registry and return qualified typename.
  fn create_type_info(registry: &mut async_graphql::registry::Registry) -> String {
    T::create_type_info(registry)
  }

  /// Resolve an output value to `async_graphql::Value`.
  async fn resolve(
    &self,
    ctx: &async_graphql::ContextSelectionSet<'_>,
    field: &async_graphql::Positioned<async_graphql::parser::types::Field>,
  ) -> async_graphql::ServerResult<async_graphql::Value> {
    self.0.resolve(ctx, field).await
  }
}

impl<T: Default> stable_hash::StableHash for GenericSingleton<T> {
  fn stable_hash<H: stable_hash::StableHasher>(&self, field_address: H::Addr, state: &mut H) {
    use stable_hash::FieldAddress;
    // subgraph uses Int (i32)
    0_i32.stable_hash(field_address.child(0), state);
  }
}

impl<T: Default + crate::__internal::ToDbFilter> crate::__internal::ToDbFilter
  for GenericSingleton<T>
{
  type Filter = T::Filter;

  fn to_db_filter(self) -> Self::Filter {
    self.0.to_db_filter()
  }
}

impl<T: Default + crate::__internal::ToGraphqlFilter> crate::__internal::ToGraphqlFilter
  for GenericSingleton<T>
{
  type Filter = T::Filter;

  fn to_graphql_filter(self) -> Self::Filter {
    self.0.to_graphql_filter()
  }
}

impl<T: Default + crate::poi::Hashable> crate::poi::Hashable for GenericSingleton<T> {
  fn variant() -> crate::poi::ValueVariant {
    T::variant()
  }
}