{-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE StandaloneDeriving #-} ----------------------------------------------------------------------------- -- | -- License : BSD-3-Clause -- Maintainer : Oleg Grenrus -- module GitHub.Data.Request ( -- * Request Request (..), SimpleRequest (..), -- * Smart constructors query, pagedQuery, command, -- * Auxiliary types RW(..), StatusMap, statusOnlyOk, CommandMethod(..), toMethod, FetchCount(..), Paths, IsPathPart(..), QueryString, Count, ) where import GitHub.Data.Definitions (Count, QueryString) import GitHub.Data.Id (Id, untagId) import GitHub.Data.Name (Name, untagName) import GitHub.Internal.Prelude import qualified Data.ByteString.Lazy as LBS import qualified Data.Text as T import qualified Network.HTTP.Types as Types import qualified Network.HTTP.Types.Method as Method ------------------------------------------------------------------------------ -- Auxillary types ------------------------------------------------------------------------------ type Paths = [Text] class IsPathPart a where toPathPart :: a -> Text instance IsPathPart (Name a) where toPathPart = untagName instance IsPathPart (Id a) where toPathPart = T.pack . show . untagId -- | Http method of requests with body. data CommandMethod a where Post :: CommandMethod a Patch :: CommandMethod a Put :: CommandMethod a Put' :: CommandMethod () Delete :: CommandMethod () deriving (Typeable) deriving instance Eq (CommandMethod a) instance Show (CommandMethod a) where showsPrec _ Post = showString "Post" showsPrec _ Patch = showString "Patch" showsPrec _ Put = showString "Put" showsPrec _ Put' = showString "Put'" showsPrec _ Delete = showString "Delete" instance Hashable (CommandMethod a) where hashWithSalt salt Post = hashWithSalt salt (0 :: Int) hashWithSalt salt Patch = hashWithSalt salt (1 :: Int) hashWithSalt salt Put = hashWithSalt salt (2 :: Int) hashWithSalt salt Put' = hashWithSalt salt (3 :: Int) hashWithSalt salt Delete = hashWithSalt salt (4 :: Int) toMethod :: CommandMethod a -> Method.Method toMethod Post = Method.methodPost toMethod Patch = Method.methodPatch toMethod Put = Method.methodPut toMethod Put' = Method.methodPut toMethod Delete = Method.methodDelete -- | 'PagedQuery' returns just some results, using this data we can specify how -- many pages we want to fetch. data FetchCount = FetchAtLeast !Word | FetchAll deriving (Eq, Ord, Read, Show, Generic, Typeable) -- | This instance is there mostly for 'fromInteger'. instance Num FetchCount where fromInteger = FetchAtLeast . fromInteger FetchAtLeast a + FetchAtLeast b = FetchAtLeast (a * b) _ + _ = FetchAll FetchAtLeast a * FetchAtLeast b = FetchAtLeast (a * b) _ * _ = FetchAll abs = error "abs @FetchCount: not implemented" signum = error "signum @FetchCount: not implemented" negate = error "negate @FetchCount: not implemented" instance Hashable FetchCount instance Binary FetchCount instance NFData FetchCount where rnf = genericRnf ------------------------------------------------------------------------------ -- Github request ------------------------------------------------------------------------------ -- | Type used as with @DataKinds@ to tag whether requests need authentication -- or aren't read-only. data RW = RO -- ^ /Read-only/, doesn't necessarily requires authentication | RA -- ^ /Read autenticated/ | RW -- ^ /Read-write/, requires authentication deriving (Eq, Ord, Read, Show, Enum, Bounded, Typeable, Data, Generic) {- data SRO (rw :: RW) where ROO :: SRO 'RO ROA :: SRO 'RA -- | This class is used to describe read-only (but pontentially class IReadOnly (rw :: RW) where iro :: SRO rw instance IReadOnly 'RO where iro = ROO instance IReadOnly 'RA where iro = ROA -} -- | Github request data type. -- -- * @k@ describes whether authentication is required. It's required for non-@GET@ requests. -- * @a@ is the result type -- -- /Note:/ 'Request' is not 'Functor' on purpose. data Request (k :: RW) a where SimpleQuery :: FromJSON a => SimpleRequest k a -> Request k a StatusQuery :: StatusMap a -> SimpleRequest k () -> Request k a HeaderQuery :: FromJSON a => Types.RequestHeaders -> SimpleRequest k a -> Request k a deriving (Typeable) data SimpleRequest (k :: RW) a where Query :: Paths -> QueryString -> SimpleRequest k a PagedQuery :: Paths -> QueryString -> FetchCount -> SimpleRequest k (Vector a) Command :: CommandMethod a -> Paths -> LBS.ByteString -> SimpleRequest 'RW a deriving (Typeable) ------------------------------------------------------------------------------- -- Status Map ------------------------------------------------------------------------------- -- TODO: Change to 'Map' ? type StatusMap a = [(Int, a)] statusOnlyOk :: StatusMap Bool statusOnlyOk = [ (202, True) , (404, False) ] ------------------------------------------------------------------------------- -- Smart constructors ------------------------------------------------------------------------------- query :: FromJSON a => Paths -> QueryString -> Request k a query ps qs = SimpleQuery (Query ps qs) pagedQuery :: FromJSON a => Paths -> QueryString -> FetchCount -> Request k (Vector a) pagedQuery ps qs fc = SimpleQuery (PagedQuery ps qs fc) command :: FromJSON a => CommandMethod a -> Paths -> LBS.ByteString -> Request 'RW a command m ps body = SimpleQuery (Command m ps body) ------------------------------------------------------------------------------- -- Instances ------------------------------------------------------------------------------- deriving instance Eq a => Eq (Request k a) deriving instance Eq a => Eq (SimpleRequest k a) instance Show (SimpleRequest k a) where showsPrec d r = showParen (d > appPrec) $ case r of Query ps qs -> showString "Query " . showsPrec (appPrec + 1) ps . showString " " . showsPrec (appPrec + 1) qs PagedQuery ps qs l -> showString "PagedQuery " . showsPrec (appPrec + 1) ps . showString " " . showsPrec (appPrec + 1) qs . showString " " . showsPrec (appPrec + 1) l Command m ps body -> showString "Command " . showsPrec (appPrec + 1) m . showString " " . showsPrec (appPrec + 1) ps . showString " " . showsPrec (appPrec + 1) body where appPrec = 10 :: Int instance Show (Request k a) where showsPrec d r = showParen (d > appPrec) $ case r of SimpleQuery req -> showString "SimpleQuery " . showsPrec (appPrec + 1) req StatusQuery m req -> showString "Status " . showsPrec (appPrec + 1) (map fst m) -- !!! printing only keys . showString " " . showsPrec (appPrec + 1) req HeaderQuery m req -> showString "Header " . showsPrec (appPrec + 1) m . showString " " . showsPrec (appPrec + 1) req where appPrec = 10 :: Int instance Hashable (SimpleRequest k a) where hashWithSalt salt (Query ps qs) = salt `hashWithSalt` (0 :: Int) `hashWithSalt` ps `hashWithSalt` qs hashWithSalt salt (PagedQuery ps qs l) = salt `hashWithSalt` (1 :: Int) `hashWithSalt` ps `hashWithSalt` qs `hashWithSalt` l hashWithSalt salt (Command m ps body) = salt `hashWithSalt` (2 :: Int) `hashWithSalt` m `hashWithSalt` ps `hashWithSalt` body instance Hashable (Request k a) where hashWithSalt salt (SimpleQuery req) = salt `hashWithSalt` (0 :: Int) `hashWithSalt` req hashWithSalt salt (StatusQuery sm req) = salt `hashWithSalt` (1 :: Int) `hashWithSalt` map fst sm `hashWithSalt` req hashWithSalt salt (HeaderQuery h req) = salt `hashWithSalt` (2 :: Int) `hashWithSalt` h `hashWithSalt` req