-- |
-- Module     : Data.Ini.Config.Raw
-- Copyright  : (c) Getty Ritter, 2017
-- License    : BSD
-- Maintainer : Getty Ritter <config-ini@infinitenegativeutility.com>
-- Stability  : experimental
--
-- __Warning!__ This module is subject to change in the future, and therefore should
-- not be relied upon to have a consistent API.
module Data.Ini.Config.Raw
  ( -- * INI types
    RawIni (..),
    IniSection (..),
    IniValue (..),
    BlankLine (..),
    NormalizedText (..),
    normalize,

    -- * serializing and deserializing
    parseRawIni,
    printRawIni,

    -- * inspection
    lookupInSection,
    lookupSection,
    lookupValue,
  )
where

import Control.Monad (void)
import qualified Data.Foldable as F
import Data.Monoid ((<>))
import Data.Sequence (Seq)
import qualified Data.Sequence as Seq
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.Lazy as LazyText
import qualified Data.Text.Lazy.Builder as Builder
import Data.Void (Void)
import Text.Megaparsec
import Text.Megaparsec.Char

type Parser = Parsec Void Text

-- | The 'NormalizedText' type is an abstract representation of text
-- which has had leading and trailing whitespace removed and been
-- normalized to lower-case, but from which we can still extract the
-- original, non-normalized version. This acts like the normalized
-- text for the purposes of 'Eq' and 'Ord' operations, so
--
-- @
--   'normalize' "  x  " == 'normalize' \"X\"
-- @
--
-- This type is used to store section and key names in the
data NormalizedText = NormalizedText
  { NormalizedText -> Text
actualText :: Text,
    NormalizedText -> Text
normalizedText :: Text
  }
  deriving (Int -> NormalizedText -> ShowS
[NormalizedText] -> ShowS
NormalizedText -> String
(Int -> NormalizedText -> ShowS)
-> (NormalizedText -> String)
-> ([NormalizedText] -> ShowS)
-> Show NormalizedText
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> NormalizedText -> ShowS
showsPrec :: Int -> NormalizedText -> ShowS
$cshow :: NormalizedText -> String
show :: NormalizedText -> String
$cshowList :: [NormalizedText] -> ShowS
showList :: [NormalizedText] -> ShowS
Show)

-- | The constructor function to build a 'NormalizedText' value. You
-- probably shouldn't be using this module directly, but if for some
-- reason you are using it, then you should be using this function to
-- create 'NormalizedText' values.
normalize :: Text -> NormalizedText
normalize :: Text -> NormalizedText
normalize Text
t = Text -> Text -> NormalizedText
NormalizedText Text
t (Text -> Text
T.toLower (Text -> Text
T.strip Text
t))

instance Eq NormalizedText where
  NormalizedText Text
_ Text
x == :: NormalizedText -> NormalizedText -> Bool
== NormalizedText Text
_ Text
y =
    Text
x Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
y

instance Ord NormalizedText where
  NormalizedText Text
_ Text
x compare :: NormalizedText -> NormalizedText -> Ordering
`compare` NormalizedText Text
_ Text
y =
    Text
x Text -> Text -> Ordering
forall a. Ord a => a -> a -> Ordering
`compare` Text
y

-- | An 'Ini' value is a mapping from section names to
--   'IniSection' values. The section names in this mapping are
--   normalized to lower-case and stripped of whitespace. This
--   sequence retains the ordering of the original source file.
newtype RawIni = RawIni
  { RawIni -> Seq (NormalizedText, IniSection)
fromRawIni :: Seq (NormalizedText, IniSection)
  }
  deriving (RawIni -> RawIni -> Bool
(RawIni -> RawIni -> Bool)
-> (RawIni -> RawIni -> Bool) -> Eq RawIni
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: RawIni -> RawIni -> Bool
== :: RawIni -> RawIni -> Bool
$c/= :: RawIni -> RawIni -> Bool
/= :: RawIni -> RawIni -> Bool
Eq, Int -> RawIni -> ShowS
[RawIni] -> ShowS
RawIni -> String
(Int -> RawIni -> ShowS)
-> (RawIni -> String) -> ([RawIni] -> ShowS) -> Show RawIni
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> RawIni -> ShowS
showsPrec :: Int -> RawIni -> ShowS
$cshow :: RawIni -> String
show :: RawIni -> String
$cshowList :: [RawIni] -> ShowS
showList :: [RawIni] -> ShowS
Show)

