{- |

This module provides an INI schema validator that is able to track unused
sections and fields in order to report warning messages to the user.


 -}
module Matterhorn.Config.Schema
  ( IniParser
  , parseIniFile
  , (<!>)

  , Fatal(..)
  , fatalString

  , Warning(..)
  , warningString

  , section
  , sectionMb
  , fieldMbOf
  , fieldMb
  , field
  , fieldDefOf
  , fieldFlagDef

  -- * Re-exports
  , number
  , string
  , listWithSeparator
  ) where

import           Prelude ()
import           Matterhorn.Prelude

import           Data.Map (Map)
import qualified Data.Map as Map
import           Data.List.NonEmpty (NonEmpty)
import qualified Data.List.NonEmpty as NonEmpty
import qualified Data.Text as T
import           Control.Monad
import           Data.Ini.Config (flag, number, listWithSeparator, string)
import           Data.Ini.Config.Raw


------------------------------------------------------------------------

newtype Parser e t a = Parser { Parser e t a
-> e
-> Map NormalizedText (NonEmpty t)
-> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], a)
unParser :: e -> Map NormalizedText (NonEmpty t) -> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], a) }

instance Functor (Parser e t) where
  fmap :: (a -> b) -> Parser e t a -> Parser e t b
fmap = (a -> b) -> Parser e t a -> Parser e t b
forall (m :: * -> *) a1 r. Monad m => (a1 -> r) -> m a1 -> m r
liftM

instance Applicative (Parser e t) where
  pure :: a -> Parser e t a
pure a
x = (e
 -> Map NormalizedText (NonEmpty t)
 -> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], a))
-> Parser e t a
forall e t a.
(e
 -> Map NormalizedText (NonEmpty t)
 -> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], a))
-> Parser e t a
Parser ((e
  -> Map NormalizedText (NonEmpty t)
  -> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], a))
 -> Parser e t a)
-> (e
    -> Map NormalizedText (NonEmpty t)
    -> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], a))
-> Parser e t a
forall a b. (a -> b) -> a -> b
$ \e
_ Map NormalizedText (NonEmpty t)
s -> (Map NormalizedText (NonEmpty t), [Warning], a)
-> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], a)
forall a b. b -> Either a b
Right (Map NormalizedText (NonEmpty t)
s, [], a
x)
  <*> :: Parser e t (a -> b) -> Parser e t a -> Parser e t b
(<*>) = Parser e t (a -> b) -> Parser e t a -> Parser e t b
forall (m :: * -> *) a b. Monad m => m (a -> b) -> m a -> m b
ap

instance Monad (Parser e t) where
  Parser e t a
m >>= :: Parser e t a -> (a -> Parser e t b) -> Parser e t b
>>= a -> Parser e t b
f = (e
 -> Map NormalizedText (NonEmpty t)
 -> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], b))
-> Parser e t b
forall e t a.
(e
 -> Map NormalizedText (NonEmpty t)
 -> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], a))
-> Parser e t a
Parser ((e
  -> Map NormalizedText (NonEmpty t)
  -> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], b))
 -> Parser e t b)
-> (e
    -> Map NormalizedText (NonEmpty t)
    -> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], b))
-> Parser e t b
forall a b. (a -> b) -> a -> b
$ \e
e Map NormalizedText (NonEmpty t)
s0 ->
              do (Map NormalizedText (NonEmpty t)
s1, [Warning]
ws1, a
x1) <- Parser e t a
-> e
-> Map NormalizedText (NonEmpty t)
-> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], a)
forall e t a.
Parser e t a
-> e
-> Map NormalizedText (NonEmpty t)
-> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], a)
unParser Parser e t a
m e
e Map NormalizedText (NonEmpty t)
s0
                 (Map NormalizedText (NonEmpty t)
s2, [Warning]
ws2, b
x2) <- Parser e t b
-> e
-> Map NormalizedText (NonEmpty t)
-> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], b)
forall e t a.
Parser e t a
-> e
-> Map NormalizedText (NonEmpty t)
-> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], a)
unParser (a -> Parser e t b
f a
x1) e
e Map NormalizedText (NonEmpty t)
s1
                 (Map NormalizedText (NonEmpty t), [Warning], b)
-> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], b)
forall a b. b -> Either a b
Right (Map NormalizedText (NonEmpty t)
s2, [Warning]
ws1[Warning] -> [Warning] -> [Warning]
forall a. [a] -> [a] -> [a]
++[Warning]
ws2, b
x2)



(<!>) :: Parser e t a -> Parser e t a -> Parser e t a
Parser e t a
p <!> :: Parser e t a -> Parser e t a -> Parser e t a
<!> Parser e t a
q = (e
 -> Map NormalizedText (NonEmpty t)
 -> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], a))
-> Parser e t a
forall e t a.
(e
 -> Map NormalizedText (NonEmpty t)
 -> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], a))
-> Parser e t a
Parser ((e
  -> Map NormalizedText (NonEmpty t)
  -> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], a))
 -> Parser e t a)
-> (e
    -> Map NormalizedText (NonEmpty t)
    -> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], a))
-> Parser e t a
forall a b. (a -> b) -> a -> b
$ \e
e Map NormalizedText (NonEmpty t)
s ->
  case Parser e t a
-> e
-> Map NormalizedText (NonEmpty t)
-> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], a)
forall e t a.
Parser e t a
-> e
-> Map NormalizedText (NonEmpty t)
-> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], a)
unParser Parser e t a
p e
e Map NormalizedText (NonEmpty t)
s of
    Right (Map NormalizedText (NonEmpty t), [Warning], a)
r -> (Map NormalizedText (NonEmpty t), [Warning], a)
-> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], a)
forall a b. b -> Either a b
Right (Map NormalizedText (NonEmpty t), [Warning], a)
r
    Left {} -> Parser e t a
-> e
-> Map NormalizedText (NonEmpty t)
-> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], a)
forall e t a.
Parser e t a
-> e
-> Map NormalizedText (NonEmpty t)
-> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], a)
unParser Parser e t a
q e
e Map NormalizedText (NonEmpty t)
s

getenv :: Parser e t e
getenv :: Parser e t e
getenv = (e
 -> Map NormalizedText (NonEmpty t)
 -> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], e))
-> Parser e t e
forall e t a.
(e
 -> Map NormalizedText (NonEmpty t)
 -> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], a))
-> Parser e t a
Parser ((e
  -> Map NormalizedText (NonEmpty t)
  -> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], e))
 -> Parser e t e)
-> (e
    -> Map NormalizedText (NonEmpty t)
    -> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], e))
-> Parser e t e
forall a b. (a -> b) -> a -> b
$ \e
e Map NormalizedText (NonEmpty t)
s -> (Map NormalizedText (NonEmpty t), [Warning], e)
-> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], e)
forall a b. b -> Either a b
Right (Map NormalizedText (NonEmpty t)
s, [], e
e)

request :: Text -> Parser e t (Maybe t)
request :: Text -> Parser e t (Maybe t)
request Text
name = (e
 -> Map NormalizedText (NonEmpty t)
 -> Either
      Fatal (Map NormalizedText (NonEmpty t), [Warning], Maybe t))
-> Parser e t (Maybe t)
forall e t a.
(e
 -> Map NormalizedText (NonEmpty t)
 -> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], a))
-> Parser e t a
Parser ((e
  -> Map NormalizedText (NonEmpty t)
  -> Either
       Fatal (Map NormalizedText (NonEmpty t), [Warning], Maybe t))
 -> Parser e t (Maybe t))
-> (e
    -> Map NormalizedText (NonEmpty t)
    -> Either
         Fatal (Map NormalizedText (NonEmpty t), [Warning], Maybe t))
-> Parser e t (Maybe t)
forall a b. (a -> b) -> a -> b
$ \e
_ Map NormalizedText (NonEmpty t)
s ->
  let name' :: NormalizedText
name' = Text -> NormalizedText
normalize Text
name in
  (Map NormalizedText (NonEmpty t), [Warning], Maybe t)
-> Either
     Fatal (Map NormalizedText (NonEmpty t), [Warning], Maybe t)
