{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NoFieldSelectors #-}
{-# LANGUAGE RecordWildCards #-}
module EVP
  ( Name
  , Error(..)
  -- * Parsers
  , Scan
  , Var(..)
  , string
  , yaml
  , parse
  , secret
  , group
  , defaultsTo
  -- * Runner
  , Settings(..)
  , def
  , scan
  , scanWith
  , enumerate
  , help
  -- * Logger
  , GroupStack
  , renderError
  , assumePrefix
  , obsolete
  -- * Advanced
  , modifyError
  ) where

import Control.Applicative
import Control.Monad
import Data.Bifunctor
import Data.Default.Class
import Data.List (isPrefixOf, intercalate)
import Data.Map.Strict qualified as Map
import Data.Set qualified as Set
import Data.String
import Data.Yaml qualified as Yaml
import Data.Text qualified as T
import Data.Text.Lazy qualified as TL
import Data.Text.Encoding
import Data.Typeable
import EVP.Internal
import System.Environment
import System.Exit
import System.IO

data Var a = Var
  { forall a. Var a -> Name
name :: Name
  , forall a. Var a -> Maybe a
defaultValue :: Maybe a
  , forall a. Var a -> Maybe Name
metavar :: Maybe String
  } deriving (Int -> Var a -> ShowS
forall a. Show a => Int -> Var a -> ShowS
forall a. Show a => [Var a] -> ShowS
forall a. Show a => Var a -> Name
forall a.
(Int -> a -> ShowS) -> (a -> Name) -> ([a] -> ShowS) -> Show a
showList :: [Var a] -> ShowS
$cshowList :: forall a. Show a => [Var a] -> ShowS
show :: Var a -> Name
$cshow :: forall a. Show a => Var a -> Name
showsPrec :: Int -> Var a -> ShowS
$cshowsPrec :: forall a. Show a => Int -> Var a -> ShowS
Show, Var a -> Var a -> Bool
forall a. Eq a => Var a -> Var a -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Var a -> Var a -> Bool
$c/= :: forall a. Eq a => Var a -> Var a -> Bool
== :: Var a -> Var a -> Bool
$c== :: forall a. Eq a => Var a -> Var a -> Bool
Eq)

-- | Provide a default value.
defaultsTo :: Var a -> a -> Var a
defaultsTo :: forall a. Var a -> a -> Var a
defaultsTo Var a
v a
a = Var a
v { $sel:defaultValue:Var :: Maybe a
defaultValue = forall a. a -> Maybe a
Just a
a }
infixl 1 `defaultsTo`

instance Typeable a => IsString (Var a) where
  fromString :: Name -> Var a
fromString Name
name = forall a. Name -> Maybe a -> Maybe Name -> Var a
Var Name
name forall a. Maybe a
Nothing forall a. Maybe a
Nothing

-- | Obtain the environment variable.
string :: (IsString a, Show a, Typeable a) => Var a -> Scan a
string :: forall a. (IsString a, Show a, Typeable a) => Var a -> Scan a
string Var{Name
Maybe a
Maybe Name
metavar :: Maybe Name
defaultValue :: Maybe a
name :: Name
$sel:metavar:Var :: forall a. Var a -> Maybe Name
$sel:defaultValue:Var :: forall a. Var a -> Maybe a
$sel:name:Var :: forall a. Var a -> Name
..} = forall a b. ScanF a -> Scan (a -> b) -> Scan b
Scan ScanF
  { Name
name :: Name
name :: Name
name
  , parser :: Maybe Name -> Either Error (Name, a)
parser = \case
    Maybe Name
Nothing -> case Maybe a
defaultValue of
      Maybe a
Nothing -> forall a b. a -> Either a b
Left (Name -> Error
Missing Name
name)
      Just a
d -> forall a b. b -> Either a b
Right (forall a. Show a => a -> Name
show a
d forall a. Semigroup a => a -> a -> a
<> Name
" (default)", a
d)
    Just Name
x -> forall a b. b -> Either a b
Right (Name
x, forall a. IsString a => Name -> a
fromString Name
x)
  , metavar :: Maybe Name
metavar = Maybe Name
metavar forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> forall a. (Typeable a, Show a) => a -> Name
toString forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe a
defaultValue
  } (forall a. a -> Scan a
Pure forall a. a -> a
id)

toString :: (Typeable a, Show a) => a -> String
toString :: forall a. (Typeable a, Show a) => a -> Name
toString a
val
  | Just Name
str <- forall a b. (Typeable a, Typeable b) => a -> Maybe b
cast a
val = Name
str
  | Just Text
str <- forall a b. (Typeable a, Typeable b) => a -> Maybe b
cast a
val = Text -> Name
T.unpack Text
str
  | Just Text
str <- forall a b. (Typeable a, Typeable b) => a -> Maybe b
cast a
val = Text -> Name
TL.unpack Text
str
  | Just ByteString
str <- forall a b. (Typeable a, Typeable b) => a -> Maybe b
cast a
val = Text -> Name
T.unpack forall a b. (a -> b) -> a -> b
$ ByteString -> Text
decodeUtf8 ByteString
str
  | Bool
otherwise = forall a. Show a => a -> Name
show a
val

-- | Parse the environment variable as a YAML value.
yaml :: forall a. (Show a, Typeable a, Yaml.FromJSON a, Yaml.ToJSON a) => Var a -> Scan a
yaml :: forall a.
(Show a, Typeable a, FromJSON a, ToJSON a) =>
Var a -> Scan a
yaml Var{Name
Maybe a
Maybe Name
metavar :: Maybe Name
defaultValue :: Maybe a
name :: Name
$sel:metavar:Var :: forall a. Var a -> Maybe Name
$sel:defaultValue:Var :: forall a. Var a -> Maybe a
$sel:name:Var :: forall a. Var a -> Name
..} = forall a. Show a => Var a -> (Name -> Either Name a) -> Scan a
parse Var
  { $sel:metavar:Var :: Maybe Name
metavar = Maybe Name
metavar
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Text -> Name
T.unpack forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text
T.strip forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Text
decodeUtf8 forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. ToJSON a => a -> ByteString
Yaml.encode) Maybe a
defaultValue
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> forall a. a -> Maybe a
Just (forall a. Show a => a -> Name
show forall a b. (a -> b) -> a -> b
$ forall {k} (proxy :: k -> *) (a :: k).
Typeable a =>
proxy a -> TypeRep
typeRep (forall {k} (t :: k). Proxy t
Proxy :: Proxy a))
  , Name
Maybe a
defaultValue :: Maybe a
name :: Name
$sel:defaultValue:Var :: Maybe a
$sel:name:Var :: Name
..
  }
  forall a. FromJSON a => Name -> Either Name a
decodeYaml

decodeYaml :: Yaml.FromJSON a => String -> Either String a
decodeYaml :: forall a. FromJSON a => Name -> Either Name a
decodeYaml = forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first ParseException -> Name
Yaml.prettyPrintParseException forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. FromJSON a => ByteString -> Either ParseException a
Yaml.decodeEither' forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> ByteString
encodeUtf8 forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. IsString a => Name -> a
fromString

-- | Parse the environment variable with a custom parser.
parse :: (Show a) => Var a -> (String -> Either String a) -> Scan a
parse :: forall a. Show a => Var a -> (Name -> Either Name a) -> Scan a
parse Var{Name
Maybe a
Maybe Name
metavar :: Maybe Name
defaultValue :: Maybe a
name :: Name
$sel:metavar:Var :: forall a. Var a -> Maybe Name
$sel:defaultValue:Var :: forall a. Var a -> Maybe a
$sel:name:Var :: forall a. Var a -> Name
..} Name -> Either Name a
f = forall a b. ScanF a -> Scan (a -> b) -> Scan b
Scan ScanF
  { Name
name :: Name
name :: Name
name
  , parser :: Maybe Name -> Either Error (Name, a)
parser = \case
    Maybe Name
Nothing -> case Maybe a
defaultValue of
      Maybe a
Nothing -> forall a b. a -> Either a b
Left (Name -> Error
Missing Name
name)
      Just a
d -> forall a b. b -> Either a b
Right (forall a. Show a => a -> Name
show a
d forall a. Semigroup a => a -> a -> a
<> Name
" (default)", a
d)
    Just Name
x -> forall (p :: * -> * -> *) a b c d.
Bifunctor p =>
(a -> b) -> (c -> d) -> p a c -> p b d
bimap (Name -> Name -> Name -> Error
ParseError Name
name Name
x) forall a. Show a => a -> (Name, a)
withShow forall a b. (a -> b) -> a -> b
$ Name -> Either Name a
f Name
x
  , Maybe Name
metavar :: Maybe Name
metavar :: Maybe Name
metavar
  }
  (forall a. a -> Scan a
Pure forall a. a -> a
id)

-- | Disable logging of parsed values.
secret :: Scan a -> Scan a
secret :: forall a. Scan a -> Scan a
secret (Pure a
a) = forall a. a -> Scan a
Pure a
a
secret (Scan ScanF a
v Scan (a -> a)
k) = forall a b. ScanF a -> Scan (a -> b) -> Scan b
Scan (ScanF a
v { parser :: Maybe Name -> Either Error (Name, a)
parser = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first (forall a b. a -> b -> a
const Name
"<REDACTED>")) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. ScanF a -> Maybe Name -> Either Error (Name, a)
parser ScanF a
v }) (forall a. Scan a -> Scan a
secret Scan (a -> a)
k)
secret (Group Name
name Scan a
s) = forall a. Name -> Scan a -> Scan a
Group Name
name forall a b. (a -> b) -> a -> b
$ forall a. Scan a -> Scan a
secret Scan a
s