-- | An 'IniSection' consists of a name, a mapping of key-value pairs,
--   and metadata about where the section starts and ends in the
--   file. The section names found in 'isName' are __not__ normalized
--   to lower-case or stripped of whitespace, and thus should appear
--   exactly as they appear in the original source file.
data IniSection = IniSection
  { -- | The name of the section, as it appears in the
    -- original INI source
    IniSection -> Text
isName :: Text,
    -- | The key-value mapping within that section. Key
    -- names here are normalized to lower-case and
    -- stripped of whitespace. This sequence retains
    -- the ordering of the original source file.
    IniSection -> Seq (NormalizedText, IniValue)
isVals :: Seq (NormalizedText, IniValue),
    -- | The line on which the section begins. This
    -- field is ignored when serializing, and is only
    -- used for error messages produced when parsing
    -- and deserializing an INI structure.
    IniSection -> Int
isStartLine :: Int,
    -- | The line on which the section ends. This field
    -- is ignored when serializing, and is only used
    -- for error messages produced when parsing and
    -- deserializing an INI structure.
    IniSection -> Int
isEndLine :: Int,
    -- | The blank lines and comments that appear prior
    -- to the section head declaration, retained for
    -- pretty-printing identical INI files.
    IniSection -> Seq BlankLine
isComments :: Seq BlankLine
  }
  deriving (IniSection -> IniSection -> Bool
(IniSection -> IniSection -> Bool)
-> (IniSection -> IniSection -> Bool) -> Eq IniSection
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: IniSection -> IniSection -> Bool
== :: IniSection -> IniSection -> Bool
$c/= :: IniSection -> IniSection -> Bool
/= :: IniSection -> IniSection -> Bool
Eq, Int -> IniSection -> ShowS
[IniSection] -> ShowS
IniSection -> String
(Int -> IniSection -> ShowS)
-> (IniSection -> String)
-> ([IniSection] -> ShowS)
-> Show IniSection
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> IniSection -> ShowS
showsPrec :: Int -> IniSection -> ShowS
$cshow :: IniSection -> String
show :: IniSection -> String
$cshowList :: [IniSection] -> ShowS
showList :: [IniSection] -> ShowS
Show)

-- | An 'IniValue' represents a key-value mapping, and also stores the
--   line number where it appears. The key names and values found in
--   'vName' and 'vValue' respectively are _not_ normalized to
--   lower-case or stripped of whitespace, and thus should appear
--   exactly as they appear in the original source file.
data IniValue = IniValue
  { -- | The line on which the key/value mapping
    -- appears. This field is ignored when
    -- serializing, and is only used for error
    -- messages produced when parsing and
    -- deserializing an INI structure.
    IniValue -> Int
vLineNo :: Int,
    -- | The name of the key, as it appears in the INI source.
    IniValue -> Text
vName :: Text,
    -- | The value of the key
    IniValue -> Text
vValue :: Text,
    IniValue -> Seq BlankLine
vComments :: Seq BlankLine,
    -- | Right now, this will never show up in a parsed INI file, but
    --   it's used when emitting a default INI file: it causes the
    --   key-value line to include a leading comment as well.
    IniValue -> Bool
vCommentedOut :: Bool,
    IniValue -> Char
vDelimiter :: Char
  }
  deriving (IniValue -> IniValue -> Bool
(IniValue -> IniValue -> Bool)
-> (IniValue -> IniValue -> Bool) -> Eq IniValue
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: IniValue -> IniValue -> Bool
== :: IniValue -> IniValue -> Bool
$c/= :: IniValue -> IniValue -> Bool
/= :: IniValue -> IniValue -> Bool
Eq, Int -> IniValue -> ShowS
[IniValue] -> ShowS
IniValue -> String
(Int -> IniValue -> ShowS)
-> (IniValue -> String) -> ([IniValue] -> ShowS) -> Show IniValue
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> IniValue -> ShowS
showsPrec :: Int -> IniValue -> ShowS
$cshow :: IniValue -> String
show :: IniValue -> String
$cshowList :: [IniValue] -> ShowS
showList :: [IniValue] -> ShowS
Show)

