Skip to content

Commit

Permalink
Use DataKinds
Browse files Browse the repository at this point in the history
  • Loading branch information
himura committed Nov 17, 2021
1 parent 4e26ddc commit a10c8fb
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 73 deletions.
24 changes: 12 additions & 12 deletions Web/Twitter/Conduit/Api.hs
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,7 @@ search = searchTweets
-- APIRequest "GET" "https://api.twitter.com/1.1/direct_messages/events/list.json" []
-- >>> directMessages & #count ?~ 50
-- APIRequest "GET" "https://api.twitter.com/1.1/direct_messages/events/list.json" [("count","50")]
directMessages :: APIRequest DirectMessages (WithCursor T.Text EventsCursorKey DirectMessage)
directMessages :: APIRequest DirectMessages (WithCursor T.Text "events" DirectMessage)
directMessages = APIRequest "GET" (endpoint ++ "direct_messages/events/list.json") def

type DirectMessages =
Expand Down Expand Up @@ -653,7 +653,7 @@ type FriendshipsNoRetweetsIds = EmptyParams
-- APIRequest "GET" "https://api.twitter.com/1.1/friends/ids.json" [("screen_name","thimura")]
-- >>> friendsIds (ScreenNameParam "thimura") & #count ?~ 5000
-- APIRequest "GET" "https://api.twitter.com/1.1/friends/ids.json" [("count","5000"),("screen_name","thimura")]
friendsIds :: UserParam -> APIRequest FriendsIds (WithCursor Integer IdsCursorKey UserId)
friendsIds :: UserParam -> APIRequest FriendsIds (WithCursor Integer "ids" UserId)
friendsIds q = APIRequest "GET" (endpoint ++ "friends/ids.json") (mkUserParam q)

type FriendsIds =
Expand All @@ -679,7 +679,7 @@ type FriendsIds =
-- APIRequest "GET" "https://api.twitter.com/1.1/followers/ids.json" [("screen_name","thimura")]
-- >>> followersIds (ScreenNameParam "thimura") & #count ?~ 5000
-- APIRequest "GET" "https://api.twitter.com/1.1/followers/ids.json" [("count","5000"),("screen_name","thimura")]
followersIds :: UserParam -> APIRequest FollowersIds (WithCursor Integer IdsCursorKey UserId)
followersIds :: UserParam -> APIRequest FollowersIds (WithCursor Integer "ids" UserId)
followersIds q = APIRequest "GET" (endpoint ++ "followers/ids.json") (mkUserParam q)

type FollowersIds =
Expand All @@ -703,7 +703,7 @@ type FollowersIds =
--
-- >>> friendshipsIncoming
-- APIRequest "GET" "https://api.twitter.com/1.1/friendships/incoming.json" []
friendshipsIncoming :: APIRequest FriendshipsIncoming (WithCursor Integer IdsCursorKey UserId)
friendshipsIncoming :: APIRequest FriendshipsIncoming (WithCursor Integer "ids" UserId)
friendshipsIncoming = APIRequest "GET" (endpoint ++ "friendships/incoming.json") def

type FriendshipsIncoming =
Expand All @@ -726,7 +726,7 @@ type FriendshipsIncoming =
--
-- >>> friendshipsOutgoing
-- APIRequest "GET" "https://api.twitter.com/1.1/friendships/outgoing.json" []
friendshipsOutgoing :: APIRequest FriendshipsOutgoing (WithCursor Integer IdsCursorKey UserId)
friendshipsOutgoing :: APIRequest FriendshipsOutgoing (WithCursor Integer "ids" UserId)
friendshipsOutgoing = APIRequest "GET" (endpoint ++ "friendships/outgoing.json") def

type FriendshipsOutgoing =
Expand Down Expand Up @@ -787,7 +787,7 @@ type FriendshipsDestroy = EmptyParams
-- APIRequest "GET" "https://api.twitter.com/1.1/friends/list.json" [("screen_name","thimura")]
-- >>> friendsList (UserIdParam 69179963)
-- APIRequest "GET" "https://api.twitter.com/1.1/friends/list.json" [("user_id","69179963")]
friendsList :: UserParam -> APIRequest FriendsList (WithCursor Integer UsersCursorKey User)
friendsList :: UserParam -> APIRequest FriendsList (WithCursor Integer "users" User)
friendsList q = APIRequest "GET" (endpoint ++ "friends/list.json") (mkUserParam q)

type FriendsList =
Expand Down Expand Up @@ -815,7 +815,7 @@ type FriendsList =
-- APIRequest "GET" "https://api.twitter.com/1.1/followers/list.json" [("screen_name","thimura")]
-- >>> followersList (UserIdParam 69179963)
-- APIRequest "GET" "https://api.twitter.com/1.1/followers/list.json" [("user_id","69179963")]
followersList :: UserParam -> APIRequest FollowersList (WithCursor Integer UsersCursorKey User)
followersList :: UserParam -> APIRequest FollowersList (WithCursor Integer "users" User)
followersList q = APIRequest "GET" (endpoint ++ "followers/list.json") (mkUserParam q)

