{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# 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) =>
  -- | Client monad
  Proxy m ->
  -- | API
  Proxy api ->
  -- | Extra context
  Rec (ContextFor m) context ->
  PStruct (m (CLIResult m api))
cliPStructWithContext :: forall (m :: * -> *) api (context :: [*]).
HasCLI m api context =>
Proxy m
-> Proxy api
-> Rec (ContextFor m) context
-> PStruct (m (CLIResult m api))
cliPStructWithContext Proxy m
pm Proxy api
pa =
  ((Request -> m (CLIResult m api)) -> m (CLIResult m api))
-> PStruct (Request -> m (CLIResult m api))
-> PStruct (m (CLIResult m api))
forall a b. (a -> b) -> PStruct a -> PStruct b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((Request -> m (CLIResult m api)) -> Request -> m (CLIResult m api)
forall a b. (a -> b) -> a -> b
$ Request
defaultRequest)
    (PStruct (Request -> m (CLIResult m api))
 -> PStruct (m (CLIResult m api)))
-> (Rec (ContextFor m) context
    -> PStruct (Request -> m (CLIResult m api)))
-> Rec (ContextFor m) context
-> PStruct (m (CLIResult m api))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Proxy m
-> Proxy api
-> Rec (ContextFor m) context
-> PStruct (Request -> m (CLIResult m api))
forall (m :: * -> *) api (ctx :: [*]).
HasCLI m api ctx =>
Proxy m
-> Proxy api
-> Rec (ContextFor m) ctx
-> PStruct (Request -> m (CLIResult m api))
cliPStructWithContext_ Proxy m
pm Proxy api
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) =>
  -- | Client monad
  Proxy m ->
  -- | API
  Proxy api ->
  -- | Extra context
  Rec (ContextFor m) context ->
  -- | Handler
  CLIHandler m api r ->
  PStruct (m r)
cliHandlePStructWithContext :: forall (m :: * -> *) api (context :: [*]) r.
(HasCLI m api context, Functor m) =>
Proxy m
-> Proxy api
-> Rec (ContextFor m) context
-> CLIHandler m api r
-> PStruct (m r)
cliHandlePStructWithContext Proxy m
pm Proxy api
pa Rec (ContextFor m) context
p CLIHandler m api r
h =
  (CLIResult m api -> r) -> m (CLIResult m api) -> m r
forall a b. (a -> b) -> m a -> m b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Proxy m
-> Proxy api
-> Proxy context
-> CLIHandler m api r
-> CLIResult m api
-> r
forall r.
Proxy m
-> Proxy api
-> Proxy context
-> CLIHandler m api r
-> CLIResult m api
-> r
forall (m :: * -> *) api (ctx :: [*]) r.
HasCLI m api ctx =>
Proxy m
-> Proxy api
-> Proxy ctx
-> CLIHandler m api r
-> CLIResult m api
-> r
cliHandler Proxy m
pm Proxy api
pa (forall (t :: [*]). Proxy t
forall {k} (t :: k). Proxy t
Proxy @context) CLIHandler m api r
h)
    (m (CLIResult m api) -> m r)
-> PStruct (m (CLIResult m api)) -> PStruct (m r)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Proxy m
-> Proxy api
-> Rec (ContextFor m) context
-> PStruct (m (CLIResult m api))
forall (m :: * -> *) api (context :: [*]).
HasCLI m api context =>
Proxy m
-> Proxy api
-> Rec (ContextFor m) context
-> PStruct (m (CLIResult m api))
cliPStructWithContext Proxy m
pm Proxy api
pa Rec (ContextFor m) context
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) =>
  -- | API
  Proxy api ->
  -- | Client monad
  Proxy m ->
  -- | Extra context
  Rec (ContextFor m) context ->
  -- | Options for top-level display
  InfoMod (m (CLIResult m api)) ->
  IO (m (CLIResult m api))