forall a b. b -> Either a b
Right ((Map NormalizedText (NonEmpty t), [Warning], Maybe t)
 -> Either
      Fatal (Map NormalizedText (NonEmpty t), [Warning], Maybe t))
-> (Map NormalizedText (NonEmpty t), [Warning], Maybe t)
-> Either
     Fatal (Map NormalizedText (NonEmpty t), [Warning], Maybe t)
forall a b. (a -> b) -> a -> b
$!
  case NormalizedText
-> Map NormalizedText (NonEmpty t) -> Maybe (NonEmpty t)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup NormalizedText
name' Map NormalizedText (NonEmpty t)
s of
    Maybe (NonEmpty t)
Nothing                 -> (Map NormalizedText (NonEmpty t)
s , [], Maybe t
forall a. Maybe a
Nothing)
    Just (t
x NonEmpty.:| [t]
xs) -> (Map NormalizedText (NonEmpty t)
s', [], t -> Maybe t
forall a. a -> Maybe a
Just t
x)
      where
        s' :: Map NormalizedText (NonEmpty t)
s' = case [t] -> Maybe (NonEmpty t)
forall a. [a] -> Maybe (NonEmpty a)
NonEmpty.nonEmpty [t]
xs of
               Maybe (NonEmpty t)
Nothing -> NormalizedText
-> Map NormalizedText (NonEmpty t)
-> Map NormalizedText (NonEmpty t)
forall k a. Ord k => k -> Map k a -> Map k a
Map.delete NormalizedText
name' Map NormalizedText (NonEmpty t)
s
               Just NonEmpty t
ne -> NormalizedText
-> NonEmpty t
-> Map NormalizedText (NonEmpty t)
-> Map NormalizedText (NonEmpty t)
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert NormalizedText
name' NonEmpty t
ne Map NormalizedText (NonEmpty t)
s

fatal :: Fatal -> Parser e t a
fatal :: Fatal -> Parser e t a
fatal Fatal
e = (e
 -> Map NormalizedText (NonEmpty t)
 -> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], a))
-> Parser e t a
forall e t a.
(e
 -> Map NormalizedText (NonEmpty t)
 -> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], a))
-> Parser e t a
Parser ((e
  -> Map NormalizedText (NonEmpty t)
  -> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], a))
 -> Parser e t a)
-> (e
    -> Map NormalizedText (NonEmpty t)
    -> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], a))
-> Parser e t a
forall a b. (a -> b) -> a -> b
$ \e
_ Map NormalizedText (NonEmpty t)
_ -> Fatal
-> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], a)
forall a b. a -> Either a b
Left Fatal
e

warnings :: [Warning] -> IniParser ()
warnings :: [Warning] -> IniParser ()
warnings [Warning]
ws = (RawIni
 -> Map NormalizedText (NonEmpty IniSection)
 -> Either
      Fatal (Map NormalizedText (NonEmpty IniSection), [Warning], ()))
-> IniParser ()
forall e t a.
(e
 -> Map NormalizedText (NonEmpty t)
 -> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], a))
-> Parser e t a
Parser ((RawIni
  -> Map NormalizedText (NonEmpty IniSection)
  -> Either
       Fatal (Map NormalizedText (NonEmpty IniSection), [Warning], ()))
 -> IniParser ())
-> (RawIni
    -> Map NormalizedText (NonEmpty IniSection)
    -> Either
         Fatal (Map NormalizedText (NonEmpty IniSection), [Warning], ()))
-> IniParser ()
forall a b. (a -> b) -> a -> b
$ \RawIni
_ Map NormalizedText (NonEmpty IniSection)
s -> (Map NormalizedText (NonEmpty IniSection), [Warning], ())
-> Either
     Fatal (Map NormalizedText (NonEmpty IniSection), [Warning], ())
forall a b. b -> Either a b
Right (Map NormalizedText (NonEmpty IniSection)
s, [Warning]
ws, ())

------------------------------------------------------------------------

type IniParser = Parser RawIni IniSection

type SectionParser = Parser IniSection IniValue

data Fatal
  = NoSection Text
  | MissingField IniSection Text
  | BadField IniSection IniValue String
  | ParseError String
  deriving Int -> Fatal -> ShowS