type FollowersList =
Expand Down Expand Up @@ -1027,7 +1027,7 @@ type ListsMembersDestroy = EmptyParams
-- APIRequest "GET" "https://api.twitter.com/1.1/lists/memberships.json" [("screen_name","thimura")]
-- >>> listsMemberships (Just (UserIdParam 69179963))
-- APIRequest "GET" "https://api.twitter.com/1.1/lists/memberships.json" [("user_id","69179963")]
listsMemberships :: Maybe UserParam -> APIRequest ListsMemberships (WithCursor Integer ListsCursorKey List)
listsMemberships :: Maybe UserParam -> APIRequest ListsMemberships (WithCursor Integer "lists" List)
listsMemberships q = APIRequest "GET" (endpoint ++ "lists/memberships.json") $ maybe [] mkUserParam q

type ListsMemberships =
Expand All @@ -1047,7 +1047,7 @@ type ListsMemberships =
-- APIRequest "GET" "https://api.twitter.com/1.1/lists/subscribers.json" [("slug","haskell"),("owner_screen_name","thimura")]
-- >>> listsSubscribers (ListIdParam 20849097)
-- APIRequest "GET" "https://api.twitter.com/1.1/lists/subscribers.json" [("list_id","20849097")]
listsSubscribers :: ListParam -> APIRequest ListsSubscribers (WithCursor Integer UsersCursorKey User)
listsSubscribers :: ListParam -> APIRequest ListsSubscribers (WithCursor Integer "users" User)
listsSubscribers q = APIRequest "GET" (endpoint ++ "lists/subscribers.json") (mkListParam q)

type ListsSubscribers =
Expand All @@ -1070,7 +1070,7 @@ type ListsSubscribers =
-- APIRequest "GET" "https://api.twitter.com/1.1/lists/subscriptions.json" [("screen_name","thimura")]
-- >>> listsSubscriptions (Just (UserIdParam 69179963))
-- APIRequest "GET" "https://api.twitter.com/1.1/lists/subscriptions.json" [("user_id","69179963")]
listsSubscriptions :: Maybe UserParam -> APIRequest ListsSubscriptions (WithCursor Integer ListsCursorKey List)
listsSubscriptions :: Maybe UserParam -> APIRequest ListsSubscriptions (WithCursor Integer "lists" List)
listsSubscriptions q = APIRequest "GET" (endpoint ++ "lists/subscriptions.json") $ maybe [] mkUserParam q

type ListsSubscriptions =
Expand All @@ -1092,7 +1092,7 @@ type ListsSubscriptions =
-- APIRequest "GET" "https://api.twitter.com/1.1/lists/ownerships.json" [("screen_name","thimura")]
-- >>> listsOwnerships (Just (UserIdParam 69179963))
-- APIRequest "GET" "https://api.twitter.com/1.1/lists/ownerships.json" [("user_id","69179963")]
listsOwnerships :: Maybe UserParam -> APIRequest ListsOwnerships (WithCursor Integer ListsCursorKey List)
listsOwnerships :: Maybe UserParam -> APIRequest ListsOwnerships (WithCursor Integer "lists" List)
listsOwnerships q = APIRequest "GET" (endpoint ++ "lists/ownerships.json") $ maybe [] mkUserParam q

type ListsOwnerships =
Expand Down Expand Up @@ -1146,7 +1146,7 @@ type ListsMembersDestroyAll = EmptyParams
-- APIRequest "GET" "https://api.twitter.com/1.1/lists/members.json" [("slug","haskell"),("owner_screen_name","thimura")]
-- >>> listsMembers (ListIdParam 20849097)
-- APIRequest "GET" "https://api.twitter.com/1.1/lists/members.json" [("list_id","20849097")]
listsMembers :: ListParam -> APIRequest ListsMembers (WithCursor Integer UsersCursorKey User)
listsMembers :: ListParam -> APIRequest ListsMembers (WithCursor Integer "users" User)
listsMembers q = APIRequest "GET" (endpoint ++ "lists/members.json") (mkListParam q)

