gooey-rs 0.12.1

Tile-based UI library with audio support
Documentation
/// Implements builtin controls and extendable control ID
macro_rules! impl_control_builtin {

  ( $control:ident <$payload:ty> {
      $($builtin:ident => $control_fun:path),+
    }
  ) => {
    impl_control_builtin!{
      @base $control <$payload> {
        $($builtin => $control_fun),+
      }
    }
    impl std::convert::TryFrom <$control> for Builtin {
      type Error = $control;
      fn try_from ($control (index) : $control) -> std::result::Result <Self, $control> {
        Builtin::from_repr (index).ok_or ($control (index))
      }
    }
    impl From <Builtin> for $control {
      fn from (builtin : Builtin) -> Self {
        $control (builtin as $crate::interface::controller::controls::ReprType)
      }
    }
    impl From <Builtin> for $crate::interface::controller::controls::Fun <$payload> {
      fn from (builtin : Builtin) -> Self {
        match builtin {
          $(Builtin::$builtin =>
            $crate::interface::controller::controls::Fun ($control_fun)
          ),+
        }
      }
    }
  };

  // a nil control type with no controls defined (nullary enum)
  ( $control:ident <$payload:ty> { /*nil*/ }
  ) => {
    impl_control_builtin!{
      @base $control <$payload> { }
    }
    impl std::convert::TryFrom <$control> for Builtin {
      type Error = $control;
      fn try_from (control : $control) -> std::result::Result <Self, $control> {
        Err (control)
      }
    }
    impl From <Builtin> for $control {
      fn from (_ : Builtin) -> Self {
        unreachable!()
      }
    }
    impl From <Builtin> for $crate::interface::controller::controls::Fun <$payload> {
      fn from (_ : Builtin) -> Self {
        unreachable!()
      }
    }
  };

  // private: base implementation rule
  ( @base
    $control:ident <$payload:ty> {
      $($builtin:ident => $control_fun:path),*
    }
  ) => {
    impl_control_builtin!{
      @base_enum $control <$payload> { $($builtin => $control_fun),* }
    }
    #[doc = "Concrete extensible "]
    #[doc = stringify!($control)]
    #[doc = " control callback index"]
    #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd,
      $crate::serde::Deserialize, $crate::serde::Serialize)]
    pub struct $control (pub $crate::interface::controller::controls::ReprType);
    impl $crate::interface::controller::controls::Control for $control {
      type Payload = $payload;
      type Builtin = Builtin;
    }
    impl From <$crate::interface::controller::controls::ReprType> for $control {
      fn from (index : $crate::interface::controller::controls::ReprType) -> Self {
        $control (index)
      }
    }
    impl AsRef <$crate::interface::controller::controls::ReprType> for $control {
      fn as_ref (&self) -> &$crate::interface::controller::controls::ReprType {
        &self.0
      }
    }
    impl $crate::interface::controller::controls::Id <$control> for Builtin { }
    impl $crate::interface::controller::controls::Id <$control> for
      $crate::interface::controller::controls::Nil { }
    impl From <$crate::interface::controller::controls::Nil> for $control {
      fn from (_ : $crate::interface::controller::controls::Nil) -> Self {
        unreachable!()
      }
    }
    impl std::convert::TryFrom <$control> for
      $crate::interface::controller::controls::Nil
    {
      type Error = $control;
      fn try_from (control : $control) -> std::result::Result <Self, $control> {
        Err (control)
      }
    }
  };
  // private: base enum implementation rule
  // NOTE: need to special case because repr is not allowed on zero-variant enums
  ( @base_enum
    $control:ident <$payload:ty> { }
  ) => {
    #[doc = "Built-in "]
    #[doc = stringify!($control)]
    #[doc = " control IDs"]
    #[derive(Clone, Debug, Eq, PartialEq,
      $crate::strum::EnumCount, $crate::strum::FromRepr,
      $crate::serde::Deserialize, $crate::serde::Serialize)]
    pub enum Builtin { }
  };
  // private: base enum implementation rule
  // NOTE: need to special case because repr is not allowed on zero-variant enums
  ( @base_enum
    $control:ident <$payload:ty> {
      $($builtin:ident => $control_fun:path),*
    }
  ) => {
    #[doc = "Built-in "]
    #[doc = stringify!($control)]
    #[doc = " control IDs"]
    #[derive(Clone, Debug, Eq, PartialEq,
      $crate::strum::EnumCount, $crate::strum::FromRepr,
      $crate::serde::Deserialize, $crate::serde::Serialize)]
    #[repr(u16)]
    pub enum Builtin {
      $($builtin),*
    }
  }
}