[Fatal] -> ShowS
Fatal -> String
(Int -> Fatal -> ShowS)
-> (Fatal -> String) -> ([Fatal] -> ShowS) -> Show Fatal
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Fatal] -> ShowS
$cshowList :: [Fatal] -> ShowS
show :: Fatal -> String
$cshow :: Fatal -> String
showsPrec :: Int -> Fatal -> ShowS
$cshowsPrec :: Int -> Fatal -> ShowS
Show

data Warning
  = UnusedSection IniSection
  | UnusedField IniSection IniValue
  deriving Int -> Warning -> ShowS
[Warning] -> ShowS
Warning -> String
(Int -> Warning -> ShowS)
-> (Warning -> String) -> ([Warning] -> ShowS) -> Show Warning
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Warning] -> ShowS
$cshowList :: [Warning] -> ShowS
show :: Warning -> String
$cshow :: Warning -> String
showsPrec :: Int -> Warning -> ShowS
$cshowsPrec :: Int -> Warning -> ShowS
Show

------------------------------------------------------------------------

fatalString :: Fatal -> String
fatalString :: Fatal -> String
fatalString (NoSection Text
name) = String
"No top-level section named " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Text -> String
forall a. Show a => a -> String
show Text
name
fatalString (MissingField IniSection
sec Text
name) = String
"Missing field " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Text -> String
forall a. Show a => a -> String
show Text
name String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" in section " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Text -> String
forall a. Show a => a -> String
show (IniSection -> Text
isName IniSection
sec)
fatalString (BadField IniSection
sec IniValue
val String
err) =
  String
"Line " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show (IniValue -> Int
vLineNo IniValue
val) String -> ShowS
forall a. [a] -> [a] -> [a]
++
  String
" in section " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Text -> String
forall a. Show a => a -> String
show (IniSection -> Text
isName IniSection
sec) String -> ShowS
forall a. [a] -> [a] -> [a]
++
  String
": " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
err
fatalString (ParseError String
err) = String
err

warningString :: Warning -> String
warningString :: Warning -> String
warningString (UnusedSection IniSection
sec) = String
"Unused section " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Text -> String
forall a. Show a => a -> String
show (IniSection -> Text
isName IniSection
sec)
warningString (UnusedField IniSection
sec IniValue
val) =
  String
"Line " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show (IniValue -> Int
vLineNo IniValue
val) String -> ShowS
forall a. [a] -> [a] -> [a]
++
  String
" in section " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Text -> String
forall a. Show a => a -> String
show (IniSection -> Text
isName IniSection
sec) String -> ShowS
forall a. [a] -> [a] -> [a]
++
  String
": unused field"

------------------------------------------------------------------------

parseIniFile :: Text -> IniParser a -> Either Fatal ([Warning], a)
parseIniFile :: Text -> IniParser a -> Either Fatal ([Warning], a)
parseIniFile Text
text IniParser a
parser =
  case Text -> Either String RawIni
parseRawIni Text
text of
    Left String
e -> Fatal -> Either Fatal ([Warning], a)
forall a b. a -> Either a b
Left (String -> Fatal
ParseError String
e)
    Right RawIni
ini ->
      let entries :: Map NormalizedText (NonEmpty IniSection)
entries = (NonEmpty IniSection -> NonEmpty IniSection -> NonEmpty IniSection)
-> [(NormalizedText, NonEmpty IniSection)]
-> Map NormalizedText (NonEmpty IniSection)
forall k a. Ord k => (a -> a -> a) -> [(k, a)] -> Map k a
Map.fromListWith NonEmpty IniSection -> NonEmpty IniSection -> NonEmpty IniSection
forall a. Semigroup a => a -> a -> a
(<>)
                       [ (NormalizedText
k, IniSection -> NonEmpty IniSection
forall (f :: * -> *) a. Applicative f => a -> f a
pure IniSection
v) | (NormalizedText
k,IniSection
v) <- Seq (NormalizedText, IniSection) -> [(NormalizedText, IniSection)]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList (RawIni -> Seq (NormalizedText, IniSection)
fromRawIni RawIni
ini) ]
      in
      case IniParser a