parseClientWithContext :: forall (m :: * -> *) api (context :: [*]).
HasCLI m api context =>
Proxy api
-> Proxy m
-> Rec (ContextFor m) context
-> InfoMod (m (CLIResult m api))
-> IO (m (CLIResult m api))
parseClientWithContext Proxy api
pa Proxy m
pm Rec (ContextFor m) context
p InfoMod (m (CLIResult m api))
im =
  ParserInfo (m (CLIResult m api)) -> IO (m (CLIResult m api))
forall a. ParserInfo a -> IO a
execParser (ParserInfo (m (CLIResult m api)) -> IO (m (CLIResult m api)))
-> (PStruct (m (CLIResult m api))
    -> ParserInfo (m (CLIResult m api)))
-> PStruct (m (CLIResult m api))
-> IO (m (CLIResult m api))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (PStruct (m (CLIResult m api))
 -> InfoMod (m (CLIResult m api))
 -> ParserInfo (m (CLIResult m api)))
-> InfoMod (m (CLIResult m api))
-> PStruct (m (CLIResult m api))
-> ParserInfo (m (CLIResult m api))
forall a b c. (a -> b -> c) -> b -> a -> c
flip PStruct (m (CLIResult m api))
-> InfoMod (m (CLIResult m api))
-> ParserInfo (m (CLIResult m api))
forall a. PStruct a -> InfoMod a -> ParserInfo a
structParser InfoMod (m (CLIResult m api))
im (PStruct (m (CLIResult m api)) -> IO (m (CLIResult m api)))
-> PStruct (m (CLIResult m api)) -> IO (m (CLIResult m api))
forall a b. (a -> b) -> a -> b
$
    Proxy m
-> Proxy api
-> Rec (ContextFor m) context
-> PStruct (m (CLIResult m api))
forall (m :: * -> *) api (context :: [*]).
HasCLI m api context =>
Proxy m
-> Proxy api
-> Rec (ContextFor m) context
-> PStruct (m (CLIResult m api))
cliPStructWithContext Proxy m
pm Proxy api
pa Rec (ContextFor m) context
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) =>
  -- | API
  Proxy api ->
  -- | Client monad
  Proxy m ->
  -- | Extra context
  Rec (ContextFor m) context ->
  -- | Options for top-level display
  InfoMod (m (CLIResult m api)) ->
  -- | Handler
  CLIHandler m api r ->
  IO (m r)
parseHandleClientWithContext :: forall (m :: * -> *) api (context :: [*]) r.
(HasCLI m api context, Functor m) =>
Proxy api
-> Proxy m
-> Rec (ContextFor m) context
-> InfoMod (m (CLIResult m api))
-> CLIHandler m api r
-> IO (m r)
parseHandleClientWithContext Proxy api
pa Proxy m
pm Rec (ContextFor m) context
p InfoMod (m (CLIResult m api))
im CLIHandler m api r
h =
  (CLIResult m api -> r) -> m (CLIResult m api) -> m r
forall a b. (a -> b) -> m a -> m b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Proxy m
-> Proxy api
-> Proxy context
-> CLIHandler m api r
-> CLIResult m api
-> r
forall r.
Proxy m
-> Proxy api
-> Proxy context
-> CLIHandler m api r
-> CLIResult m api
-> r
forall (m :: * -> *) api (ctx :: [*]) r.
HasCLI m api ctx =>
Proxy m
-> Proxy api
-> Proxy ctx
-> CLIHandler m api r
-> CLIResult m api
-> r
cliHandler Proxy m
pm Proxy api
pa (forall (t :: [*]). Proxy t
forall {k} (t :: k). Proxy t
Proxy @context) CLIHandler m api r
h)
    (m (CLIResult m api) -> m r)
-> IO (m (CLIResult m api)) -> IO (m r)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Proxy api
-> Proxy m
-> Rec (ContextFor m) context
-> InfoMod (m (CLIResult m api))
-> IO (m (CLIResult m api))
forall (m :: * -> *) api (context :: [*]).
HasCLI m api context =>
Proxy api
-> Proxy m
-> Rec (ContextFor m) context
-> InfoMod (m (CLIResult m api))
-> IO (m (CLIResult m api))
parseClientWithContext Proxy api
pa Proxy m
pm Rec (ContextFor m) context
p InfoMod (m (CLIResult m api))
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 '[]) =>
  -- | Client monad
  Proxy m ->
  -- | API
  Proxy api ->
  PStruct (m (CLIResult m api))