-- | We want to keep track of the whitespace/comments in between KV
--   lines, so this allows us to track those lines in a reproducible
--   way.
data BlankLine
  = CommentLine Char Text
  | BlankLine
  deriving (BlankLine -> BlankLine -> Bool
(BlankLine -> BlankLine -> Bool)
-> (BlankLine -> BlankLine -> Bool) -> Eq BlankLine
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: BlankLine -> BlankLine -> Bool
== :: BlankLine -> BlankLine -> Bool
$c/= :: BlankLine -> BlankLine -> Bool
/= :: BlankLine -> BlankLine -> Bool
Eq, Int -> BlankLine -> ShowS
[BlankLine] -> ShowS
BlankLine -> String
(Int -> BlankLine -> ShowS)
-> (BlankLine -> String)
-> ([BlankLine] -> ShowS)
-> Show BlankLine
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> BlankLine -> ShowS
showsPrec :: Int -> BlankLine -> ShowS
$cshow :: BlankLine -> String
show :: BlankLine -> String
$cshowList :: [BlankLine] -> ShowS
showList :: [BlankLine] -> ShowS
Show)

-- | Parse a 'Text' value into an 'Ini' value, retaining a maximal
-- amount of structure as needed to reconstruct the original INI file.
parseRawIni :: Text -> Either String RawIni
parseRawIni :: Text -> Either String RawIni
parseRawIni Text
t = case Parsec Void Text RawIni
-> String -> Text -> Either (ParseErrorBundle Text Void) RawIni
forall e s a.
Parsec e s a -> String -> s -> Either (ParseErrorBundle s e) a
runParser Parsec Void Text RawIni
pIni String
"ini file" Text
t of
  Left ParseErrorBundle Text Void
err -> String -> Either String RawIni
forall a b. a -> Either a b
Left (ParseErrorBundle Text Void -> String
forall s e.
(VisualStream s, TraversableStream s, ShowErrorComponent e) =>
ParseErrorBundle s e -> String
errorBundlePretty ParseErrorBundle Text Void
err)
  Right RawIni
v -> RawIni -> Either String RawIni
forall a b. b -> Either a b
Right RawIni
v

pIni :: Parser RawIni
pIni :: Parsec Void Text RawIni
pIni = do
  leading <- Parser (Seq BlankLine)
sBlanks
  pSections leading Seq.empty

sBlanks :: Parser (Seq BlankLine)
sBlanks :: Parser (Seq BlankLine)
sBlanks = [BlankLine] -> Seq BlankLine
forall a. [a] -> Seq a
Seq.fromList ([BlankLine] -> Seq BlankLine)
-> ParsecT Void Text Identity [BlankLine] -> Parser (Seq BlankLine)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ParsecT Void Text Identity BlankLine
-> ParsecT Void Text Identity [BlankLine]
forall (m :: * -> *) a. MonadPlus m => m a -> m [a]
many ((BlankLine
BlankLine BlankLine
-> ParsecT Void Text Identity ()
-> ParsecT Void Text Identity BlankLine
forall a b.
a -> ParsecT Void Text Identity b -> ParsecT Void Text Identity a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ ParsecT Void Text Identity (Tokens Text)
-> ParsecT Void Text Identity ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void ParsecT Void Text Identity (Tokens Text)
forall e s (m :: * -> *).
(MonadParsec e s m, Token s ~ Char) =>
m (Tokens s)
eol) ParsecT Void Text Identity BlankLine
-> ParsecT Void Text Identity BlankLine
-> ParsecT Void Text Identity BlankLine
forall a.
ParsecT Void Text Identity a
-> ParsecT Void Text Identity a -> ParsecT Void Text Identity a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> ParsecT Void Text Identity BlankLine
sComment)