type ListsMembers =
Expand Down
5 changes: 3 additions & 2 deletions Web/Twitter/Conduit/Base.hs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import qualified Data.Conduit.Attoparsec as CA
import qualified Data.Conduit.List as CL
import qualified Data.Map as M
import qualified Data.Text.Encoding as T
import GHC.TypeLits (KnownSymbol)
import Network.HTTP.Client.MultipartFormData
import qualified Network.HTTP.Conduit as HTTP
import qualified Network.HTTP.Types as HT
Expand Down Expand Up @@ -293,7 +294,7 @@ sourceWithMaxId' info mgr = loop
sourceWithCursor ::
( MonadIO m
, FromJSON responseType
, CursorKey ck
, KnownSymbol ck
, HasParam "cursor" Integer supports
) =>
-- | Twitter Setting
Expand All @@ -317,7 +318,7 @@ sourceWithCursor info mgr req = loop (Just (-1))
-- This function cooperate with instances of 'HasCursorParam'.
sourceWithCursor' ::
( MonadIO m
, CursorKey ck
, KnownSymbol ck
, HasParam "cursor" Integer supports
) =>
-- | Twitter Setting
Expand Down
80 changes: 25 additions & 55 deletions Web/Twitter/Conduit/Cursor.hs
Original file line number Diff line number Diff line change
@@ -1,95 +1,65 @@
{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Web.Twitter.Conduit.Cursor (
CursorKey (..),
IdsCursorKey,
UsersCursorKey,
ListsCursorKey,
EventsCursorKey,
WithCursor (..),
) where

-- import Control.DeepSeq (NFData)
import Data.Aeson
import Data.Text (Text)
import Data.Proxy (Proxy (..))
import Data.String
import GHC.Generics
import Web.Twitter.Types (checkError)
import GHC.TypeLits (KnownSymbol, Symbol, symbolVal)

-- $setup
-- >>> import Data.Text
-- >>> type UserId = Integer

{- ORMOLU_DISABLE -}
class CursorKey a where
#if MIN_VERSION_aeson(2, 0, 0)
cursorKey :: a -> Key
#else
cursorKey :: a -> Text
#endif
{- ORMOLU_ENABLE -}

-- | Phantom type to specify the key which point out the content in the response.
data IdsCursorKey

instance CursorKey IdsCursorKey where
cursorKey = const "ids"

-- | Phantom type to specify the key which point out the content in the response.
data UsersCursorKey

instance CursorKey UsersCursorKey where
cursorKey = const "users"

-- | Phantom type to specify the key which point out the content in the response.
data ListsCursorKey

instance CursorKey ListsCursorKey where
cursorKey = const "lists"

data EventsCursorKey
instance CursorKey EventsCursorKey where
cursorKey = const "events"

-- | A wrapper for API responses which have "next_cursor" field.
--
-- The first type parameter of 'WithCursor' specifies the field name of contents.
--
-- >>> let Just res = decode "{\"previous_cursor\": 0, \"next_cursor\": 1234567890, \"ids\": [1111111111]}" :: Maybe (WithCursor Integer IdsCursorKey UserId)
-- >>> let Just res = decode "{\"previous_cursor\": 0, \"next_cursor\": 1234567890, \"ids\": [1111111111]}" :: Maybe (WithCursor Integer "ids" UserId)
-- >>> nextCursor res
-- Just 1234567890
-- >>> contents res
-- [1111111111]
--
-- >>> let Just res = decode "{\"previous_cursor\": 0, \"next_cursor\": 0, \"users\": [1000]}" :: Maybe (WithCursor Integer UsersCursorKey UserId)
-- >>> let Just res = decode "{\"previous_cursor\": 0, \"next_cursor\": 0, \"users\": [1000]}" :: Maybe (WithCursor Integer "users" UserId)
-- >>> nextCursor res
-- Just 0
-- >>> contents res
-- [1000]
--
-- >>> let Just res = decode "{\"next_cursor\": \"hogehoge\", \"events\": [1000]}" :: Maybe (WithCursor Text EventsCursorKey UserId)
-- >>> let Just res = decode "{\"next_cursor\": \"hogehoge\", \"events\": [1000]}" :: Maybe (WithCursor Text "events" UserId)
-- >>> nextCursor res
-- Just "hogehoge"
-- >>> contents res
-- [1000]
data WithCursor cursorType cursorKey wrapped = WithCursor
data WithCursor cursorType (cursorKey :: Symbol) wrapped = WithCursor
{ previousCursor :: Maybe cursorType
, nextCursor :: Maybe cursorType
, contents :: [wrapped]
}
deriving (Show, Eq, Generic, Functor, Foldable, Traversable)
deriving (Show, Eq, Generic, Generic1, Functor, Foldable, Traversable)

instance (KnownSymbol cursorKey, FromJSON cursorType) => FromJSON1 (WithCursor cursorType cursorKey) where
liftParseJSON _ lp =
withObject ("WithCursor \"" ++ cursorKeyStr ++ "\"") $ \obj ->
WithCursor <$> obj .:? "previous_cursor"
<*> obj .:? "next_cursor"
<*> (obj .: fromString cursorKeyStr >>= lp)
where
cursorKeyStr = symbolVal (Proxy :: Proxy cursorKey)

instance (KnownSymbol cursorKey, FromJSON cursorType, FromJSON wrapped) => FromJSON (WithCursor cursorType cursorKey wrapped) where
parseJSON = parseJSON1

instance
(FromJSON wrapped, FromJSON ct, CursorKey c) =>
FromJSON (WithCursor ct c wrapped)
where
parseJSON (Object o) =
checkError o
>> WithCursor <$> o .:? "previous_cursor"
<*> o .:? "next_cursor"
<*> o .: cursorKey (undefined :: c)
parseJSON _ = mempty
-- instance NFData a => NFData (WithCursor cursorType cursorKey wrapped)
4 changes: 0 additions & 4 deletions Web/Twitter/Conduit/Lens.hs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,6 @@ module Web.Twitter.Conduit.Lens (

-- * Re-exports
TT.TwitterError (..),
TT.CursorKey (..),
TT.IdsCursorKey,
TT.UsersCursorKey,
TT.ListsCursorKey,
) where

import Control.Lens
Expand Down

0 comments on commit a10c8fb

Please sign in to comment.