cliPStruct :: forall (m :: * -> *) api.
HasCLI m api '[] =>
Proxy m -> Proxy api -> PStruct (m (CLIResult m api))
cliPStruct Proxy m
pm Proxy api
pa = Proxy m
-> Proxy api
-> Rec (ContextFor m) '[]
-> PStruct (m (CLIResult m api))
forall (m :: * -> *) api (context :: [*]).
HasCLI m api context =>
Proxy m
-> Proxy api
-> Rec (ContextFor m) context
-> PStruct (m (CLIResult m api))
cliPStructWithContext Proxy m
pm Proxy api
pa Rec (ContextFor m) '[]
forall {u} (a :: u -> *). Rec a '[]
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) =>
  -- | Client monad
  Proxy m ->
  -- | API
  Proxy api ->
  -- | Handler
  CLIHandler m api r ->
  PStruct (m r)
cliHandlePStruct :: forall (m :: * -> *) api r.
(HasCLI m api '[], Functor m) =>
Proxy m -> Proxy api -> CLIHandler m api r -> PStruct (m r)
cliHandlePStruct Proxy m
pm Proxy api
pa = Proxy m
-> Proxy api
-> Rec (ContextFor m) '[]
-> CLIHandler m api r
-> PStruct (m r)
forall (m :: * -> *) api (context :: [*]) r.
(HasCLI m api context, Functor m) =>
Proxy m
-> Proxy api
-> Rec (ContextFor m) context
-> CLIHandler m api r
-> PStruct (m r)
cliHandlePStructWithContext Proxy m
pm Proxy api
pa Rec (ContextFor m) '[]
forall {u} (a :: u -> *). Rec a '[]
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 '[]) =>
  -- | API
  Proxy api ->
  -- | Client monad
  Proxy m ->
  -- | Options for top-level display
  InfoMod (m (CLIResult m api)) ->
  IO (m (CLIResult m api))
parseClient :: forall (m :: * -> *) api.
HasCLI m api '[] =>
Proxy api
-> Proxy m
-> InfoMod (m (CLIResult m api))
-> IO (m (CLIResult m api))
parseClient Proxy api
pa Proxy m
pm = Proxy api
-> Proxy m
-> Rec (ContextFor m) '[]
-> InfoMod (m (CLIResult m api))
-> IO (m (CLIResult m api))
forall (m :: * -> *) api (context :: [*]).
HasCLI m api context =>
Proxy api
-> Proxy m
-> Rec (ContextFor m) context
-> InfoMod (m (CLIResult m api))
-> IO (m (CLIResult m api))
parseClientWithContext Proxy api
pa Proxy m
pm Rec (ContextFor m) '[]
forall {u} (a :: u -> *). Rec a '[]
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) =>
  -- | API
  Proxy api ->
  -- | Client monad
  Proxy m ->
  -- | Options for top-level display
  InfoMod (m (CLIResult m api)) ->
  -- | Handler
  CLIHandler m api r ->
  IO (m r)
parseHandleClient :: forall (m :: * -> *) api r.
(HasCLI m api '[], Functor m) =>
Proxy api
-> Proxy m
-> InfoMod (m (CLIResult m api))
-> CLIHandler m api r
-> IO (m r)
parseHandleClient Proxy api
pa Proxy m
pm = Proxy api
-> Proxy m
-> Rec (ContextFor m) '[]
-> InfoMod (m (CLIResult m api))
-> CLIHandler m api r
-> IO (m r)
forall (m :: * -> *) api (context :: [*]) r.
(HasCLI m api context, Functor m) =>
Proxy api
-> Proxy m
-> Rec (ContextFor m) context
-> InfoMod (m (CLIResult m api))
-> CLIHandler m api r
-> IO (m r)
parseHandleClientWithContext Proxy api
pa Proxy m
pm Rec (ContextFor m) '[]
forall {u} (a :: u -> *). Rec a '[]
RNil