-- | Give a name to a group of parsers.
group :: String -> Scan a -> Scan a
group :: forall a. Name -> Scan a -> Scan a
group = forall a. Name -> Scan a -> Scan a
Group

withShow :: Show a => a -> (String, a)
withShow :: forall a. Show a => a -> (Name, a)
withShow a
x = (forall a. Show a => a -> Name
show a
x, a
x)

type EnvMap = Map.Map String String

type GroupStack = [String]

renderError :: Error -> String
renderError :: Error -> Name
renderError (Missing Name
name) = [Name] -> Name
unwords [Name
"Missing environment variable", Name
name]
renderError (ParseError Name
name Name
value Name
reason) = [Name] -> Name
unwords [Name
"Failed to parse", Name
name forall a. Semigroup a => a -> a -> a
<> Name
"=" forall a. Semigroup a => a -> a -> a
<> Name
value forall a. Semigroup a => a -> a -> a
<> Name
":", Name
reason]
renderError (CustomError Name
reason) = Name
reason

data Settings = Settings
  { Settings -> [Name] -> Name -> Name -> IO ()
parseLogger :: GroupStack -> Name -> String -> IO ()
  , Settings -> [Name] -> Error -> IO ()
errorLogger :: GroupStack -> Error -> IO ()
  , Settings -> Name -> Maybe (IO ())
unusedLogger :: Name -> Maybe (IO ())
  , Settings -> Bool
pedantic :: Bool -- ^ exit on warning
  , Settings -> Maybe Name
helpFlag :: Maybe Name -- ^ when an environment varialbe with this name is set, print the help message and exit
  }

