@@ -0,0 +1,83 @@
1+{-# LANGUAGE PolyKinds #-}
2+{-# LANGUAGE DataKinds #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE TypeFamilies #-}
5+{-# LANGUAGE TypeOperators #-}
6+{-# LANGUAGE FunctionalDependencies #-}
7+{-# LANGUAGE ScopedTypeVariables #-}
8+{-# LANGUAGE UndecidableInstances #-}
9+module Servant.API.Elem where
10+
11+import Data.Proxy
12+import GHC.TypeLits
13+
14+import Servant.API.Capture
15+import Servant.API.ReqBody
16+import Servant.API.QueryParam
17+import Servant.API.Get
18+import Servant.API.Post
19+import Servant.API.Put
20+import Servant.API.Delete
21+import Servant.API.Sub
22+import Servant.API.Alternative
23+
24+
25+type family Or a b where
26+ Or 'False 'False = 'False
27+ Or 'True b = 'True
28+ Or a 'True = 'True
29+
30+type family And a b where
31+ And 'True 'True = 'True
32+ And a 'False = 'False
33+ And 'False b = 'False
34+
35+type family IsElem a s where
36+ IsElem e (sa :<|> sb) = Or (IsElem e sa) (IsElem e sb)
37+ IsElem (e :> sa) (e :> sb) = IsElem sa sb
38+ IsElem (e :> sa) (Capture x y :> sb) = IsElem sa sb
39+ IsElem sa (ReqBody x :> sb) = IsElem sa sb
40+ IsElem sa (QueryParam x y :> sb) = IsElem sa sb
41+ IsElem e e = 'True
42+ IsElem e a = 'False
43+
44+type family IsLink'' l where
45+ IsLink'' (e :> Get x) = IsLink' e
46+ IsLink'' (e :> Post x) = IsLink' e
47+ IsLink'' (e :> Put x) = IsLink' e
48+ IsLink'' (e :> Delete) = IsLink' e
49+ IsLink'' a = 'False
50+
51+type family IsLink' e where
52+ IsLink' (f :: Symbol) = 'True
53+
54+type family IsLink e where
55+ IsLink (a :> b) = Or (And (IsLink' a) (IsLink'' b))
56+ (IsLink'' (a :> b))
57+
58+
59+-- | The 'ValidLinkIn f s' constraint holds when 's' is an API that
60+-- contains 'f', and 'f' is a link.
61+class ValidLinkIn f s where
62+ mkLink :: f -> s -> Link -- ^ This function will only typecheck if `f`
63+ -- is an URI within `s`
64+
65+instance ( IsElem f s ~ 'True
66+ , IsLink f ~ 'True
67+ , VLinkHelper f) => ValidLinkIn f s where
68+ mkLink _ _ = Link (vlh (Proxy :: Proxy f))
69+
70+data Link = Link String deriving Show
71+
72+class VLinkHelper f where
73+ vlh :: forall proxy. proxy f -> String
74+
75+instance (KnownSymbol s, VLinkHelper e) => VLinkHelper (s :> e) where
76+ vlh _ = "/" ++ symbolVal (Proxy :: Proxy s) ++ vlh (Proxy :: Proxy e)
77+
78+instance VLinkHelper (Get x) where
79+ vlh _ = ""
80+
81+instance VLinkHelper (Post x) where
82+ vlh _ = ""
83+