-> RawIni
-> Map NormalizedText (NonEmpty IniSection)
-> Either
     Fatal (Map NormalizedText (NonEmpty IniSection), [Warning], a)
forall e t a.
Parser e t a
-> e
-> Map NormalizedText (NonEmpty t)
-> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], a)
unParser IniParser a
parser RawIni
ini Map NormalizedText (NonEmpty IniSection)
entries of
        Left Fatal
e -> Fatal -> Either Fatal ([Warning], a)
forall a b. a -> Either a b
Left Fatal
e
        Right (Map NormalizedText (NonEmpty IniSection)
entries', [Warning]
ws, a
x) -> ([Warning], a) -> Either Fatal ([Warning], a)
forall a b. b -> Either a b
Right ([Warning]
ws [Warning] -> [Warning] -> [Warning]
forall a. [a] -> [a] -> [a]
++ [Warning]
unused, a
x)
          where
            unused :: [Warning]
unused = [ IniSection -> Warning
UnusedSection IniSection
e | IniSection
e <- (NonEmpty IniSection -> [IniSection])
-> Map NormalizedText (NonEmpty IniSection) -> [IniSection]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap NonEmpty IniSection -> [IniSection]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList Map NormalizedText (NonEmpty IniSection)
entries' ]

------------------------------------------------------------------------

section :: Text -> SectionParser a -> IniParser a
section :: Text -> SectionParser a -> IniParser a
section Text
name SectionParser a
parser =
  do Maybe a
mb <- Text -> SectionParser a -> IniParser (Maybe a)
forall a. Text -> SectionParser a -> IniParser (Maybe a)
sectionMb Text
name SectionParser a
parser
     case Maybe a
mb of
       Maybe a
Nothing -> Fatal -> IniParser a
forall e t a. Fatal -> Parser e t a
fatal (Text -> Fatal
NoSection Text
name)
       Just a
x  -> a -> IniParser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure a
x

sectionMb :: Text -> SectionParser a -> IniParser (Maybe a)
sectionMb :: Text -> SectionParser a -> IniParser (Maybe a)
sectionMb Text
name SectionParser a
parser =
  do Maybe IniSection
mb <- Text -> Parser RawIni IniSection (Maybe IniSection)
forall e t. Text -> Parser e t (Maybe t)
request Text
name
     case Maybe IniSection
mb of
       Maybe IniSection
Nothing -> Maybe a -> IniParser (Maybe a)
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe a
forall a. Maybe a
Nothing
       Just IniSection
sec ->
         let entries :: Map NormalizedText (NonEmpty IniValue)
entries = (NonEmpty IniValue -> NonEmpty IniValue -> NonEmpty IniValue)
-> [(NormalizedText, NonEmpty IniValue)]
-> Map NormalizedText (NonEmpty IniValue)
forall k a. Ord k => (a -> a -> a) -> [(k, a)] -> Map k a
Map.fromListWith NonEmpty IniValue -> NonEmpty IniValue -> NonEmpty IniValue
forall a. Semigroup a => a -> a -> a
(<>)
                       [ (NormalizedText
k, IniValue -> NonEmpty IniValue
forall (f :: * -> *) a. Applicative f => a -> f a
pure IniValue
v) | (NormalizedText
k,IniValue
v) <- Seq (NormalizedText, IniValue) -> [(NormalizedText, IniValue)]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList (IniSection -> Seq (NormalizedText, IniValue)
isVals IniSection
sec) ]
         in
         case SectionParser a
-> IniSection
-> Map NormalizedText (NonEmpty IniValue)
-> Either
     Fatal (Map NormalizedText (NonEmpty IniValue), [Warning], a)
forall e t a.
Parser e t a
-> e
-> Map NormalizedText (NonEmpty t)
-> Either Fatal (Map NormalizedText (NonEmpty t), [Warning], a)
unParser SectionParser a
parser IniSection
sec Map NormalizedText (NonEmpty IniValue)
entries of
           Left Fatal
e -> Fatal -> IniParser (Maybe a)
forall e t a. Fatal -> Parser e t a
fatal Fatal
e
           Right (Map NormalizedText (NonEmpty IniValue)
entries', [Warning]
ws, a
result) ->
             do [Warning] -> IniParser ()
warnings ([Warning]
ws [Warning] -> [Warning] -> [Warning]
forall a. [a] -> [a] -> [a]
++ [ IniSection -> IniValue -> Warning
UnusedField IniSection
sec IniValue
v | IniValue
v <- (NonEmpty IniValue -> [IniValue])
-> Map NormalizedText (NonEmpty IniValue) -> [IniValue]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap NonEmpty IniValue -> [IniValue]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList Map NormalizedText (NonEmpty IniValue)
entries' ])
                Maybe a -> IniParser (Maybe a)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (a -> Maybe a
forall a. a -> Maybe a
Just a
result)

------------------------------------------------------------------------

field :: Text -> SectionParser Text
field :: Text -> SectionParser Text
field Text
name =
  do Maybe Text
mb <- Text -> SectionParser (Maybe Text)
fieldMb Text
name
     case Maybe Text
mb of
       Just Text
x -> Text -> SectionParser Text
forall (f :: * -> *) a. Applicative f => a -> f a
pure Text
x
       Maybe Text
Nothing ->
         do IniSection
s <- Parser IniSection IniValue IniSection
forall e t. Parser e t e
getenv
            Fatal -> SectionParser Text
forall e t a. Fatal -> Parser e t a
fatal (IniSection -> Text -> Fatal
MissingField IniSection
s Text
name)

fieldMbOf :: Text -> (Text -> Either String a) -> SectionParser (Maybe a)
fieldMbOf :: Text -> (Text -> Either String a) -> SectionParser (Maybe a)
fieldMbOf Text
name Text -> Either String a
validate =
  do Maybe IniValue
mb <- Text -> Parser IniSection IniValue (Maybe IniValue)
forall e t. Text -> Parser e t (Maybe t)
request Text
name
     case Maybe IniValue
mb of
       Maybe IniValue
Nothing -> Maybe a -> SectionParser (Maybe a)
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe a
forall a. Maybe a
Nothing
       Just IniValue
val ->
         case Text -> Either String a
validate (IniValue -> Text
getVal IniValue
val) of
           Left String
e ->
             do IniSection
sec <- Parser IniSection IniValue IniSection
forall e t. Parser e t e
getenv
                Fatal -> SectionParser (Maybe a)
forall e t a. Fatal -> Parser e t a
fatal (IniSection -> IniValue -> String -> Fatal
BadField IniSection
sec IniValue
val String
e)
           Right a
x -> Maybe a -> SectionParser (Maybe a)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (a -> Maybe a
forall a. a -> Maybe a
Just a
x)

fieldMb :: Text -> SectionParser (Maybe Text)
fieldMb :: Text -> SectionParser (Maybe Text)
fieldMb Text
name = (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)
-> Parser IniSection IniValue (Maybe IniValue)
-> SectionParser (Maybe Text)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text -> Parser IniSection IniValue (Maybe IniValue)
forall e t. Text -> Parser e t (Maybe t)
request Text
name

fieldDefOf :: Text -> (Text -> Either String a) -> a -> SectionParser a
fieldDefOf :: Text -> (Text -> Either String a) -> a -> SectionParser a
fieldDefOf Text
name Text -> Either String a
validate a
def = a -> Maybe a -> a
forall a. a -> Maybe a -> a
fromMaybe a
def (Maybe a -> a)
-> Parser IniSection IniValue (Maybe a) -> SectionParser a
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text
-> (Text -> Either String a)
-> Parser IniSection IniValue (Maybe a)
forall a.
Text -> (Text -> Either String a) -> SectionParser (Maybe a)
fieldMbOf Text
name Text -> Either String a
validate

fieldFlagDef :: Text -> Bool -> SectionParser Bool
fieldFlagDef :: Text -> Bool -> SectionParser Bool
fieldFlagDef Text
name Bool
def = Text -> (Text -> Either String Bool) -> Bool -> SectionParser Bool
forall a. Text -> (Text -> Either String a) -> a -> SectionParser a
fieldDefOf Text
name Text -> Either String Bool
flag Bool
def

------------------------------------------------------------------------

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