header :: GroupStack -> String -> String
header :: [Name] -> ShowS
header [] Name
level = Name
"[EVP " forall a. Semigroup a => a -> a -> a
<> Name
level forall a. Semigroup a => a -> a -> a
<> Name
"] "
header [Name]
xs Name
level = forall a. Monoid a => [a] -> a
mconcat [Name
"[EVP ", Name
level, forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (Name
"/"<>) forall a b. (a -> b) -> a -> b
$ forall a. [a] -> [a]
reverse [Name]
xs, Name
"] "]

instance Default Settings where
  def :: Settings
def = Settings
    { $sel:parseLogger:Settings :: [Name] -> Name -> Name -> IO ()
parseLogger = \[Name]
stack Name
name Name
value -> do
      Bool
isTerminal <- Handle -> IO Bool
hIsTerminalDevice Handle
stdout
      Handle -> Name -> IO ()
hPutStrLn (if Bool
isTerminal then Handle
stdout else Handle
stderr)
        forall a b. (a -> b) -> a -> b
$ [Name] -> ShowS
header [Name]
stack Name
"Info" forall a. Semigroup a => a -> a -> a
<> Name
name forall a. Semigroup a => a -> a -> a
<> Name
": " forall a. Semigroup a => a -> a -> a
<> Name
value
    , $sel:errorLogger:Settings :: [Name] -> Error -> IO ()
errorLogger = \[Name]
stack Error
e -> Handle -> Name -> IO ()
hPutStrLn Handle
stderr forall a b. (a -> b) -> a -> b
$ forall a. [a] -> [[a]] -> [a]
intercalate Name
"\n" forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map ([Name] -> ShowS
header [Name]
stack Name
"Error" <>) forall a b. (a -> b) -> a -> b
$ Name -> [Name]
lines forall a b. (a -> b) -> a -> b
$ Error -> Name
renderError Error
e
    , $sel:unusedLogger:Settings :: Name -> Maybe (IO ())
unusedLogger = forall a. Monoid a => a
mempty
    , $sel:pedantic:Settings :: Bool
pedantic = Bool
False
    , $sel:helpFlag:Settings :: Maybe Name
helpFlag = forall a. a -> Maybe a
Just Name
"EVP_HELP"
    }