sComment :: Parser BlankLine
sComment :: ParsecT Void Text Identity BlankLine
sComment = do
  c <- [Token Text] -> ParsecT Void Text Identity (Token Text)
forall (f :: * -> *) e s (m :: * -> *).
(Foldable f, MonadParsec e s m) =>
f (Token s) -> m (Token s)
oneOf [Char
';', Char
'#']
  txt <- T.pack `fmap` manyTill anySingle eol
  return (CommentLine c txt)

pSections :: Seq BlankLine -> Seq (NormalizedText, IniSection) -> Parser RawIni
pSections :: Seq BlankLine
-> Seq (NormalizedText, IniSection) -> Parsec Void Text RawIni
pSections Seq BlankLine
leading Seq (NormalizedText, IniSection)
prevs =
  Seq BlankLine
-> Seq (NormalizedText, IniSection) -> Parsec Void Text RawIni
pSection Seq BlankLine
leading Seq (NormalizedText, IniSection)
prevs Parsec Void Text RawIni
-> Parsec Void Text RawIni -> Parsec Void Text RawIni
forall a.
ParsecT Void Text Identity a
-> ParsecT Void Text Identity a -> ParsecT Void Text Identity a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> (Seq (NormalizedText, IniSection) -> RawIni
RawIni Seq (NormalizedText, IniSection)
prevs RawIni -> ParsecT Void Text Identity () -> Parsec Void Text RawIni
forall a b.
a -> ParsecT Void Text Identity b -> ParsecT Void Text Identity a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ ParsecT Void Text Identity () -> ParsecT Void Text Identity ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void ParsecT Void Text Identity ()
forall e s (m :: * -> *). MonadParsec e s m => m ()
eof)

pSection :: Seq BlankLine -> Seq (NormalizedText, IniSection) -> Parser RawIni
pSection :: Seq BlankLine
-> Seq (NormalizedText, IniSection) -> Parsec Void Text RawIni
pSection Seq BlankLine
leading Seq (NormalizedText, IniSection)
prevs = do
  start <- Parser Int
getCurrentLine
  void (char '[')
  name <- T.pack `fmap` some (noneOf ['[', ']'])
  void (char ']')
  void eol
  comments <- sBlanks
  pPairs (T.strip name) start leading prevs comments Seq.empty

pPairs ::
  Text ->
  Int ->
  Seq BlankLine ->
  Seq (NormalizedText, IniSection) ->
  Seq BlankLine ->
  Seq (NormalizedText, IniValue) ->
  Parser RawIni
pPairs :: Text
-> Int
-> Seq BlankLine
-> Seq (NormalizedText, IniSection)
-> Seq BlankLine
-> Seq (NormalizedText, IniValue)
-> Parsec Void Text RawIni
pPairs Text
name Int
start Seq BlankLine
leading Seq (NormalizedText, IniSection)
prevs Seq BlankLine
comments Seq (NormalizedText, IniValue)
pairs = Parsec Void Text RawIni
newPair Parsec Void Text RawIni
-> Parsec Void Text RawIni -> Parsec Void Text RawIni
forall a.
ParsecT Void Text Identity a
-> ParsecT Void Text Identity a -> ParsecT Void Text Identity a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parsec Void Text RawIni
finishedSection
  where
    newPair :: Parsec Void Text RawIni
newPair = do
      (n, pair) <- Seq BlankLine -> Parser (NormalizedText, IniValue)
pPair Seq BlankLine
comments
      rs <- sBlanks
      pPairs name start leading prevs rs (pairs Seq.|> (n, pair))
    finishedSection :: Parsec Void Text RawIni
finishedSection = do
      end <- Parser Int
getCurrentLine
      let newSection =
            IniSection
              { isName :: Text
isName = Text
name,
                isVals :: Seq (NormalizedText, IniValue)
isVals = Seq (NormalizedText, IniValue)
pairs,
                isStartLine :: Int
isStartLine = Int
start,
                isEndLine :: Int
isEndLine = Int
end,
                isComments :: Seq BlankLine
isComments = Seq BlankLine
leading
              }
      pSections comments (prevs Seq.|> (normalize name, newSection))