/// Defines one or more extended control [`Id`](super::Id)s for a
/// [`Control`](super::Control) type
#[macro_export]
macro_rules! extend_control {
  ( $extended:ident <$payload:ty> ($($control_path:ident)::+, $control:ident) {
      $first:ident     => $first_fun:path
      $(, $variant:ident => $extended_fun:path)*
    }
  ) => {
    #[derive(Clone, Debug, Eq, PartialEq, $crate::strum::FromRepr,
      $crate::serde::Deserialize, $crate::serde::Serialize)]
    #[repr(u16)]
    pub enum $extended {
      $first = <
        <$($control_path)::+::$control
          as $crate::interface::controller::controls::Control>::Builtin
        as $crate::strum::EnumCount
      >::COUNT as $crate::interface::controller::controls::ReprType
      $(, $variant)*
    }
    impl $crate::interface::controller::controls::Id <$($control_path)::+::$control>
      for $extended { }
    impl std::convert::TryFrom <$($control_path)::+::$control> for $extended {
      type Error = $($control_path)::+::$control;
      fn try_from (id : $($control_path)::+::$control)
        -> std::result::Result <Self, $($control_path)::+::$control>
      {
        Self::from_repr (*id.as_ref()).ok_or (id)
      }
    }
    impl From <$extended> for $($control_path)::+::$control {
      fn from (control : $extended) -> Self {
        $($control_path)::+::$control::from (
          control as $crate::interface::controller::controls::ReprType
        )
      }
    }
    impl From <$extended> for $($control_path)::+::Control <$extended> {
      fn from (extended : $extended) -> Self {
        $($control_path)::+::Control (extended.into(), Default::default())
      }
    }
    impl From <$extended> for $crate::interface::controller::controls::Fun <$payload> {
      fn from (control : $extended) -> Self {
        match control {
          $extended::$first   =>
            $crate::interface::controller::controls::Fun ($first_fun)
          $(,
          $extended::$variant =>
            $crate::interface::controller::controls::Fun ($extended_fun)
          )*
        }
      }
    }
  }
}

/// Internal macro: implements human-readable de/serialization for control newtype
macro_rules! impl_control_serdes {
  ($control:ident) => {
    /// Newtype for seralization
    #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
    pub struct Control <E :
      $crate::interface::controller::controls::Id <$control> =
      $crate::interface::controller::controls::Nil
    > (pub $control, pub std::marker::PhantomData <E>);

    impl <E : $crate::interface::controller::controls::Id <$control>>
      From <$control> for Control <E>
    {
      fn from (control : $control) -> Self {
        Control (control, Default::default())
      }
    }

    impl <E : $crate::interface::controller::controls::Id <$control>>
      From <Control <E>> for $control
    {
      fn from (binding : Control <E>) -> Self {
        binding.0
      }
    }

    impl <E : $crate::interface::controller::controls::Id <$control>>
      From <Builtin> for Control <E>
    {
      fn from (builtin : Builtin) -> Self {
        Control (builtin.into(), Default::default())
      }
    }

    struct ControlVisitor <E :
      $crate::interface::controller::controls::Id <$control> =
      $crate::interface::controller::controls::Nil
    > (std::marker::PhantomData <E>);

    impl <E : $crate::interface::controller::controls::Id <$control>>
      serde::Serialize for Control <E>
    {
      fn serialize <S : serde::Serializer> (&self, serializer : S)
        -> std::result::Result <S::Ok, S::Error>
      {
        use std::convert::TryFrom;
        use serde::ser::Error;
        let control = if let Ok (builtin) = Builtin::try_from (self.0.clone()) {
          serde_plain::to_string (&builtin).unwrap()
        } else if let Ok (application) = E::try_from (self.0.clone()) {
          serde_plain::to_string (&application).unwrap()
        } else {
          return Err (S::Error::custom (format!("invalid control index: {:?}", self.0)))
        };
        serializer.serialize_str(&control)
      }
    }

    impl <E : $crate::interface::controller::controls::Id <$control>>
      serde::de::Visitor <'_> for ControlVisitor <E>
    {
      type Value = Control <E>;
      fn expecting (&self, formatter : &mut std::fmt::Formatter) -> std::fmt::Result {
        formatter.write_str ("A control identifier")
      }
      fn visit_str <ERR : serde::de::Error> (self, value : &str)
        -> std::result::Result <Self::Value, ERR>
      {
        let control = if let Ok (builtin) = serde_plain::from_str::<Builtin> (value) {
          builtin.into()
        } else if let Ok (application) = serde_plain::from_str::<E> (value) {
          application.into()
        } else {
          return Err (ERR::custom (format!("invalid control identifier: {:?}", value)))
        };
        Ok (Control (control, Default::default()))
      }
    }

    impl <'de, E : $crate::interface::controller::controls::Id <$control>>
      serde::Deserialize <'de> for Control <E>
    {
      fn deserialize <D : serde::Deserializer <'de>> (deserializer : D)
        -> std::result::Result <Self, D::Error>
      {
        deserializer.deserialize_str (ControlVisitor (Default::default()))
      }
    }
  }
}