-- | Custom logging function for 'unusedLogger'.
-- @'assumePrefix' p@ prints a warning for each unused environment variable prefixed by @p@.
assumePrefix :: String -> Name -> Maybe (IO ())
assumePrefix :: Name -> Name -> Maybe (IO ())
assumePrefix Name
prefix Name
name
  | forall a. Eq a => [a] -> [a] -> Bool
isPrefixOf Name
prefix Name
name = forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ Handle -> Name -> IO ()
hPutStrLn Handle
stderr forall a b. (a -> b) -> a -> b
$ [Name] -> Name
unwords [Name
"[EVP Warn]", Name
name, Name
"is set but not used"]
  | Bool
otherwise = forall a. Maybe a
Nothing

-- | @'obsolete' names@ prints a warning if any of the @names@ is set but not used.
obsolete :: [Name] -> Name -> Maybe (IO ())
obsolete :: [Name] -> Name -> Maybe (IO ())
obsolete [Name]
nameSet Name
name
  | forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
elem Name
name [Name]
nameSet = forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ Handle -> Name -> IO ()
hPutStrLn Handle
stderr forall a b. (a -> b) -> a -> b
$ [Name] -> Name
unwords [Name
"[EVP Warn]", Name
name, Name
"is obsolete"]
  | Bool
otherwise = forall a. Maybe a
Nothing

-- | Enumerate the names of the variables it would parse.
enumerate :: Scan a -> [Name]
enumerate :: forall a. Scan a -> [Name]
enumerate Scan a
m = forall a. Set a -> [a]
Set.toList forall a b. (a -> b) -> a -> b
$ forall a. Set Name -> Scan a -> Set Name
go forall a. Set a
Set.empty Scan a
m where
  go :: Set.Set Name -> Scan a -> Set.Set Name
  go :: forall a. Set Name -> Scan a -> Set Name
go !Set Name
s (Pure a
_) = Set Name
s
  go !Set Name
s (Scan ScanF a
k Scan (a -> a)
cont) = forall a. Set Name -> Scan a -> Set Name
go (forall a. Ord a => a -> Set a -> Set a
Set.insert (forall a. ScanF a -> Name
name ScanF a
k) Set Name
s) Scan (a -> a)
cont
  go !Set Name
s (Group Name
_ Scan a
cont) = forall a. Set Name -> Scan a -> Set Name
go Set Name
s Scan a
cont

-- | Parse environment variables with the default settings.
scan :: Scan a -> IO a
scan :: forall a. Scan a -> IO a
scan = forall a. Settings -> Scan a -> IO a
scanWith forall a. Default a => a
def

-- | Parse environment variables with custom settings.
scanWith :: Settings -> Scan a -> IO a
scanWith :: forall a. Settings -> Scan a -> IO a
scanWith Settings{Bool
Maybe Name
Name -> Maybe (IO ())
[Name] -> Name -> Name -> IO ()
[Name] -> Error -> IO ()
helpFlag :: Maybe Name
pedantic :: Bool
unusedLogger :: Name -> Maybe (IO ())
errorLogger :: [Name] -> Error -> IO ()
parseLogger :: [Name] -> Name -> Name -> IO ()
$sel:helpFlag:Settings :: Settings -> Maybe Name
$sel:pedantic:Settings :: Settings -> Bool
$sel:unusedLogger:Settings :: Settings -> Name -> Maybe (IO ())
$sel:errorLogger:Settings :: Settings -> [Name] -> Error -> IO ()
$sel:parseLogger:Settings :: Settings -> [Name] -> Name -> Name -> IO ()
..} Scan a
action = do
  Map Name Name
