@@ -1,3 +1,6 @@
1+{-# LANGUAGE TypeFamilies #-}
2+{-# LANGUAGE TypeOperators #-}
3+{-# LANGUAGE UndecidableInstances #-}
14module Servant.API (
25
36 -- * Combinators
@@ -43,11 +46,16 @@ module Servant.API (
4346 module Servant.Common.Text,
4447 -- | Classes and instances for types that can be converted to and from @Text@
4548
49+ -- * Canonicalizing (flattening) API types
50+ Canonicalize,
51+ canonicalize,
52+
4653 -- * Utilities
4754 module Servant.Utils.Links,
4855 -- | Type-safe internal URIs
4956 ) where
5057
58+import Data.Proxy (Proxy(..))
5159import Servant.Common.Text (FromText(..), ToText(..))
5260import Servant.API.Alternative ((:<|>) (..))
5361import Servant.API.Capture (Capture)
@@ -69,3 +77,34 @@ import Servant.API.ReqBody (ReqBody)
6977import Servant.API.Sub ((:>))
7078import Servant.Utils.Links (HasLink (..), IsElem, IsElem',
7179 URI (..), safeLink)
80+
81+-- | Turn an API type into its canonical form.
82+--
83+-- The canonical form of an API type is basically the all-flattened form
84+-- of the original type. More formally, it takes a type as input and hands you
85+-- back an /equivalent/ type formed of toplevel `:<|>`-separated chains of `:>`s,
86+-- i.e with all `:>`s distributed inside the `:<|>`s.
87+--
88+-- It basically turns:
89+--
90+-- > "hello" :> (Get Hello :<|> ReqBody Hello :> Put Hello)
91+--
92+-- into
93+--
94+-- > ("hello" :> Get Hello) :<|> ("hello" :> ReqBody Hello :> Put Hello)
95+--
96+-- i.e distributing all ':>'-separated bits into the subsequent ':<|>'s.
97+type family Canonicalize api :: * where
98+ -- requires UndecidableInstances
99+ Canonicalize (a :> (b :<|> c)) = a :> Canonicalize b :<|> a :> Canonicalize c
100+ Canonicalize ((a :<|> b) :> c) = a :> Canonicalize c :<|> b :> Canonicalize c
101+ Canonicalize (a :> b) = Redex b (Canonicalize b) a
102+ Canonicalize (a :<|> b) = Canonicalize a :<|> Canonicalize b
103+ Canonicalize a = a
104+
105+type family Redex a b c :: * where
106+ Redex a a first = Canonicalize first :> a
107+ Redex a b first = Canonicalize (first :> b)
108+
109+canonicalize :: Proxy layout -> Proxy (Canonicalize layout)
110+canonicalize Proxy = Proxy