servant/src/Servant/Utils/Links.hs

84 lines
2.4 KiB
Haskell
Raw Normal View History

2014-10-27 08:52:18 +01:00
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE UndecidableInstances #-}
module Servant.Utils.Links where
2014-10-27 08:52:18 +01:00
import Data.Proxy
import GHC.TypeLits
import Servant.API.Capture
import Servant.API.ReqBody
import Servant.API.QueryParam
import Servant.API.Get
import Servant.API.Post
import Servant.API.Put
import Servant.API.Delete
import Servant.API.Sub
import Servant.API.Alternative
type family Or a b where
Or 'False 'False = 'False
Or 'True b = 'True
Or a 'True = 'True
type family And a b where
And 'True 'True = 'True
And a 'False = 'False
And 'False b = 'False
type family IsElem a s where
IsElem e (sa :<|> sb) = Or (IsElem e sa) (IsElem e sb)
IsElem (e :> sa) (e :> sb) = IsElem sa sb
IsElem (e :> sa) (Capture x y :> sb) = IsElem sa sb
IsElem sa (ReqBody x :> sb) = IsElem sa sb
IsElem sa (QueryParam x y :> sb) = IsElem sa sb
IsElem e e = 'True
IsElem e a = 'False
type family IsLink'' l where
IsLink'' (e :> Get x) = IsLink' e
IsLink'' (e :> Post x) = IsLink' e
IsLink'' (e :> Put x) = IsLink' e
IsLink'' (e :> Delete) = IsLink' e
IsLink'' a = 'False
type family IsLink' e where
IsLink' (f :: Symbol) = 'True
type family IsLink e where
IsLink (a :> b) = Or (And (IsLink' a) (IsLink'' b))
(IsLink'' (a :> b))
-- | The 'ValidLinkIn f s' constraint holds when 's' is an API that
-- contains 'f', and 'f' is a link.
class ValidLinkIn f s where
mkLink :: f -> s -> Link -- ^ This function will only typecheck if `f`
-- is an URI within `s`
instance ( IsElem f s ~ 'True
, IsLink f ~ 'True
, VLinkHelper f) => ValidLinkIn f s where
mkLink _ _ = Link (vlh (Proxy :: Proxy f))
data Link = Link String deriving Show
class VLinkHelper f where
vlh :: forall proxy. proxy f -> String
instance (KnownSymbol s, VLinkHelper e) => VLinkHelper (s :> e) where
vlh _ = "/" ++ symbolVal (Proxy :: Proxy s) ++ vlh (Proxy :: Proxy e)
instance VLinkHelper (Get x) where
vlh _ = ""
instance VLinkHelper (Post x) where
vlh _ = ""