    {-|
    Module     : Data.Ini.Config
    Copyright  : (c) Getty Ritter, 2017
    License    : BSD
    Maintainer : Getty Ritter <config-ini@infinitenegativeutility.com>
    Stability  : experimental

    The 'config-ini' library exports some simple monadic functions to
    make parsing INI-like configuration easier. INI files have a
    two-level structure: the top-level named chunks of configuration,
    and the individual key-value pairs contained within those chunks.
    For example, the following INI file has two sections, @NETWORK@
    and @LOCAL@, and each contains its own key-value pairs. Comments,
    which begin with @#@ or @;@, are ignored:

    > [NETWORK]
    > host = example.com
    > port = 7878
    >
    > # here is a comment
    > [LOCAL]
    > user = terry

    The combinators provided here are designed to write quick and
    idiomatic parsers for files of this form. Sections are parsed by
    'IniParser' computations, like 'section' and its variations,
    while the fields within sections are parsed by 'SectionParser'
    computations, like 'field' and its variations. If we want to
    parse an INI file like the one above, treating the entire
    @LOCAL@ section as optional, we can write it like this:

    > data Config = Config
    >   { cfNetwork :: NetworkConfig, cfLocal :: Maybe LocalConfig }
    >     deriving (Eq, Show)
    >
    > data NetworkConfig = NetworkConfig
    >   { netHost :: String, netPort :: Int }
    >     deriving (Eq, Show)
    >
    > data LocalConfig = LocalConfig
    >   { localUser :: Text }
    >     deriving (Eq, Show)
    >
    > configParser :: IniParser Config
    > configParser = do
    >   netCf <- section "NETWORK" $ do
    >     host <- fieldOf "host" string
    >     port <- fieldOf "port" number
    >     return NetworkConfig { netHost = host, netPort = port }
    >   locCf <- sectionMb "LOCAL" $
    >     LocalConfig <$> field "user"
    >   return Config { cfNetwork = netCf, cfLocal = locCf }

    We can run our computation with 'parseIniFile', which,
    when run on our example file above, would produce the
    following:

    >>> parseIniFile example configParser
    Right (Config {cfNetwork = NetworkConfig {netHost = "example.com", netPort = 7878}, cfLocal = Just (LocalConfig {localUser = "terry"})})

    -}

    {-# LANGUAGE OverloadedStrings #-}
    {-# LANGUAGE ScopedTypeVariables #-}
    {-# LANGUAGE GeneralizedNewtypeDeriving #-}

    module Data.Ini.Config
    (
    -- * Parsing Files
      parseIniFile
    -- * Parser Types
    , IniParser
    , SectionParser
    -- * Section-Level Parsing
    , section
    , sections
    , sectionOf
    , sectionsOf
    , sectionMb
    , sectionDef
    -- * Field-Level Parsing
    , field
    , fieldOf
    , fieldMb
    , fieldMbOf
    , fieldDef
    , fieldDefOf
    , fieldFlag
    , fieldFlagDef
    -- * Reader Functions
    , readable
    , number
    , string
    , flag
    , listWithSeparator
    ) where

    import           Control.Applicative (Alternative(..))
    import           Control.Monad.Trans.Except
    import           Data.Ini.Config.Raw
    import           Data.Sequence (Seq)
    import qualified Data.Sequence as Seq
    import           Data.String (IsString(..))
    import           Data.Text (Text)
    import qualified Data.Text as T
    import           Data.Typeable (Typeable, Proxy(..), typeRep)
    import           GHC.Exts (IsList(..))
    import           Text.Read (readMaybe)

    lkp :: NormalizedText -> Seq (NormalizedText, a) -> Maybe a
    lkp :: NormalizedText -> Seq (NormalizedText, a) -> Maybe a
    lkp t :: NormalizedText
    t = ViewL (NormalizedText, a) -> Maybe a
    forall a. ViewL (NormalizedText, a) -> Maybe a
    go (ViewL (NormalizedText, a) -> Maybe a)
    -> (Seq (NormalizedText, a) -> ViewL (NormalizedText, a))
    -> Seq (NormalizedText, a)
    -> Maybe a
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Seq (NormalizedText, a) -> ViewL (NormalizedText, a)
    forall a. Seq a -> ViewL a
    Seq.viewl
      where go :: ViewL (NormalizedText, a) -> Maybe a
    go ((t' :: NormalizedText
    t', x :: a
    x) Seq.:< rs :: Seq (NormalizedText, a)
    rs)
              | NormalizedText
    t NormalizedText -> NormalizedText -> Bool
    forall a. Eq a => a -> a -> Bool
    == NormalizedText
    t'   = a -> Maybe a
    forall a. a -> Maybe a
    Just a
    x
              | Bool
    otherwise = ViewL (NormalizedText, a) -> Maybe a
    go (Seq (NormalizedText, a) -> ViewL (NormalizedText, a)
    forall a. Seq a -> ViewL a
    Seq.viewl Seq (NormalizedText, a)
    rs)
            go Seq.EmptyL = Maybe a
    forall a. Maybe a
    Nothing

    addLineInformation :: Int -> Text -> StParser s a -> StParser s a
    addLineInformation :: Int -> Text -> StParser s a -> StParser s a
    addLineInformation lineNo :: Int
    lineNo sec :: Text
    sec = ([Char] -> [Char]) -> StParser s a -> StParser s a
    forall (m :: * -> *) e e' a.
    Functor m =>
    (e -> e') -> ExceptT e m a -> ExceptT e' m a
    withExceptT [Char] -> [Char]
    go
      where go :: [Char] -> [Char]
    go e :: [Char]
    e = "Line " [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ Int -> [Char]
    forall a. Show a => a -> [Char]
    show Int
    lineNo [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++
                   ", in section " [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ Text -> [Char]
    forall a. Show a => a -> [Char]
    show Text
    sec [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++
                   ": " [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    e

    type StParser s a = ExceptT String ((->) s) a

    -- | An 'IniParser' value represents a computation for parsing entire
    --   INI-format files.
    newtype IniParser a = IniParser (StParser RawIni a)
      deriving (a -> IniParser b -> IniParser a
    (a -> b) -> IniParser a -> IniParser b
    (forall a b. (a -> b) -> IniParser a -> IniParser b)
    -> (forall a b. a -> IniParser b -> IniParser a)
    -> Functor IniParser
    forall a b. a -> IniParser b -> IniParser a
    forall a b. (a -> b) -> IniParser a -> IniParser b
    forall (f :: * -> *).
    (forall a b. (a -> b) -> f a -> f b)
    -> (forall a b. a -> f b -> f a) -> Functor f
    <$ :: a -> IniParser b -> IniParser a
    $c<$ :: forall a b. a -> IniParser b -> IniParser a
    fmap :: (a -> b) -> IniParser a -> IniParser b
    $cfmap :: forall a b. (a -> b) -> IniParser a -> IniParser b
    Functor, Functor IniParser
    a -> IniParser a
    Functor IniParser =>
    (forall a. a -> IniParser a)
    -> (forall a b. IniParser (a -> b) -> IniParser a -> IniParser b)
    -> (forall a b c.
        (a -> b -> c) -> IniParser a -> IniParser b -> IniParser c)
    -> (forall a b. IniParser a -> IniParser b -> IniParser b)
    -> (forall a b. IniParser a -> IniParser b -> IniParser a)
    -> Applicative IniParser
    IniParser a -> IniParser b -> IniParser b
    IniParser a -> IniParser b -> IniParser a
    IniParser (a -> b) -> IniParser a -> IniParser b
    (a -> b -> c) -> IniParser a -> IniParser b -> IniParser c
    forall a. a -> IniParser a
    forall a b. IniParser a -> IniParser b -> IniParser a
    forall a b. IniParser a -> IniParser b -> IniParser b
    forall a b. IniParser (a -> b) -> IniParser a -> IniParser b
    forall a b c.
    (a -> b -> c) -> IniParser a -> IniParser b -> IniParser c
    forall (f :: * -> *).
    Functor f =>
    (forall a. a -> f a)
    -> (forall a b. f (a -> b) -> f a -> f b)
    -> (forall a b c. (a -> b -> c) -> f a -> f b -> f c)
    -> (forall a b. f a -> f b -> f b)
    -> (forall a b. f a -> f b -> f a)
    -> Applicative f
    <* :: IniParser a -> IniParser b -> IniParser a
    $c<* :: forall a b. IniParser a -> IniParser b -> IniParser a
    *> :: IniParser a -> IniParser b -> IniParser b
    $c*> :: forall a b. IniParser a -> IniParser b -> IniParser b
    liftA2 :: (a -> b -> c) -> IniParser a -> IniParser b -> IniParser c
    $cliftA2 :: forall a b c.
    (a -> b -> c) -> IniParser a -> IniParser b -> IniParser c
    <*> :: IniParser (a -> b) -> IniParser a -> IniParser b
    $c<*> :: forall a b. IniParser (a -> b) -> IniParser a -> IniParser b
    pure :: a -> IniParser a
    $cpure :: forall a. a -> IniParser a
    $cp1Applicative :: Functor IniParser
    Applicative, Applicative IniParser
    IniParser a
    Applicative IniParser =>
    (forall a. IniParser a)
    -> (forall a. IniParser a -> IniParser a -> IniParser a)
    -> (forall a. IniParser a -> IniParser [a])
    -> (forall a. IniParser a -> IniParser [a])
    -> Alternative IniParser
    IniParser a -> IniParser a -> IniParser a
    IniParser a -> IniParser [a]
    IniParser a -> IniParser [a]
    forall a. IniParser a
    forall a. IniParser a -> IniParser [a]
    forall a. IniParser a -> IniParser a -> IniParser a
    forall (f :: * -> *).
    Applicative f =>
    (forall a. f a)
    -> (forall a. f a -> f a -> f a)
    -> (forall a. f a -> f [a])
    -> (forall a. f a -> f [a])
    -> Alternative f
    many :: IniParser a -> IniParser [a]
    $cmany :: forall a. IniParser a -> IniParser [a]
    some :: IniParser a -> IniParser [a]
    $csome :: forall a. IniParser a -> IniParser [a]
    <|> :: IniParser a -> IniParser a -> IniParser a
    $c<|> :: forall a. IniParser a -> IniParser a -> IniParser a
    empty :: IniParser a
    $cempty :: forall a. IniParser a
    $cp1Alternative :: Applicative IniParser
    Alternative, Applicative IniParser
    a -> IniParser a
    Applicative IniParser =>
    (forall a b. IniParser a -> (a -> IniParser b) -> IniParser b)
    -> (forall a b. IniParser a -> IniParser b -> IniParser b)
    -> (forall a. a -> IniParser a)
    -> Monad IniParser
    IniParser a -> (a -> IniParser b) -> IniParser b
    IniParser a -> IniParser b -> IniParser b
    forall a. a -> IniParser a
    forall a b. IniParser a -> IniParser b -> IniParser b
    forall a b. IniParser a -> (a -> IniParser b) -> IniParser b
    forall (m :: * -> *).
    Applicative m =>
    (forall a b. m a -> (a -> m b) -> m b)
    -> (forall a b. m a -> m b -> m b)
    -> (forall a. a -> m a)
    -> Monad m
    return :: a -> IniParser a
    $creturn :: forall a. a -> IniParser a
    >> :: IniParser a -> IniParser b -> IniParser b
    $c>> :: forall a b. IniParser a -> IniParser b -> IniParser b
    >>= :: IniParser a -> (a -> IniParser b) -> IniParser b
    $c>>= :: forall a b. IniParser a -> (a -> IniParser b) -> IniParser b
    $cp1Monad :: Applicative IniParser
    Monad)

    -- | A 'SectionParser' value represents a computation for parsing a single
    --   section of an INI-format file.
    newtype SectionParser a = SectionParser (StParser IniSection a)
      deriving (a -> SectionParser b -> SectionParser a
    (a -> b) -> SectionParser a -> SectionParser b
    (forall a b. (a -> b) -> SectionParser a -> SectionParser b)
    -> (forall a b. a -> SectionParser b -> SectionParser a)
    -> Functor SectionParser
    forall a b. a -> SectionParser b -> SectionParser a
    forall a b. (a -> b) -> SectionParser a -> SectionParser b
    forall (f :: * -> *).
    (forall a b. (a -> b) -> f a -> f b)
    -> (forall a b. a -> f b -> f a) -> Functor f
    <$ :: a -> SectionParser b -> SectionParser a
    $c<$ :: forall a b. a -> SectionParser b -> SectionParser a
    fmap :: (a -> b) -> SectionParser a -> SectionParser b
    $cfmap :: forall a b. (a -> b) -> SectionParser a -> SectionParser b
    Functor, Functor SectionParser
    a -> SectionParser a
    Functor SectionParser =>
    (forall a. a -> SectionParser a)
    -> (forall a b.
        SectionParser (a -> b) -> SectionParser a -> SectionParser b)
    -> (forall a b c.
        (a -> b -> c)
        -> SectionParser a -> SectionParser b -> SectionParser c)
    -> (forall a b.
        SectionParser a -> SectionParser b -> SectionParser b)
    -> (forall a b.
        SectionParser a -> SectionParser b -> SectionParser a)
    -> Applicative SectionParser
    SectionParser a -> SectionParser b -> SectionParser b
    SectionParser a -> SectionParser b -> SectionParser a
    SectionParser (a -> b) -> SectionParser a -> SectionParser b
    (a -> b -> c)
    -> SectionParser a -> SectionParser b -> SectionParser c
    forall a. a -> SectionParser a
    forall a b. SectionParser a -> SectionParser b -> SectionParser a
    forall a b. SectionParser a -> SectionParser b -> SectionParser b
    forall a b.
    SectionParser (a -> b) -> SectionParser a -> SectionParser b
    forall a b c.
    (a -> b -> c)
    -> SectionParser a -> SectionParser b -> SectionParser c
    forall (f :: * -> *).
    Functor f =>
    (forall a. a -> f a)
    -> (forall a b. f (a -> b) -> f a -> f b)
    -> (forall a b c. (a -> b -> c) -> f a -> f b -> f c)
    -> (forall a b. f a -> f b -> f b)
    -> (forall a b. f a -> f b -> f a)
    -> Applicative f
    <* :: SectionParser a -> SectionParser b -> SectionParser a
    $c<* :: forall a b. SectionParser a -> SectionParser b -> SectionParser a
    *> :: SectionParser a -> SectionParser b -> SectionParser b
    $c*> :: forall a b. SectionParser a -> SectionParser b -> SectionParser b
    liftA2 :: (a -> b -> c)
    -> SectionParser a -> SectionParser b -> SectionParser c
    $cliftA2 :: forall a b c.
    (a -> b -> c)
    -> SectionParser a -> SectionParser b -> SectionParser c
    <*> :: SectionParser (a -> b) -> SectionParser a -> SectionParser b
    $c<*> :: forall a b.
    SectionParser (a -> b) -> SectionParser a -> SectionParser b
    pure :: a -> SectionParser a
    $cpure :: forall a. a -> SectionParser a
    $cp1Applicative :: Functor SectionParser
    Applicative, Applicative SectionParser
    SectionParser a
    Applicative SectionParser =>
    (forall a. SectionParser a)
    -> (forall a.
        SectionParser a -> SectionParser a -> SectionParser a)
    -> (forall a. SectionParser a -> SectionParser [a])
    -> (forall a. SectionParser a -> SectionParser [a])
    -> Alternative SectionParser
    SectionParser a -> SectionParser a -> SectionParser a
    SectionParser a -> SectionParser [a]
    SectionParser a -> SectionParser [a]
    forall a. SectionParser a
    forall a. SectionParser a -> SectionParser [a]
    forall a. SectionParser a -> SectionParser a -> SectionParser a
    forall (f :: * -> *).
    Applicative f =>
    (forall a. f a)
    -> (forall a. f a -> f a -> f a)
    -> (forall a. f a -> f [a])
    -> (forall a. f a -> f [a])
    -> Alternative f
    many :: SectionParser a -> SectionParser [a]
    $cmany :: forall a. SectionParser a -> SectionParser [a]
    some :: SectionParser a -> SectionParser [a]
    $csome :: forall a. SectionParser a -> SectionParser [a]
    <|> :: SectionParser a -> SectionParser a -> SectionParser a
    $c<|> :: forall a. SectionParser a -> SectionParser a -> SectionParser a
    empty :: SectionParser a
    $cempty :: forall a. SectionParser a
    $cp1Alternative :: Applicative SectionParser
    Alternative, Applicative SectionParser
    a -> SectionParser a
    Applicative SectionParser =>
    (forall a b.
     SectionParser a -> (a -> SectionParser b) -> SectionParser b)
    -> (forall a b.
        SectionParser a -> SectionParser b -> SectionParser b)
    -> (forall a. a -> SectionParser a)
    -> Monad SectionParser
    SectionParser a -> (a -> SectionParser b) -> SectionParser b
    SectionParser a -> SectionParser b -> SectionParser b
    forall a. a -> SectionParser a
    forall a b. SectionParser a -> SectionParser b -> SectionParser b
    forall a b.
    SectionParser a -> (a -> SectionParser b) -> SectionParser b
    forall (m :: * -> *).
    Applicative m =>
    (forall a b. m a -> (a -> m b) -> m b)
    -> (forall a b. m a -> m b -> m b)
    -> (forall a. a -> m a)
    -> Monad m
    return :: a -> SectionParser a
    $creturn :: forall a. a -> SectionParser a
    >> :: SectionParser a -> SectionParser b -> SectionParser b
    $c>> :: forall a b. SectionParser a -> SectionParser b -> SectionParser b
    >>= :: SectionParser a -> (a -> SectionParser b) -> SectionParser b
    $c>>= :: forall a b.
    SectionParser a -> (a -> SectionParser b) -> SectionParser b
    $cp1Monad :: Applicative SectionParser
    Monad)

    -- | Parse a 'Text' value as an INI file and run an 'IniParser' over it
    parseIniFile :: Text -> IniParser a -> Either String a
    parseIniFile :: Text -> IniParser a -> Either [Char] a
    parseIniFile text :: Text
    text (IniParser mote :: StParser RawIni a
    mote) = do
      RawIni
    ini <- Text -> Either [Char] RawIni
    parseRawIni Text
    text
      StParser RawIni a -> RawIni -> Either [Char] a
    forall e (m :: * -> *) a. ExceptT e m a -> m (Either e a)
    runExceptT StParser RawIni a
    mote RawIni
    ini

    -- | Find a named section in the INI file and parse it with the provided
    --   section parser, failing if the section does not exist. In order to
    --   support classic INI files with capitalized section names, section
    --   lookup is __case-insensitive__.
    --
    --   >>> parseIniFile "[ONE]\nx = hello\n" $ section "ONE" (field "x")
    --   Right "hello"
    --   >>> parseIniFile "[ONE]\nx = hello\n" $ section "TWO" (field "x")
    --   Left "No top-level section named \"TWO\""
    section :: Text -> SectionParser a -> IniParser a
    section :: Text -> SectionParser a -> IniParser a
    section name :: Text
    name (SectionParser thunk :: StParser IniSection a
    thunk) = StParser RawIni a -> IniParser a
    forall a. StParser RawIni a -> IniParser a
    IniParser (StParser RawIni a -> IniParser a)
    -> StParser RawIni a -> IniParser a
    forall a b. (a -> b) -> a -> b
    $ (RawIni -> Either [Char] a) -> StParser RawIni a
    forall e (m :: * -> *) a. m (Either e a) -> ExceptT e m a
    ExceptT ((RawIni -> Either [Char] a) -> StParser RawIni a)
    -> (RawIni -> Either [Char] a) -> StParser RawIni a
    forall a b. (a -> b) -> a -> b
    $ \(RawIni ini :: Seq (NormalizedText, IniSection)
    ini) ->
      case NormalizedText
    -> Seq (NormalizedText, IniSection) -> Maybe IniSection
    forall a. NormalizedText -> Seq (NormalizedText, a) -> Maybe a
    lkp (Text -> NormalizedText
    normalize Text
    name) Seq (NormalizedText, IniSection)
    ini of
        Nothing  -> [Char] -> Either [Char] a
    forall a b. a -> Either a b
    Left ("No top-level section named " [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ Text -> [Char]
    forall a. Show a => a -> [Char]
    show Text
    name)
        Just sec :: IniSection
    sec -> StParser IniSection a -> IniSection -> Either [Char] a
    forall e (m :: * -> *) a. ExceptT e m a -> m (Either e a)
    runExceptT StParser IniSection a
    thunk IniSection
    sec

    -- | Find multiple named sections in the INI file and parse them all
    --   with the provided section parser. In order to support classic INI
    --   files with capitalized section names, section lookup is
    --   __case-insensitive__.
    --
    --   >>> parseIniFile "[ONE]\nx = hello\n[ONE]\nx = goodbye\n" $ sections "ONE" (field "x")
    --   Right (fromList ["hello","goodbye"])
    --   >>> parseIniFile "[ONE]\nx = hello\n" $ sections "TWO" (field "x")
    --   Right (fromList [])
    sections :: Text -> SectionParser a -> IniParser (Seq a)
    sections :: Text -> SectionParser a -> IniParser (Seq a)
    sections name :: Text
    name (SectionParser thunk :: StParser IniSection a
    thunk) = StParser RawIni (Seq a) -> IniParser (Seq a)
    forall a. StParser RawIni a -> IniParser a
    IniParser (StParser RawIni (Seq a) -> IniParser (Seq a))
    -> StParser RawIni (Seq a) -> IniParser (Seq a)
    forall a b. (a -> b) -> a -> b
    $ (RawIni -> Either [Char] (Seq a)) -> StParser RawIni (Seq a)
    forall e (m :: * -> *) a. m (Either e a) -> ExceptT e m a
    ExceptT ((RawIni -> Either [Char] (Seq a)) -> StParser RawIni (Seq a))
    -> (RawIni -> Either [Char] (Seq a)) -> StParser RawIni (Seq a)
    forall a b. (a -> b) -> a -> b
    $ \(RawIni ini :: Seq (NormalizedText, IniSection)
    ini) ->
      let name' :: NormalizedText
    name' = Text -> NormalizedText
    normalize Text
    name
      in ((NormalizedText, IniSection) -> Either [Char] a)
    -> Seq (NormalizedText, IniSection) -> Either [Char] (Seq a)
    forall (t :: * -> *) (m :: * -> *) a b.
    (Traversable t, Monad m) =>
    (a -> m b) -> t a -> m (t b)
    mapM (StParser IniSection a -> IniSection -> Either [Char] a
    forall e (m :: * -> *) a. ExceptT e m a -> m (Either e a)
    runExceptT StParser IniSection a
    thunk (IniSection -> Either [Char] a)
    -> ((NormalizedText, IniSection) -> IniSection)
    -> (NormalizedText, IniSection)
    -> Either [Char] a
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (NormalizedText, IniSection) -> IniSection
    forall a b. (a, b) -> b
    snd)
              (((NormalizedText, IniSection) -> Bool)
    -> Seq (NormalizedText, IniSection)
    -> Seq (NormalizedText, IniSection)
    forall a. (a -> Bool) -> Seq a -> Seq a
    Seq.filter (\ (t :: NormalizedText
    t, _) -> NormalizedText
    t NormalizedText -> NormalizedText -> Bool
    forall a. Eq a => a -> a -> Bool
    == NormalizedText
    name') Seq (NormalizedText, IniSection)
    ini)

    -- | A call to @sectionOf f@ will apply @f@ to each section name and,
    --   if @f@ produces a "Just" value, pass the extracted value in order
    --   to get the "SectionParser" to use for that section. This will
    --   find at most one section, and will produce an error if no section
    --   exists.
    --
    --   >>> parseIniFile "[FOO]\nx = hello\n" $ sectionOf (T.stripSuffix "OO") (\ l -> fmap ((,) l) (field "x"))
    --   Right ("F","hello")
    --   >>> parseIniFile "[BAR]\nx = hello\n" $ sectionOf (T.stripSuffix "OO") (\ l -> fmap ((,) l) (field "x"))
    --   Left "No matching top-level section"
    sectionOf :: (Text -> Maybe b) -> (b -> SectionParser a) -> IniParser a
    sectionOf :: (Text -> Maybe b) -> (b -> SectionParser a) -> IniParser a
    sectionOf fn :: Text -> Maybe b
    fn sectionParser :: b -> SectionParser a
    sectionParser = StParser RawIni a -> IniParser a
    forall a. StParser RawIni a -> IniParser a
    IniParser (StParser RawIni a -> IniParser a)
    -> StParser RawIni a -> IniParser a
    forall a b. (a -> b) -> a -> b
    $ (RawIni -> Either [Char] a) -> StParser RawIni a
    forall e (m :: * -> *) a. m (Either e a) -> ExceptT e m a
    ExceptT ((RawIni -> Either [Char] a) -> StParser RawIni a)
    -> (RawIni -> Either [Char] a) -> StParser RawIni a
    forall a b. (a -> b) -> a -> b
    $ \(RawIni ini :: Seq (NormalizedText, IniSection)
    ini) ->
      let go :: ViewL (NormalizedText, IniSection) -> Either [Char] a
    go Seq.EmptyL = [Char] -> Either [Char] a
    forall a b. a -> Either a b
    Left "No matching top-level section"
          go ((t :: NormalizedText
    t, sec :: IniSection
    sec) Seq.:< rs :: Seq (NormalizedText, IniSection)
    rs)
            | Just v :: b
    v <- Text -> Maybe b
    fn (NormalizedText -> Text
    actualText NormalizedText
    t) =
                let SectionParser thunk :: StParser IniSection a
    thunk = b -> SectionParser a
    sectionParser b
    v
                in StParser IniSection a -> IniSection -> Either [Char] a
    forall e (m :: * -> *) a. ExceptT e m a -> m (Either e a)
    runExceptT StParser IniSection a
    thunk IniSection
    sec
            | Bool
    otherwise = ViewL (NormalizedText, IniSection) -> Either [Char] a
    go (Seq (NormalizedText, IniSection)
    -> ViewL (NormalizedText, IniSection)
    forall a. Seq a -> ViewL a
    Seq.viewl Seq (NormalizedText, IniSection)
    rs)
      in ViewL (NormalizedText, IniSection) -> Either [Char] a
    go (Seq (NormalizedText, IniSection)
    -> ViewL (NormalizedText, IniSection)
    forall a. Seq a -> ViewL a
    Seq.viewl Seq (NormalizedText, IniSection)
    ini)

    -- | A call to @sectionsOf f@ will apply @f@ to each section name and,
    --   if @f@ produces a @Just@ value, pass the extracted value in order
    --   to get the "SectionParser" to use for that section. This will
    --   return every section for which the call to @f@ produces a "Just"
    --   value.
    --
    --   >>> parseIniFile "[FOO]\nx = hello\n[BOO]\nx = goodbye\n" $ sectionsOf (T.stripSuffix "OO") (\ l -> fmap ((,) l) (field "x"))
    --   Right (fromList [("F","hello"),("B","goodbye")])
    --   >>> parseIniFile "[BAR]\nx = hello\n" $ sectionsOf (T.stripSuffix "OO") (\ l -> fmap ((,) l) (field "x"))
    --   Right (fromList [])
    sectionsOf :: (Text -> Maybe b) -> (b -> SectionParser a) -> IniParser (Seq a)
    sectionsOf :: (Text -> Maybe b) -> (b -> SectionParser a) -> IniParser (Seq a)
    sectionsOf fn :: Text -> Maybe b
    fn sectionParser :: b -> SectionParser a
    sectionParser = StParser RawIni (Seq a) -> IniParser (Seq a)
    forall a. StParser RawIni a -> IniParser a
    IniParser (StParser RawIni (Seq a) -> IniParser (Seq a))
    -> StParser RawIni (Seq a) -> IniParser (Seq a)
    forall a b. (a -> b) -> a -> b
    $ (RawIni -> Either [Char] (Seq a)) -> StParser RawIni (Seq a)
    forall e (m :: * -> *) a. m (Either e a) -> ExceptT e m a
    ExceptT ((RawIni -> Either [Char] (Seq a)) -> StParser RawIni (Seq a))
    -> (RawIni -> Either [Char] (Seq a)) -> StParser RawIni (Seq a)
    forall a b. (a -> b) -> a -> b
    $ \(RawIni ini :: Seq (NormalizedText, IniSection)
    ini) ->
      let go :: ViewL (NormalizedText, IniSection) -> Either [Char] (Seq a)
    go Seq.EmptyL = Seq a -> Either [Char] (Seq a)
    forall (m :: * -> *) a. Monad m => a -> m a
    return Seq a
    forall a. Seq a
    Seq.empty
          go ((t :: NormalizedText
    t, sec :: IniSection
    sec) Seq.:< rs :: Seq (NormalizedText, IniSection)
    rs)
            | Just v :: b
    v <- Text -> Maybe b
    fn (NormalizedText -> Text
    actualText NormalizedText
    t) =
                let SectionParser thunk :: StParser IniSection a
    thunk = b -> SectionParser a
    sectionParser b
    v
                in do
                  a
    x <- StParser IniSection a -> IniSection -> Either [Char] a
    forall e (m :: * -> *) a. ExceptT e m a -> m (Either e a)
    runExceptT StParser IniSection a
    thunk IniSection
    sec
                  Seq a
    xs <- ViewL (NormalizedText, IniSection) -> Either [Char] (Seq a)
    go (Seq (NormalizedText, IniSection)
    -> ViewL (NormalizedText, IniSection)
    forall a. Seq a -> ViewL a
    Seq.viewl Seq (NormalizedText, IniSection)
    rs)
                  Seq a -> Either [Char] (Seq a)
    forall (m :: * -> *) a. Monad m => a -> m a
    return (a
    x a -> Seq a -> Seq a
    forall a. a -> Seq a -> Seq a
    Seq.<| Seq a
    xs)
            | Bool
    otherwise = ViewL (NormalizedText, IniSection) -> Either [Char] (Seq a)
    go (Seq (NormalizedText, IniSection)
    -> ViewL (NormalizedText, IniSection)
    forall a. Seq a -> ViewL a
    Seq.viewl Seq (NormalizedText, IniSection)
    rs)
      in ViewL (NormalizedText, IniSection) -> Either [Char] (Seq a)
    go (Seq (NormalizedText, IniSection)
    -> ViewL (NormalizedText, IniSection)
    forall a. Seq a -> ViewL a
    Seq.viewl Seq (NormalizedText, IniSection)
    ini)

    -- | Find a named section in the INI file and parse it with the provided
    --   section parser, returning 'Nothing' if the section does not exist.
    --   In order to
    --   support classic INI files with capitalized section names, section
    --   lookup is __case-insensitive__.
    --
    --   >>> parseIniFile "[ONE]\nx = hello\n" $ sectionMb "ONE" (field "x")
    --   Right (Just "hello")
    --   >>> parseIniFile "[ONE]\nx = hello\n" $ sectionMb "TWO" (field "x")
    --   Right Nothing
    sectionMb :: Text -> SectionParser a -> IniParser (Maybe a)
    sectionMb :: Text -> SectionParser a -> IniParser (Maybe a)
    sectionMb name :: Text
    name (SectionParser thunk :: StParser IniSection a
    thunk) = StParser RawIni (Maybe a) -> IniParser (Maybe a)
    forall a. StParser RawIni a -> IniParser a
    IniParser (StParser RawIni (Maybe a) -> IniParser (Maybe a))
    -> StParser RawIni (Maybe a) -> IniParser (Maybe a)
    forall a b. (a -> b) -> a -> b
    $ (RawIni -> Either [Char] (Maybe a)) -> StParser RawIni (Maybe a)
    forall e (m :: * -> *) a. m (Either e a) -> ExceptT e m a
    ExceptT ((RawIni -> Either [Char] (Maybe a)) -> StParser RawIni (Maybe a))
    -> (RawIni -> Either [Char] (Maybe a)) -> StParser RawIni (Maybe a)
    forall a b. (a -> b) -> a -> b
    $ \(RawIni ini :: Seq (NormalizedText, IniSection)
    ini) ->
      case NormalizedText
    -> Seq (NormalizedText, IniSection) -> Maybe IniSection
    forall a. NormalizedText -> Seq (NormalizedText, a) -> Maybe a
    lkp (Text -> NormalizedText
    normalize Text
    name) Seq (NormalizedText, IniSection)
    ini of
        Nothing  -> Maybe a -> Either [Char] (Maybe a)
    forall (m :: * -> *) a. Monad m => a -> m a
    return Maybe a
    forall a. Maybe a
    Nothing
        Just sec :: IniSection
    sec -> a -> Maybe a
    forall a. a -> Maybe a
    Just (a -> Maybe a) -> Either [Char] a -> Either [Char] (Maybe a)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    `fmap` StParser IniSection a -> IniSection -> Either [Char] a
    forall e (m :: * -> *) a. ExceptT e m a -> m (Either e a)
    runExceptT StParser IniSection a
    thunk IniSection
    sec

    -- | Find a named section in the INI file and parse it with the provided
    --   section parser, returning a default value if the section does not exist.
    --   In order to
    --   support classic INI files with capitalized section names, section
    --   lookup is __case-insensitive__.
    --
    --   >>> parseIniFile "[ONE]\nx = hello\n" $ sectionDef "ONE" "def" (field "x")
    --   Right "hello"
    --   >>> parseIniFile "[ONE]\nx = hello\n" $ sectionDef "TWO" "def" (field "x")
    --   Right "def"
    sectionDef :: Text -> a -> SectionParser a -> IniParser a
    sectionDef :: Text -> a -> SectionParser a -> IniParser a
    sectionDef name :: Text
    name def :: a
    def (SectionParser thunk :: StParser IniSection a
    thunk) = StParser RawIni a -> IniParser a
    forall a. StParser RawIni a -> IniParser a
    IniParser (StParser RawIni a -> IniParser a)
    -> StParser RawIni a -> IniParser a
    forall a b. (a -> b) -> a -> b
    $ (RawIni -> Either [Char] a) -> StParser RawIni a
    forall e (m :: * -> *) a. m (Either e a) -> ExceptT e m a
    ExceptT ((RawIni -> Either [Char] a) -> StParser RawIni a)
    -> (RawIni -> Either [Char] a) -> StParser RawIni a
    forall a b. (a -> b) -> a -> b
    $ \(RawIni ini :: Seq (NormalizedText, IniSection)
    ini) ->
      case NormalizedText
    -> Seq (NormalizedText, IniSection) -> Maybe IniSection
    forall a. NormalizedText -> Seq (NormalizedText, a) -> Maybe a
    lkp (Text -> NormalizedText
    normalize Text
    name) Seq (NormalizedText, IniSection)
    ini of
        Nothing  -> a -> Either [Char] a
    forall (m :: * -> *) a. Monad m => a -> m a
    return a
    def
        Just sec :: IniSection
    sec -> StParser IniSection a -> IniSection -> Either [Char] a
    forall e (m :: * -> *) a. ExceptT e m a -> m (Either e a)
    runExceptT StParser IniSection a
    thunk IniSection
    sec

    ---

    throw :: String -> StParser s a
    throw :: [Char] -> StParser s a
    throw msg :: [Char]
    msg = (s -> Either [Char] a) -> StParser s a
    forall e (m :: * -> *) a. m (Either e a) -> ExceptT e m a
    ExceptT ((s -> Either [Char] a) -> StParser s a)
    -> (s -> Either [Char] a) -> StParser s a
    forall a b. (a -> b) -> a -> b
    $ (\ _ -> [Char] -> Either [Char] a
    forall a b. a -> Either a b
    Left [Char]
    msg)

    getSectionName :: StParser IniSection Text
    getSectionName :: StParser IniSection Text
    getSectionName = (IniSection -> Either [Char] Text) -> StParser IniSection Text
    forall e (m :: * -> *) a. m (Either e a) -> ExceptT e m a
    ExceptT ((IniSection -> Either [Char] Text) -> StParser IniSection Text)
    -> (IniSection -> Either [Char] Text) -> StParser IniSection Text
    forall a b. (a -> b) -> a -> b
    $ (\ m :: IniSection
    m -> Text -> Either [Char] Text
    forall (m :: * -> *) a. Monad m => a -> m a
    return (IniSection -> Text
    isName IniSection
    m))

    rawFieldMb :: Text -> StParser IniSection (Maybe IniValue)
    rawFieldMb :: Text -> StParser IniSection (Maybe IniValue)
    rawFieldMb name :: Text
    name = (IniSection -> Either [Char] (Maybe IniValue))
    -> StParser IniSection (Maybe IniValue)
    forall e (m :: * -> *) a. m (Either e a) -> ExceptT e m a
    ExceptT ((IniSection -> Either [Char] (Maybe IniValue))
     -> StParser IniSection (Maybe IniValue))
    -> (IniSection -> Either [Char] (Maybe IniValue))
    -> StParser IniSection (Maybe IniValue)
    forall a b. (a -> b) -> a -> b
    $ \m :: IniSection
    m ->
      Maybe IniValue -> Either [Char] (Maybe IniValue)
    forall (m :: * -> *) a. Monad m => a -> m a
    return (NormalizedText -> Seq (NormalizedText, IniValue) -> Maybe IniValue
    forall a. NormalizedText -> Seq (NormalizedText, a) -> Maybe a
    lkp (Text -> NormalizedText
    normalize Text
    name) (IniSection -> Seq (NormalizedText, IniValue)
    isVals IniSection
    m))

    rawField :: Text -> StParser IniSection IniValue
    rawField :: Text -> StParser IniSection IniValue
    rawField name :: Text
    name = do
      Text
    sec   <- StParser IniSection Text
    getSectionName
      Maybe IniValue
    valMb <- Text -> StParser IniSection (Maybe IniValue)
    rawFieldMb Text
    name
      case Maybe IniValue
    valMb of
        Nothing -> [Char] -> StParser IniSection IniValue
    forall s a. [Char] -> StParser s a
    throw ("Missing field " [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ Text -> [Char]
    forall a. Show a => a -> [Char]
    show Text
    name [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++
                          " in section " [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ Text -> [Char]
    forall a. Show a => a -> [Char]
    show Text
    sec)
        Just x :: IniValue
    x  -> IniValue -> StParser IniSection IniValue
    forall (m :: * -> *) a. Monad m => a -> m a
    return IniValue
    x

    getVal :: IniValue -> Text
    getVal :: IniValue -> Text
    getVal = Text -> Text
    T.strip (Text -> Text) -> (IniValue -> Text) -> IniValue -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . IniValue -> Text
    vValue

    -- | Retrieve a field, failing if it doesn't exist, and return its raw value.
    --
    --   >>> parseIniFile "[MAIN]\nx = hello\n" $ section "MAIN" (field "x")
    --   Right "hello"
    --   >>> parseIniFile "[MAIN]\nx = hello\n" $ section "MAIN" (field "y")
    --   Left "Missing field \"y\" in section \"MAIN\""
    field :: Text -> SectionParser Text
    field :: Text -> SectionParser Text
    field name :: Text
    name = StParser IniSection Text -> SectionParser Text
    forall a. StParser IniSection a -> SectionParser a
    SectionParser (StParser IniSection Text -> SectionParser Text)
    -> StParser IniSection Text -> SectionParser Text
    forall a b. (a -> b) -> a -> b
    $ IniValue -> Text
    getVal (IniValue -> Text)
    -> StParser IniSection IniValue -> StParser IniSection Text
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    `fmap` Text -> StParser IniSection IniValue
    rawField Text
    name

    -- | Retrieve a field and use the supplied parser to parse it as a value,
    --   failing if the field does not exist, or if the parser fails to
    --   produce a value.
    --
    --   >>> parseIniFile "[MAIN]\nx = 72\n" $ section "MAIN" (fieldOf "x" number)
    --   Right 72
    --   >>> parseIniFile "[MAIN]\nx = hello\n" $ section "MAIN" (fieldOf "x" number)
    --   Left "Line 2, in section \"MAIN\": Unable to parse \"hello\" as a value of type Integer"
    --   >>> parseIniFile "[MAIN]\nx = 72\n" $ section "MAIN" (fieldOf "y" number)
    --   Left "Missing field \"y\" in section \"MAIN\""
    fieldOf :: Text -> (Text -> Either String a) -> SectionParser a
    fieldOf :: Text -> (Text -> Either [Char] a) -> SectionParser a
    fieldOf name :: Text
    name parse :: Text -> Either [Char] a
    parse = StParser IniSection a -> SectionParser a
    forall a. StParser IniSection a -> SectionParser a
    SectionParser (StParser IniSection a -> SectionParser a)
    -> StParser IniSection a -> SectionParser a
    forall a b. (a -> b) -> a -> b
    $ do
      Text
    sec <- StParser IniSection Text
    getSectionName
      IniValue
    val <- Text -> StParser IniSection IniValue
    rawField Text
    name
      case Text -> Either [Char] a
    parse (IniValue -> Text
    getVal IniValue
    val) of
        Left err :: [Char]
    err -> Int -> Text -> StParser IniSection a -> StParser IniSection a
    forall s a. Int -> Text -> StParser s a -> StParser s a
    addLineInformation (IniValue -> Int
    vLineNo IniValue
    val) Text
    sec ([Char] -> StParser IniSection a
    forall s a. [Char] -> StParser s a
    throw [Char]
    err)
        Right x :: a
    x  -> a -> StParser IniSection a
    forall (m :: * -> *) a. Monad m => a -> m a
    return a
    x

    -- | Retrieve a field, returning a @Nothing@ value if it does not exist.
    --
    --   >>> parseIniFile "[MAIN]\nx = hello\n" $ section "MAIN" (fieldMb "x")
    --   Right (Just "hello")
    --   >>> parseIniFile "[MAIN]\nx = hello\n" $ section "MAIN" (fieldMb "y")
    --   Right Nothing
    fieldMb :: Text -> SectionParser (Maybe Text)
    fieldMb :: Text -> SectionParser (Maybe Text)
    fieldMb name :: Text
    name = StParser IniSection (Maybe Text) -> SectionParser (Maybe Text)
    forall a. StParser IniSection a -> SectionParser a
    SectionParser (StParser IniSection (Maybe Text) -> SectionParser (Maybe Text))
    -> StParser IniSection (Maybe Text) -> SectionParser (Maybe Text)
    forall a b. (a -> b) -> a -> b
    $ (IniValue -> Text) -> Maybe IniValue -> Maybe Text
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap IniValue -> Text
    getVal (Maybe IniValue -> Maybe Text)
    -> StParser IniSection (Maybe IniValue)
    -> StParser IniSection (Maybe Text)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    `fmap` Text -> StParser IniSection (Maybe IniValue)
    rawFieldMb Text
    name

    -- | Retrieve a field and parse it according to the given parser, returning
    --   @Nothing@ if it does not exist. If the parser fails, then this will
    --   fail.
    --
    --   >>> parseIniFile "[MAIN]\nx = 72\n" $ section "MAIN" (fieldMbOf "x" number)
    --   Right (Just 72)
    --   >>> parseIniFile "[MAIN]\nx = hello\n" $ section "MAIN" (fieldMbOf "x" number)
    --   Left "Line 2, in section \"MAIN\": Unable to parse \"hello\" as a value of type Integer"
    --   >>> parseIniFile "[MAIN]\nx = 72\n" $ section "MAIN" (fieldMbOf "y" number)
    --   Right Nothing
    fieldMbOf :: Text -> (Text -> Either String a) -> SectionParser (Maybe a)
    fieldMbOf :: Text -> (Text -> Either [Char] a) -> SectionParser (Maybe a)
    fieldMbOf name :: Text
    name parse :: Text -> Either [Char] a
    parse = StParser IniSection (Maybe a) -> SectionParser (Maybe a)
    forall a. StParser IniSection a -> SectionParser a
    SectionParser (StParser IniSection (Maybe a) -> SectionParser (Maybe a))
    -> StParser IniSection (Maybe a) -> SectionParser (Maybe a)
    forall a b. (a -> b) -> a -> b
    $ do
      Text
    sec <- StParser IniSection Text
    getSectionName
      Maybe IniValue
    mb <- Text -> StParser IniSection (Maybe IniValue)
    rawFieldMb Text
    name
      case Maybe IniValue
    mb of
        Nothing  -> Maybe a -> StParser IniSection (Maybe a)
    forall (m :: * -> *) a. Monad m => a -> m a
    return Maybe a
    forall a. Maybe a
    Nothing
        Just v :: IniValue
    v -> case Text -> Either [Char] a
    parse (IniValue -> Text
    getVal IniValue
    v) of
          Left err :: [Char]
    err -> Int
    -> Text
    -> StParser IniSection (Maybe a)
    -> StParser IniSection (Maybe a)
    forall s a. Int -> Text -> StParser s a -> StParser s a
    addLineInformation (IniValue -> Int
    vLineNo IniValue
    v) Text
    sec ([Char] -> StParser IniSection (Maybe a)
    forall s a. [Char] -> StParser s a
    throw [Char]
    err)
          Right x :: a
    x  -> Maybe a -> StParser IniSection (Maybe a)
    forall (m :: * -> *) a. Monad m => a -> m a
    return (a -> Maybe a
    forall a. a -> Maybe a
    Just a
    x)

    -- | Retrieve a field and supply a default value for if it doesn't exist.
    --
    --   >>> parseIniFile "[MAIN]\nx = hello\n" $ section "MAIN" (fieldDef "x" "def")
    --   Right "hello"
    --   >>> parseIniFile "[MAIN]\nx = hello\n" $ section "MAIN" (fieldDef "y" "def")
    --   Right "def"
    fieldDef :: Text -> Text -> SectionParser Text
    fieldDef :: Text -> Text -> SectionParser Text
    fieldDef name :: Text
    name def :: Text
    def = StParser IniSection Text -> SectionParser Text
    forall a. StParser IniSection a -> SectionParser a
    SectionParser (StParser IniSection Text -> SectionParser Text)
    -> StParser IniSection Text -> SectionParser Text
    forall a b. (a -> b) -> a -> b
    $ (IniSection -> Either [Char] Text) -> StParser IniSection Text
    forall e (m :: * -> *) a. m (Either e a) -> ExceptT e m a
    ExceptT ((IniSection -> Either [Char] Text) -> StParser IniSection Text)
    -> (IniSection -> Either [Char] Text) -> StParser IniSection Text
    forall a b. (a -> b) -> a -> b
    $ \m :: IniSection
    m ->
      case NormalizedText -> Seq (NormalizedText, IniValue) -> Maybe IniValue
    forall a. NormalizedText -> Seq (NormalizedText, a) -> Maybe a
    lkp (Text -> NormalizedText
    normalize Text
    name) (IniSection -> Seq (NormalizedText, IniValue)
    isVals IniSection
    m) of
        Nothing -> Text -> Either [Char] Text
    forall (m :: * -> *) a. Monad m => a -> m a
    return Text
    def
        Just x :: IniValue
    x  -> Text -> Either [Char] Text
    forall (m :: * -> *) a. Monad m => a -> m a
    return (IniValue -> Text
    getVal IniValue
    x)

    -- | Retrieve a field, parsing it according to the given parser, and returning
    --   a default value if it does not exist. If the parser fails, then this will
    --   fail.
    --
    --   >>> parseIniFile "[MAIN]\nx = 72\n" $ section "MAIN" (fieldDefOf "x" number 99)
    --   Right 72
    --   >>> parseIniFile "[MAIN]\nx = hello\n" $ section "MAIN" (fieldDefOf "x" number 99)
    --   Left "Line 2, in section \"MAIN\": Unable to parse \"hello\" as a value of type Integer"
    --   >>> parseIniFile "[MAIN]\nx = 72\n" $ section "MAIN" (fieldDefOf "y" number 99)
    --   Right 99
    fieldDefOf :: Text -> (Text -> Either String a) -> a -> SectionParser a
    fieldDefOf :: Text -> (Text -> Either [Char] a) -> a -> SectionParser a
    fieldDefOf name :: Text
    name parse :: Text -> Either [Char] a
    parse def :: a
    def = StParser IniSection a -> SectionParser a
    forall a. StParser IniSection a -> SectionParser a
    SectionParser (StParser IniSection a -> SectionParser a)
    -> StParser IniSection a -> SectionParser a
    forall a b. (a -> b) -> a -> b
    $ do
      Text
    sec <- StParser IniSection Text
    getSectionName
      Maybe IniValue
    mb <- Text -> StParser IniSection (Maybe IniValue)
    rawFieldMb Text
    name
      case Maybe IniValue
    mb of
        Nothing  -> a -> StParser IniSection a
    forall (m :: * -> *) a. Monad m => a -> m a
    return a
    def
        Just v :: IniValue
    v -> case Text -> Either [Char] a
    parse (IniValue -> Text
    getVal IniValue
    v) of
          Left err :: [Char]
    err -> Int -> Text -> StParser IniSection a -> StParser IniSection a
    forall s a. Int -> Text -> StParser s a -> StParser s a
    addLineInformation (IniValue -> Int
    vLineNo IniValue
    v) Text
    sec ([Char] -> StParser IniSection a
    forall s a. [Char] -> StParser s a
    throw [Char]
    err)
          Right x :: a
    x  -> a -> StParser IniSection a
    forall (m :: * -> *) a. Monad m => a -> m a
    return a
    x

    -- | Retrieve a field and treat it as a boolean, failing if it
    --   does not exist.
    --
    --   >>> parseIniFile "[MAIN]\nx = yes\n" $ section "MAIN" (fieldFlag "x")
    --   Right True
    --   >>> parseIniFile "[MAIN]\nx = yes\n" $ section "MAIN" (fieldFlag "y")
    --   Left "Missing field \"y\" in section \"MAIN\""
    fieldFlag :: Text -> SectionParser Bool
    fieldFlag :: Text -> SectionParser Bool
    fieldFlag name :: Text
    name = Text -> (Text -> Either [Char] Bool) -> SectionParser Bool
    forall a. Text -> (Text -> Either [Char] a) -> SectionParser a
    fieldOf Text
    name Text -> Either [Char] Bool
    flag

    -- | Retrieve a field and treat it as a boolean, subsituting
    --   a default value if it doesn't exist.
    --
    --   >>> parseIniFile "[MAIN]\nx = yes\n" $ section "MAIN" (fieldFlagDef "x" False)
    --   Right True
    --   >>> parseIniFile "[MAIN]\nx = hello\n" $ section "MAIN" (fieldFlagDef "x" False)
    --   Left "Line 2, in section \"MAIN\": Unable to parse \"hello\" as a boolean"
    --   >>> parseIniFile "[MAIN]\nx = yes\n" $ section "MAIN" (fieldFlagDef "y" False)
    --   Right False
    fieldFlagDef :: Text -> Bool -> SectionParser Bool
    fieldFlagDef :: Text -> Bool -> SectionParser Bool
    fieldFlagDef name :: Text
    name def :: Bool
    def = Text -> (Text -> Either [Char] Bool) -> Bool -> SectionParser Bool
    forall a. Text -> (Text -> Either [Char] a) -> a -> SectionParser a
    fieldDefOf Text
    name Text -> Either [Char] Bool
    flag Bool
    def

    ---

    -- | Try to use the "Read" instance for a type to parse a value, failing
    --   with a human-readable error message if reading fails.
    --
    --   >>> readable "(5, 7)" :: Either String (Int, Int)
    --   Right (5,7)
    --   >>> readable "hello" :: Either String (Int, Int)
    --   Left "Unable to parse \"hello\" as a value of type (Int,Int)"
    readable :: forall a. (Read a, Typeable a) => Text -> Either String a
    readable :: Text -> Either [Char] a
    readable t :: Text
    t = case [Char] -> Maybe a
    forall a. Read a => [Char] -> Maybe a
    readMaybe [Char]
    str of
      Just v :: a
    v  -> a -> Either [Char] a
    forall a b. b -> Either a b
    Right a
    v
      Nothing -> [Char] -> Either [Char] a
    forall a b. a -> Either a b
    Left ("Unable to parse " [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ [Char] -> [Char]
    forall a. Show a => a -> [Char]
    show [Char]
    str [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++
                       " as a value of type " [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ TypeRep -> [Char]
    forall a. Show a => a -> [Char]
    show TypeRep
    typ)
      where str :: [Char]
    str = Text -> [Char]
    T.unpack Text
    t
            typ :: TypeRep
    typ = Proxy a -> TypeRep
    forall k (proxy :: k -> *) (a :: k).
    Typeable a =>
    proxy a -> TypeRep
    typeRep Proxy a
    prx
            prx :: Proxy a
            prx :: Proxy a
    prx = Proxy a
    forall k (t :: k). Proxy t
    Proxy

    -- | Try to use the "Read" instance for a numeric type to parse a value,
    --   failing with a human-readable error message if reading fails.
    --
    --   >>> number "5" :: Either String Int
    --   Right 5
    --   >>> number "hello" :: Either String Int
    --   Left "Unable to parse \"hello\" as a value of type Int"
    number :: (Num a, Read a, Typeable a) => Text -> Either String a
    number :: Text -> Either [Char] a
    number = Text -> Either [Char] a
    forall a. (Read a, Typeable a) => Text -> Either [Char] a
    readable

    -- | Convert a textual value to the appropriate string type. This will
    --   never fail.
    --
    --   >>> string "foo" :: Either String String
    --   Right "foo"
    string :: (IsString a) => Text -> Either String a
    string :: Text -> Either [Char] a
    string = a -> Either [Char] a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (a -> Either [Char] a) -> (Text -> a) -> Text -> Either [Char] a
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [Char] -> a
    forall a. IsString a => [Char] -> a
    fromString ([Char] -> a) -> (Text -> [Char]) -> Text -> a
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Text -> [Char]
    T.unpack

    -- | Convert a string that represents a boolean to a proper boolean. This
    --   is case-insensitive, and matches the words @true@, @false@, @yes@,
    --   @no@, as well as single-letter abbreviations for all of the above.
    --   If the input does not match, then this will fail with a human-readable
    --   error message.
    --
    --   >>> flag "TRUE"
    --   Right True
    --   >>> flag "y"
    --   Right True
    --   >>> flag "no"
    --   Right False
    --   >>> flag "F"
    --   Right False
    --   >>> flag "That's a secret!"
    --   Left "Unable to parse \"That's a secret!\" as a boolean"
    flag :: Text -> Either String Bool
    flag :: Text -> Either [Char] Bool
    flag s :: Text
    s = case Text -> Text
    T.toLower Text
    s of
      "true"  -> Bool -> Either [Char] Bool
    forall a b. b -> Either a b
    Right Bool
    True
      "yes"   -> Bool -> Either [Char] Bool
    forall a b. b -> Either a b
    Right Bool
    True
      "t"     -> Bool -> Either [Char] Bool
    forall a b. b -> Either a b
    Right Bool
    True
      "y"     -> Bool -> Either [Char] Bool
    forall a b. b -> Either a b
    Right Bool
    True
      "false" -> Bool -> Either [Char] Bool
    forall a b. b -> Either a b
    Right Bool
    False
      "no"    -> Bool -> Either [Char] Bool
    forall a b. b -> Either a b
    Right Bool
    False
      "f"     -> Bool -> Either [Char] Bool
    forall a b. b -> Either a b
    Right Bool
    False
      "n"     -> Bool -> Either [Char] Bool
    forall a b. b -> Either a b
    Right Bool
    False
      _       -> [Char] -> Either [Char] Bool
    forall a b. a -> Either a b
    Left ("Unable to parse " [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ Text -> [Char]
    forall a. Show a => a -> [Char]
    show Text
    s [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ " as a boolean")

    -- | Convert a reader for a value into a reader for a list of those
    --   values, separated by a chosen separator. This will split apart
    --   the string on that separator, get rid of leading and trailing
    --   whitespace on the individual chunks, and then attempt to parse
    --   each of them according to the function provided, turning the
    --   result into a list.
    --
    --   This is overloaded with the "IsList" typeclass, so it can be
    --   used transparently to parse other list-like types.
    --
    --   >>> listWithSeparator "," number "2, 3, 4" :: Either String [Int]
    --   Right [2,3,4]
    --   >>> listWithSeparator " " number "7 8 9" :: Either String [Int]
    --   Right [7,8,9]
    --   >>> listWithSeparator ":" string "/bin:/usr/bin" :: Either String [FilePath]
    --   Right ["/bin","/usr/bin"]
    --   >>> listWithSeparator "," number "7 8 9" :: Either String [Int]
    --   Left "Unable to parse \"7 8 9\" as a value of type Int"
    listWithSeparator :: (IsList l)
                      => Text
                      -> (Text -> Either String (Item l))
                      -> Text -> Either String l
    listWithSeparator :: Text -> (Text -> Either [Char] (Item l)) -> Text -> Either [Char] l
    listWithSeparator sep :: Text
    sep rd :: Text -> Either [Char] (Item l)
    rd =
      ([Item l] -> l) -> Either [Char] [Item l] -> Either [Char] l
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap [Item l] -> l
    forall l. IsList l => [Item l] -> l
    fromList (Either [Char] [Item l] -> Either [Char] l)
    -> (Text -> Either [Char] [Item l]) -> Text -> Either [Char] l
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Text -> Either [Char] (Item l))
    -> [Text] -> Either [Char] [Item l]
    forall (t :: * -> *) (m :: * -> *) a b.
    (Traversable t, Monad m) =>
    (a -> m b) -> t a -> m (t b)
    mapM (Text -> Either [Char] (Item l)
    rd (Text -> Either [Char] (Item l))
    -> (Text -> Text) -> Text -> Either [Char] (Item l)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Text -> Text
    T.strip) ([Text] -> Either [Char] [Item l])
    -> (Text -> [Text]) -> Text -> Either [Char] [Item l]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Text -> Text -> [Text]
    T.splitOn Text
    sep

    -- $setup
    --
    -- >>> :{
    -- data NetworkConfig = NetworkConfig
    --    { netHost :: String, netPort :: Int }
    --     deriving (Eq, Show)
    -- >>> :}
    --
    -- >>> :{
    -- data LocalConfig = LocalConfig
    --   { localUser :: Text }
    --     deriving (Eq, Show)
    -- >>> :}
    --
    -- >>> :{
    -- data Config = Config
    --   { cfNetwork :: NetworkConfig, cfLocal :: Maybe LocalConfig }
    --     deriving (Eq, Show)
    -- >>> :}
    --
    -- >>> :{
    -- let configParser = do
    --       netCf <- section "NETWORK" $ do
    --         host <- fieldOf "host" string
    --         port <- fieldOf "port" number
    --         return NetworkConfig { netHost = host, netPort = port }
    --       locCf <- sectionMb "LOCAL" $
    --         LocalConfig <$> field "user"
    --       return Config { cfNetwork = netCf, cfLocal = locCf }
    -- >>> :}
    --
    -- >>> :{
    --    let example = "[NETWORK]\nhost = example.com\nport = 7878\n\n# here is a comment\n[LOCAL]\nuser = terry\n"
    -- >>> :}