envs0 <- forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO [(Name, Name)]
getEnvironment

  forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ Maybe Name
helpFlag forall a b. (a -> b) -> a -> b
$ \Name
flag -> do
    forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Name
flag forall k a. Ord k => k -> Map k a -> Bool
`Map.member` Map Name Name
envs0) forall a b. (a -> b) -> a -> b
$ do
      Name -> IO ()
putStrLn forall a b. (a -> b) -> a -> b
$ forall a. Scan a -> Name
help Scan a
action
      forall a. IO a
exitSuccess

  (Map Name Name
remainder, [([Name], Error)]
errors, Maybe a
result) <- forall a.
Map Name Name
-> Map Name Name
-> [Name]
-> Scan a
-> IO (Map Name Name, [([Name], Error)], Maybe a)
go Map Name Name
envs0 Map Name Name
envs0 [] Scan a
action
  forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry [Name] -> Error -> IO ()
errorLogger) [([Name], Error)]
errors
  case forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
foldMap Name -> Maybe (IO ())
unusedLogger forall a b. (a -> b) -> a -> b
$ forall k a. Map k a -> [k]
Map.keys Map Name Name
remainder of
    Maybe (IO ())
Nothing -> forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
    Just IO ()
m -> do
      IO ()
m
      forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when Bool
pedantic forall a. IO a
exitFailure
  case Maybe a
result of
    Maybe a
Nothing -> forall a. IO a
exitFailure
    Just a
a -> forall (f :: * -> *) a. Applicative f => a -> f a
pure a
a
  where
    go :: EnvMap -> EnvMap -> GroupStack -> Scan a -> IO (EnvMap, [(GroupStack, Error)], Maybe a)
    go :: forall a.
Map Name Name
-> Map Name Name
-> [Name]
-> Scan a
-> IO (Map Name Name, [([Name], Error)], Maybe a)
go Map Name Name
_ Map Name Name
envs [Name]
_ (Pure a
a) = forall (f :: * -> *) a. Applicative f => a -> f a
pure (Map Name Name
envs, [], forall a. a -> Maybe a
Just a
a)
    go Map Name Name
allEnvs Map Name Name
envs [Name]
groupStack (Group Name
name Scan a
inner) = do
      let stack :: [Name]
stack = Name
name forall a. a -> [a] -> [a]
: [Name]
groupStack
      forall a.
Map Name Name
-> Map Name Name
-> [Name]
-> Scan a
-> IO (Map Name Name, [([Name], Error)], Maybe a)
go Map Name Name
allEnvs Map Name Name
envs [Name]
stack Scan a
inner
    go Map Name Name
allEnvs Map Name Name
envs [Name]
groupStack (Scan ScanF{Name
Maybe Name
Maybe Name -> Either Error (Name, a)
metavar :: Maybe Name
parser :: Maybe Name -> Either Error (Name, a)
name :: Name
metavar :: forall a. ScanF a -> Maybe Name
parser :: forall a. ScanF a -> Maybe Name -> Either Error (Name, a)
name :: forall a. ScanF a -> Name
..} Scan (a -> a)
cont) = case Maybe Name -> Either Error (Name, a)
parser (forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup Name
name Map Name Name
allEnvs) of
      Left Error
e -> do
        (Map Name Name
remainder, [([Name], Error)]
errors, Maybe (a -> a)
_) <- forall a.
Map Name Name
-> Map Name Name
-> [Name]
-> Scan a
-> IO (Map Name Name, [([Name], Error)], Maybe a)
go Map Name Name
allEnvs (forall k a. Ord k => k -> Map k a -> Map k a
Map.delete Name
name Map Name Name
envs) [Name]
groupStack Scan (a -> a)
cont
        forall (f :: * -> *) a. Applicative f => a -> f a
pure (Map Name Name
remainder, ([Name]
groupStack, Error
e) forall a. a -> [a] -> [a]
: [([Name], Error)]
errors, forall a. Maybe a
Nothing)
      Right (Name
display, a
v) -> do
        [Name] -> Name -> Name -> IO ()
