servant-cli-0.1.0.0: src/Servant/CLI.hs
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeInType #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
-- |
-- Module : Servant.CLI
-- Copyright : (c) Justin Le 2019
-- License : BSD3
--
-- Maintainer : justin@jle.im
-- Stability : experimental
-- Portability : non-portable
--
-- Parse command line arguments into a servant client, from a servant API.
--
-- Mainly used through 'parseClient' and 'parseHandleClient'.
-- 'parseClient' returns a servant client action that returns nested
-- 'Either's for every endpoint, but 'parseHandleClient' allows you to
-- conveniently specify how you want to sort each endpoint entry into
-- a single result.
--
-- See <https://hackage.haskell.org/package/servant-cli README> for
-- a tutorial.
module Servant.CLI (
-- * Parse Client
parseClient, parseHandleClient
-- ** With context
, parseClientWithContext, parseHandleClientWithContext
-- * Typeclasses
, HasCLI (CLIResult, CLIHandler, cliHandler)
-- * Context
, ContextFor(..)
, NamedContext(..)
, descendIntoNamedContext
-- * Lower-level
, cliPStruct, cliPStructWithContext
, structParser
-- ** With context
, cliHandlePStruct, cliHandlePStructWithContext
-- * Re-export
, ParseBody(..), defaultParseBody
, ToCapture(..), DocCapture(..)
, ToParam(..), DocQueryParam(..), ParamKind(..)
, ToAuthInfo(..), DocAuthentication(..)
) where
import Data.Proxy
import Data.Vinyl
import Options.Applicative
import Servant.CLI.HasCLI
import Servant.CLI.Internal.PStruct
import Servant.CLI.ParseBody
import Servant.Client.Core
import Servant.Docs.Internal
-- | A version of 'cliPStruct' that can be used if the API requires
-- any external context to generate runtime data.
cliPStructWithContext
:: HasCLI m api context
=> Proxy m -- ^ Client monad
-> Proxy api -- ^ API
-> Rec (ContextFor m) context -- ^ Extra context
-> PStruct (m (CLIResult m api))
cliPStructWithContext pm pa = fmap ($ defaultRequest)
. cliPStructWithContext_ pm pa
-- | A version of 'cliHandlePStruct' that can be used if the API requires
-- any external context to generate runtime data.
cliHandlePStructWithContext
:: forall m api context r. (HasCLI m api context, Functor m)
=> Proxy m -- ^ Client monad
-> Proxy api -- ^ API
-> Rec (ContextFor m) context -- ^ Extra context
-> CLIHandler m api r -- ^ Handler
-> PStruct (m r)
cliHandlePStructWithContext pm pa p h =
fmap (cliHandler pm pa (Proxy @context) h)
<$> cliPStructWithContext pm pa p
-- | A version of 'parseClient' that can be used if the API requires
-- any external context to generate runtime data.
parseClientWithContext
:: HasCLI m api context
=> Proxy api -- ^ API
-> Proxy m -- ^ Client monad
-> Rec (ContextFor m) context -- ^ Extra context
-> InfoMod (m (CLIResult m api)) -- ^ Options for top-level display
-> IO (m (CLIResult m api))
parseClientWithContext pa pm p im = execParser . flip structParser im
$ cliPStructWithContext pm pa p
-- | A version of 'parseHandleClient' that can be used if the API requires
-- any external context to generate runtime data.
parseHandleClientWithContext
:: forall m api context r. (HasCLI m api context, Functor m)
=> Proxy api -- ^ API
-> Proxy m -- ^ Client monad
-> Rec (ContextFor m) context -- ^ Extra context
-> InfoMod (m (CLIResult m api)) -- ^ Options for top-level display
-> CLIHandler m api r -- ^ Handler
-> IO (m r)
parseHandleClientWithContext pa pm p im h =
fmap (cliHandler pm pa (Proxy @context) h)
<$> parseClientWithContext pa pm p im
-- | Create a structure for a command line parser.
--
-- This can be useful if you are combining functionality with existing
-- /optparse-applicative/ parsers. You can convert a 'PStruct' to
-- a 'Parser' using 'structParser'.
cliPStruct
:: HasCLI m api '[]
=> Proxy m -- ^ Client monad
-> Proxy api -- ^ API
-> PStruct (m (CLIResult m api))
cliPStruct pm pa = cliPStructWithContext pm pa RNil
-- | Create a structure for a command line parser, producing results
-- according to a 'CLIHandler'. See 'parseHandleClient' for more
-- information.
--
-- This can be useful if you are combining functionality with existing
-- /optparse-applicative/ parsers. You can convert a 'PStruct' to
-- a 'Parser' using 'structParser'.
cliHandlePStruct
:: (HasCLI m api '[], Functor m)
=> Proxy m -- ^ Client monad
-> Proxy api -- ^ API
-> CLIHandler m api r -- ^ Handler
-> PStruct (m r)
cliHandlePStruct pm pa = cliHandlePStructWithContext pm pa RNil
-- | Parse a servant client; the result can be run. The choice of @m@
-- gives the backend you are using; for example, the default GHC
-- /servant-client/ backend is 'Servant.Client.ClientM'.
--
-- Returns the request response, which is usually a layer of 'Either' for
-- every endpoint branch. You can find the response type directly by using
-- typed holes or asking ghci with @:t@ or @:kind! forall m. CLIResult
-- m MyAPI@. Because it might be tedious handling nested 'Either's, see
-- 'parseHandleClient' for a way to handle each potential branch in
-- a convenient way.
--
-- Takes options on how the top-level prompt is displayed when given
-- @"--help"@; it can be useful for adding a header or program description.
-- Otherwise, just use 'mempty'.
parseClient
:: HasCLI m api '[]
=> Proxy api -- ^ API
-> Proxy m -- ^ Client monad
-> InfoMod (m (CLIResult m api)) -- ^ Options for top-level display
-> IO (m (CLIResult m api))
parseClient pa pm = parseClientWithContext pa pm RNil
-- | Parse a server client, like 'parseClient'. However, instead of that
-- client action returning the request response, instead use a 'CLIHandler'
-- to handle every potential request response. It essentially lets you
-- specify how to sort each potential endpoint's response into a single
-- output value.
--
-- The handler is usually a 'Servant.API.:<|>' for every endpoint branch.
-- You can find it by using typed holes or asking ghci with @:t@ or @:kind!
-- forall m r. CLIHandler m MyAPI r@.
--
-- Takes options on how the top-level prompt is displayed when given
-- @"--help"@; it can be useful for adding a header or program description.
-- Otherwise, just use 'mempty'.
parseHandleClient
:: (HasCLI m api '[], Functor m)
=> Proxy api -- ^ API
-> Proxy m -- ^ Client monad
-> InfoMod (m (CLIResult m api)) -- ^ Options for top-level display
-> CLIHandler m api r -- ^ Handler
-> IO (m r)
parseHandleClient pa pm = parseHandleClientWithContext pa pm RNil