pPair :: Seq BlankLine -> Parser (NormalizedText, IniValue)
pPair :: Seq BlankLine -> Parser (NormalizedText, IniValue)
pPair Seq BlankLine
leading = do
  pos <- Parser Int
getCurrentLine
  key <- T.pack `fmap` some (noneOf ['[', ']', '=', ':'])
  delim <- oneOf [':', '=']
  val <- T.pack `fmap` manyTill anySingle eol
  return
    ( normalize key,
      IniValue
        { vLineNo = pos,
          vName = key,
          vValue = val,
          vComments = leading,
          vCommentedOut = False,
          vDelimiter = delim
        }
    )

getCurrentLine :: Parser Int
getCurrentLine :: Parser Int
getCurrentLine = (Int -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Int) -> (SourcePos -> Int) -> SourcePos -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Pos -> Int
unPos (Pos -> Int) -> (SourcePos -> Pos) -> SourcePos -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SourcePos -> Pos
sourceLine) (SourcePos -> Int)
-> ParsecT Void Text Identity SourcePos -> Parser Int
forall a b.
(a -> b)
-> ParsecT Void Text Identity a -> ParsecT Void Text Identity b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` ParsecT Void Text Identity SourcePos
forall s e (m :: * -> *).
(TraversableStream s, MonadParsec e s m) =>
m SourcePos
getSourcePos

-- | Serialize an INI file to text, complete with any comments which
-- appear in the INI structure, and retaining the aesthetic details
-- which are present in the INI file.
printRawIni :: RawIni -> Text
printRawIni :: RawIni -> Text
printRawIni = LazyText -> Text
LazyText.toStrict (LazyText -> Text) -> (RawIni -> LazyText) -> RawIni -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> LazyText
Builder.toLazyText (Builder -> LazyText) -> (RawIni -> Builder) -> RawIni -> LazyText
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((NormalizedText, IniSection) -> Builder)
-> Seq (NormalizedText, IniSection) -> Builder
forall m a. Monoid m => (a -> m) -> Seq a -> m
forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
F.foldMap (NormalizedText, IniSection) -> Builder
forall {a}. (a, IniSection) -> Builder
build (Seq (NormalizedText, IniSection) -> Builder)
-> (RawIni -> Seq (NormalizedText, IniSection))
-> RawIni
-> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RawIni -> Seq (NormalizedText, IniSection)
fromRawIni
  where
    build :: (a, IniSection) -> Builder
build (a
_, IniSection
ini) =
      (BlankLine -> Builder) -> Seq BlankLine -> Builder
forall m a. Monoid m => (a -> m) -> Seq a -> m
forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
F.foldMap BlankLine -> Builder
buildComment (IniSection -> Seq BlankLine
isComments IniSection
ini)
        Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Char -> Builder
Builder.singleton Char
'['
        Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Text -> Builder
Builder.fromText (IniSection -> Text
isName IniSection
ini)
        Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> String -> Builder
Builder.fromString String
"]\n"
        Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ((NormalizedText, IniValue) -> Builder)
-> Seq (NormalizedText, IniValue) -> Builder
forall m a. Monoid m => (a -> m) -> Seq a -> m
forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
F.foldMap (NormalizedText, IniValue) -> Builder
forall {a}. (a, IniValue) -> Builder
buildKV (IniSection -> Seq (NormalizedText, IniValue)
isVals IniSection
ini)
    buildComment :: BlankLine -> Builder
buildComment BlankLine
BlankLine = Char -> Builder
Builder.singleton Char
'\n'
    buildComment (CommentLine Char
c Text
txt) =
      Char -> Builder
Builder.singleton Char
c Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Text -> Builder
Builder.fromText Text
txt Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Char -> Builder
Builder.singleton Char
'\n'
    buildKV :: (a, IniValue) -> Builder
buildKV (a
_, IniValue
val) =
      (BlankLine -> Builder) -> Seq BlankLine -> Builder
forall m a. Monoid m => (a -> m) -> Seq a -> m
forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
F.foldMap BlankLine -> Builder
buildComment (IniValue -> Seq BlankLine
vComments IniValue
val)
        Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> (if IniValue -> Bool
vCommentedOut IniValue
val then String -> Builder
Builder.fromString String
"# " else Builder
forall a. Monoid a => a
mempty)
        Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Text -> Builder
Builder.fromText (IniValue -> Text
vName IniValue
val)
        Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Char -> Builder
Builder.singleton (IniValue -> Char
vDelimiter IniValue
val)
        Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Text -> Builder
Builder.fromText (IniValue -> Text
vValue IniValue
val)
        Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Char -> Builder
Builder.singleton Char
'\n'

-- | Look up an Ini value by section name and key. Returns the sequence
-- of matches.
lookupInSection ::
  -- | The section name. Will be normalized prior to
  -- comparison.
  Text ->
  -- | The key. Will be normalized prior to comparison.
  Text ->
  -- | The Ini to search.
  RawIni ->
  Seq.Seq Text
lookupInSection :: Text -> Text -> RawIni -> Seq Text
lookupInSection Text
sec Text
opt RawIni
ini =
  IniValue -> Text
vValue (IniValue -> Text) -> Seq IniValue -> Seq Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Seq (Seq IniValue) -> Seq IniValue
forall (t :: * -> *) (f :: * -> *) a.
(Foldable t, Alternative f) =>
t (f a) -> f a
F.asum (Text -> IniSection -> Seq IniValue
lookupValue Text
opt (IniSection -> Seq IniValue)
-> Seq IniSection -> Seq (Seq IniValue)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text -> RawIni -> Seq IniSection
lookupSection Text
sec RawIni
ini)

-- | Look up an Ini section by name. Returns a sequence of all matching
-- section records.
lookupSection ::
  -- | The section name. Will be normalized prior to
  -- comparison.
  Text ->
  -- | The Ini to search.
  RawIni ->
  Seq.Seq IniSection
lookupSection :: Text -> RawIni -> Seq IniSection
lookupSection Text
name RawIni
ini =
  (NormalizedText, IniSection) -> IniSection
forall a b. (a, b) -> b
snd ((NormalizedText, IniSection) -> IniSection)
-> Seq (NormalizedText, IniSection) -> Seq IniSection
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ((NormalizedText, IniSection) -> Bool)
-> Seq (NormalizedText, IniSection)
-> Seq (NormalizedText, IniSection)
forall a. (a -> Bool) -> Seq a -> Seq a
Seq.filter ((NormalizedText -> NormalizedText -> Bool
forall a. Eq a => a -> a -> Bool
== Text -> NormalizedText
normalize Text
name) (NormalizedText -> Bool)
-> ((NormalizedText, IniSection) -> NormalizedText)
-> (NormalizedText, IniSection)
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (NormalizedText, IniSection) -> NormalizedText
forall a b. (a, b) -> a
fst) (RawIni -> Seq (NormalizedText, IniSection)
fromRawIni RawIni
ini)

-- | Look up an Ini key's value in a given section by the key. Returns
-- the sequence of matches.
lookupValue ::
  -- | The key. Will be normalized prior to comparison.
  Text ->
  -- | The section to search.
  IniSection ->
  Seq.Seq IniValue
lookupValue :: Text -> IniSection -> Seq IniValue
lookupValue Text
name IniSection
section =
  (NormalizedText, IniValue) -> IniValue
forall a b. (a, b) -> b
snd ((NormalizedText, IniValue) -> IniValue)
-> Seq (NormalizedText, IniValue) -> Seq IniValue
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ((NormalizedText, IniValue) -> Bool)
-> Seq (NormalizedText, IniValue) -> Seq (NormalizedText, IniValue)
forall a. (a -> Bool) -> Seq a -> Seq a
Seq.filter ((NormalizedText -> NormalizedText -> Bool
forall a. Eq a => a -> a -> Bool
== Text -> NormalizedText
normalize Text
name) (NormalizedText -> Bool)
-> ((NormalizedText, IniValue) -> NormalizedText)
-> (NormalizedText, IniValue)
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (NormalizedText, IniValue) -> NormalizedText
forall a b. (a, b) -> a
fst) (IniSection -> Seq (NormalizedText, IniValue)
isVals IniSection
section)