parseLogger [Name]
groupStack Name
name Name
display
        (Map Name Name
remainder, [([Name], Error)]
errors, Maybe (a -> a)
func) <- forall a.
Map Name Name
-> Map Name Name
-> [Name]
-> Scan a
-> IO (Map Name Name, [([Name], Error)], Maybe a)
go Map Name Name
allEnvs (forall k a. Ord k => k -> Map k a -> Map k a
Map.delete Name
name Map Name Name
envs) [Name]
groupStack Scan (a -> a)
cont
        forall (f :: * -> *) a. Applicative f => a -> f a
pure (Map Name Name
remainder, [([Name], Error)]
errors, (forall a b. (a -> b) -> a -> b
$ a
v) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe (a -> a)
func)

modifyError :: (Error -> Error) -> Scan a -> Scan a
modifyError :: forall a. (Error -> Error) -> Scan a -> Scan a
modifyError Error -> Error
func = forall a. Scan a -> Scan a
go where
  go :: Scan a -> Scan a
  go :: forall a. Scan a -> Scan a
go (Pure a
a) = forall a. a -> Scan a
Pure a
a
  go (Scan ScanF a
v Scan (a -> a)
k) = forall a b. ScanF a -> Scan (a -> b) -> Scan b
Scan (forall {a}. ScanF a -> ScanF a
apply ScanF a
v) (forall a. Scan a -> Scan a
go Scan (a -> a)
k)
  go (Group Name
name Scan a
k) = forall a. Name -> Scan a -> Scan a
Group Name
name (forall a. Scan a -> Scan a
go Scan a
k)

  apply :: ScanF a -> ScanF a
apply ScanF{Name
Maybe Name
Maybe Name -> Either Error (Name, a)
metavar :: Maybe Name
parser :: Maybe Name -> Either Error (Name, a)
name :: Name
metavar :: forall a. ScanF a -> Maybe Name
parser :: forall a. ScanF a -> Maybe Name -> Either Error (Name, a)
name :: forall a. ScanF a -> Name
..} = ScanF
    { Name
name :: Name
name :: Name
name
    , parser :: Maybe Name -> Either Error (Name, a)
parser = forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first Error -> Error
func forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe Name -> Either Error (Name, a)
parser
    , Maybe Name
metavar :: Maybe Name
metavar :: Maybe Name
metavar
    }

-- | Display the list of environment variables and their default values in the dotenv format.
help :: Scan a -> String
help :: forall a. Scan a -> Name
help = [Name] -> Name
unlines forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Int -> Scan a -> [Name]
go Int
1 where

  go :: Int -> Scan a -> [String]
  go :: forall a. Int -> Scan a -> [Name]
go Int
_ (Pure a
_) = []
  go Int
depth (Scan ScanF a
v Scan (a -> a)
k) = forall a. ScanF a -> Name
format ScanF a
v forall a. a -> [a] -> [a]
: forall a. Int -> Scan a -> [Name]
go Int
depth Scan (a -> a)
k
  go Int
depth (Group Name
name Scan a
k) = (forall a. Int -> a -> [a]
replicate Int
depth Char
'#' forall a. Semigroup a => a -> a -> a
<> Name
" " forall a. Semigroup a => a -> a -> a
<> Name
name) forall a. a -> [a] -> [a]
: forall a. Int -> Scan a -> [Name]
go (Int
depth forall a. Num a => a -> a -> a
+ Int
1) Scan a
k

  format :: ScanF a -> Name
format ScanF{Name
Maybe Name
Maybe Name -> Either Error (Name, a)
metavar :: Maybe Name
parser :: Maybe Name -> Either Error (Name, a)
name :: Name
metavar :: forall a. ScanF a -> Maybe Name
parser :: forall a. ScanF a -> Maybe Name -> Either Error (Name, a)
name :: forall a. ScanF a -> Name
..} = Name
name forall a. Semigroup a => a -> a -> a
<> Name
"=" forall a. Semigroup a => a -> a -> a
<> forall b a. b -> (a -> b) -> Maybe a -> b
maybe Name
"" forall a. a -> a
id Maybe Name
metavar