diff --git a/src/Servant/Utils/ApiQuasiQuoting.hs b/src/Servant/Utils/ApiQuasiQuoting.hs index e768c73e..9337a42d 100644 --- a/src/Servant/Utils/ApiQuasiQuoting.hs +++ b/src/Servant/Utils/ApiQuasiQuoting.hs @@ -5,6 +5,27 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} {-# OPTIONS_GHC -fno-warn-unused-do-bind #-} +-- | QuasiQuoting utilities for API types. +-- +-- 'sitemap' allows you to write your type in a very natural way: +-- +-- @ +-- [sitemap| +-- PUT hello String -> () +-- POST hello/p:Int String -> () +-- GET hello/?name:String Int +-- |] +-- @ +-- +-- Will generate: +-- +-- @ +-- "hello" :> ReqBody String :> Put () +-- :<|> "hello" :> Capture "p" Int :> ReqBody String :> Post () +-- :<|> "hello" :> QueryParam "name" String :> Get Int +-- @ +-- +-- Note the '/' before a 'QueryParam'! module Servant.Utils.ApiQuasiQuoting where import Control.Monad (void) @@ -23,6 +44,11 @@ import Servant.API.ReqBody import Servant.API.Sub import Servant.API.Alternative +-- | Finally-tagless encoding for our DSL. +-- Keeping 'repr'' and 'repr' distinct when writing functions with an +-- @ExpSYM@ context ensures certain invariants (for instance, that there is +-- only one of 'get', 'post', 'put', and 'delete' in a value), but +-- sometimes requires a little more work. class ExpSYM repr' repr | repr -> repr', repr' -> repr where lit :: String -> repr' -> repr capture :: String -> String -> repr -> repr @@ -148,6 +174,23 @@ parseAll = do where union :: Type -> Type -> Type union a = AppT (AppT (ConT ''(:<|>)) a) +-- | The sitemap QuasiQuoter. +-- +-- * @.../:/...@ becomes a capture +-- +-- * @.../?:@ becomes a query parameter +-- +-- * @ ... @ becomes a method returning @@ +-- +-- * @ ... -> @ becomes a method with request +-- body of @@ and returning @@ +-- +-- Comments are allowed, and have the standard Haskell format +-- +-- * @--@ for inline +-- +-- * @{- ... -}@ for block +-- sitemap :: QuasiQuoter sitemap = QuasiQuoter { quoteExp = undefined , quotePat = undefined diff --git a/src/Servant/Utils/Links.hs b/src/Servant/Utils/Links.hs index 97f65f21..f25533f5 100644 --- a/src/Servant/Utils/Links.hs +++ b/src/Servant/Utils/Links.hs @@ -6,6 +6,32 @@ {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE UndecidableInstances #-} +-- | Type safe internal links. +-- +-- Provides the function 'mkLink': +-- @ +-- type API = Proxy ("hello" :> Get Int +-- :<|> "bye" :> QueryParam "name" String :> Post Bool) +-- +-- api :: API +-- api = proxy +-- +-- link1 :: Proxy ("hello" :> Get Int) +-- link1 = proxy +-- +-- link2 :: Proxy ("hello" :> Delete) +-- link2 = proxy +-- +-- mkLink link1 API -- typechecks, returns 'Link "/hello"' +-- +-- mkLink link2 API -- doesn't typecheck +-- @ +-- +-- That is, 'mkLink' takes two arguments, a link proxy and a sitemap, and +-- returns a 'Link', but only typechecks if the link proxy is a valid link, +-- and part of the sitemap. +-- +-- __N.B.:__ 'mkLink' assumes a capture matches any string (without slashes). module Servant.Utils.Links where import Data.Proxy