packages feed

calamity-0.1.25.1: src/Calamity/Types/Upgradeable.hs

-- | Things that can be upgraded from snowflakes to their full data
module Calamity.Types.Upgradeable
    ( Upgradeable(..) ) where

import           Calamity.Cache.Eff
import           Calamity.Client.Types
import           Calamity.HTTP                  as H
import           Calamity.Internal.Utils
import qualified Calamity.Internal.SnowflakeMap as SM
import           Calamity.Types.Model.Channel
import           Calamity.Types.Model.Guild
import           Calamity.Types.Model.User
import           Calamity.Types.Snowflake

import           Control.Applicative
import           Control.Lens

import           Data.Generics.Sum.Constructors

import qualified Polysemy                       as P
import qualified Polysemy.Fail                  as P
import qualified Polysemy.NonDet                as P

-- | A typeclass that represents snowflakes that can be upgraded to their
-- complete data, either through the cache or HTTP.
class Upgradeable a ids | a -> ids, ids -> a where
  -- | Upgrade a snowflake to it's complete data.
  --
  -- If it existed in the cache then it is returned from there, otherwise we
  -- fetch from HTTP and update the cache on success.
  upgrade :: BotC r => ids -> P.Sem r (Maybe a)

maybeToAlt :: Alternative f => Maybe a -> f a
maybeToAlt (Just x) = pure x
maybeToAlt Nothing = empty

instance Upgradeable User (Snowflake User) where
  upgrade uid = P.runNonDetMaybe ((getUser uid >>= maybeToAlt) <|> gethttp)
    where
      gethttp = P.failToNonDet $ do
        Right u <- invoke $ H.GetUser uid
        setUser u
        pure u

instance Upgradeable Member (Snowflake Guild, Snowflake Member) where
  upgrade (gid, mid) = P.runNonDetMaybe (getcache <|> gethttp)
    where
      getcache = P.failToNonDet $ do
        Just g <- getGuild gid
        Just m <- pure (g ^. #members . at mid)
        pure m
      gethttp = P.failToNonDet $ do
        Right m <- invoke $ H.GetGuildMember gid (coerceSnowflake @_ @User mid)
        -- getcache could have failed becuase the member wasn't cached
        updateGuild gid (#members . at mid ?~ m)
        pure m

instance Upgradeable Guild (Snowflake Guild) where
  upgrade gid = P.runNonDetMaybe ((getGuild gid >>= maybeToAlt) <|> gethttp)
    where
      gethttp = P.failToNonDet $ do
        Right g <- invoke $ H.GetGuild gid
        pure g

insertChannel :: BotC r => Channel -> P.Sem r ()
insertChannel (DMChannel' dm) = setDM dm
insertChannel (GuildChannel' ch) =
  updateGuild (getID ch) (#channels . at (getID @GuildChannel ch) ?~ ch)
insertChannel _ = pure ()

instance Upgradeable Channel (Snowflake Channel) where
  upgrade cid = P.runNonDetMaybe (getcacheDM <|> getcacheGuild <|> gethttp)
    where
      getcacheDM = DMChannel' <<$>> getDM (coerceSnowflake cid) >>= maybeToAlt
      getcacheGuild = GuildChannel' <<$>> getGuildChannel (coerceSnowflake cid) >>= maybeToAlt
      gethttp = P.failToNonDet $ do
        Right c <- invoke $ H.GetChannel cid
        insertChannel c
        pure c

instance Upgradeable GuildChannel (Snowflake GuildChannel) where
  upgrade cid = P.runNonDetMaybe (getcache <|> gethttp)
    where
      getcache = getGuildChannel (coerceSnowflake cid) >>= maybeToAlt
      gethttp = P.failToNonDet $ do
        Right c <- invoke $ H.GetChannel (coerceSnowflake @_ @Channel cid)
        insertChannel c
        maybeToAlt (c ^? _Ctor @"GuildChannel'")

instance Upgradeable VoiceChannel (Snowflake VoiceChannel) where
    upgrade s = upgrade (coerceSnowflake @_ @Channel s) <&> \case
            Just (GuildChannel' (GuildVoiceChannel vc)) -> Just vc
            _ -> Nothing

instance Upgradeable DMChannel (Snowflake DMChannel) where
    upgrade s = upgrade (coerceSnowflake @_ @Channel s) <&> \case
            Just (DMChannel' dc) -> Just dc
            _ -> Nothing

instance Upgradeable GroupChannel (Snowflake GroupChannel) where
    upgrade s = upgrade (coerceSnowflake @_ @Channel s) <&> \case
            Just (GroupChannel' gc) -> Just gc
            _ -> Nothing

instance Upgradeable TextChannel (Snowflake TextChannel) where
    upgrade s = upgrade (coerceSnowflake @_ @Channel s) <&> \case
            Just (GuildChannel' (GuildTextChannel tc)) -> Just tc
            _ -> Nothing

instance Upgradeable Category (Snowflake Category) where
    upgrade s = upgrade (coerceSnowflake @_ @Channel s) <&> \case
            Just (GuildChannel' (GuildCategory c)) -> Just c
            _ -> Nothing


instance Upgradeable Emoji (Snowflake Guild, Snowflake Emoji) where
  upgrade (gid, eid) = P.runNonDetMaybe (getcache <|> gethttp)
    where
      getcache = P.failToNonDet $ do
        Just g <- getGuild gid
        Just m <- pure (g ^. #emojis . at eid)
        pure m
      gethttp = P.failToNonDet $ do
        Right e <- invoke $ H.GetGuildEmoji gid eid
        updateGuild gid (#emojis . at eid ?~ e)
        pure e

instance Upgradeable Role (Snowflake Guild, Snowflake Role) where
  upgrade (gid, rid) = P.runNonDetMaybe (getcache <|> gethttp)
    where
      getcache = P.failToNonDet $ do
        Just g <- getGuild gid
        Just r <- pure (g ^. #roles . at rid)
        pure r
      gethttp = P.failToNonDet $ do
        Right rs <- invoke $ H.GetGuildRoles gid
        let sm = SM.fromList rs
        updateGuild gid (#roles <>~ sm)
        Just r <- pure (sm ^. at rid)
        pure r