packages feed

ini 0.3.6 → 0.4.0

raw patch · 3 files changed

+177/−23 lines, 3 filesdep +hspecdep +inidep +semigroupsdep ~basePVP ok

version bump matches the API change (PVP)

Dependencies added: hspec, ini, semigroups

Dependency ranges changed: base

API changes (from Hackage documentation)

- Data.Ini: [unIni] :: Ini -> HashMap Text (HashMap Text Text)
- Data.Ini: instance Data.Semigroup.Semigroup Data.Ini.Ini
- Data.Ini: newtype Ini
+ Data.Ini: [iniGlobals] :: Ini -> [(Text, Text)]
+ Data.Ini: [iniSections] :: Ini -> HashMap Text [(Text, Text)]
+ Data.Ini: data Ini
+ Data.Ini: instance GHC.Base.Semigroup Data.Ini.Ini
+ Data.Ini: instance GHC.Classes.Eq Data.Ini.Ini
+ Data.Ini: lookupArray :: Text -> Text -> Ini -> Either String [Text]
+ Data.Ini: readArray :: Text -> Text -> (Text -> Either String (a, Text)) -> Ini -> Either String [a]
+ Data.Ini: unIni :: Ini -> HashMap Text (HashMap Text Text)
- Data.Ini: Ini :: HashMap Text (HashMap Text Text) -> Ini
+ Data.Ini: Ini :: HashMap Text [(Text, Text)] -> [(Text, Text)] -> Ini
- Data.Ini: sectionParser :: Parser (Text, HashMap Text Text)
+ Data.Ini: sectionParser :: Parser (Text, [(Text, Text)])

Files

ini.cabal view
@@ -1,5 +1,5 @@ name:                ini-version:             0.3.6+version:             0.4.0 synopsis:            Quick and easy configuration files in the INI format. description:         Quick and easy configuration files in the INI format. license:             BSD3@@ -22,6 +22,17 @@                      attoparsec,                      text,                      unordered-containers+  if !impl(ghc >= 8)+    build-depends: semigroups >= 0.10 && < 0.19++test-suite ini-test+  type: exitcode-stdio-1.0+  hs-source-dirs: test+  main-is: Main.hs+  build-depends:     base >= 4 && <5+                   , ini+                   , hspec+                   , unordered-containers  source-repository head   type:     git
src/Data/Ini.hs view
@@ -42,7 +42,9 @@    readIniFile   ,parseIni   ,lookupValue+  ,lookupArray   ,readValue+  ,readArray   ,parseValue   ,sections   ,keys@@ -57,6 +59,7 @@   ,writeIniFileWith    -- * Types   ,Ini(..)+  ,unIni    -- * Parsers   ,iniParser   ,sectionParser@@ -71,17 +74,34 @@ import           Data.Char import           Data.HashMap.Strict        (HashMap) import qualified Data.HashMap.Strict        as M-import           Data.Semigroup+import Data.Maybe import           Data.Monoid (Monoid)+import           Data.Semigroup import           Data.Text                  (Text) import qualified Data.Text                  as T import qualified Data.Text.IO               as T import           Prelude                    hiding (takeWhile)  -- | An INI configuration.-newtype Ini = Ini { unIni :: HashMap Text (HashMap Text Text) }-  deriving (Show, Semigroup, Monoid)+data Ini =+  Ini+    { iniSections :: HashMap Text [(Text, Text)]+    , iniGlobals  :: [(Text, Text)]+    }+  deriving (Show, Eq) +instance Semigroup Ini where+  (<>) = mappend++instance Monoid Ini where+  mempty = Ini {iniGlobals = mempty, iniSections = mempty}+  mappend x y =+    Ini {iniGlobals = mempty, iniSections = iniSections x <> iniSections y}++{-# DEPRECATED #-}+unIni :: Ini -> HashMap Text (HashMap Text Text)+unIni = fmap M.fromList . iniSections+ -- | Parse an INI file. readIniFile :: FilePath -> IO (Either String Ini) readIniFile = fmap parseIni . T.readFile@@ -90,36 +110,89 @@ parseIni :: Text -> Either String Ini parseIni = parseOnly iniParser --- | Lookup values in the config.-lookupValue :: Text -> Text -> Ini -> Either String Text-lookupValue name key (Ini ini) =-  case M.lookup name ini of+-- | Lookup one value in the config.+--+-- Example:+--+-- >>> parseIni "[SERVER]\nport: 6667\nhostname: localhost" >>= lookupValue "SERVER" "hostname"+-- Right "localhost"+lookupValue :: Text -- ^ Section name+            -> Text -- ^ Key+            -> Ini -> Either String Text+lookupValue name key (Ini {iniSections=secs}) =+  case M.lookup name secs of     Nothing -> Left ("Couldn't find section: " ++ T.unpack name)     Just section ->-      case M.lookup key section of+      case lookup key section of         Nothing -> Left ("Couldn't find key: " ++ T.unpack key)         Just value -> return value +-- | Lookup one value in the config.+--+-- Example:+--+-- >>> parseIni "[SERVER]\nport: 6667\nhostname: localhost" >>= lookupValue "SERVER" "hostname"+-- Right "localhost"+lookupArray :: Text -- ^ Section name+            -> Text -- ^ Key+            -> Ini -> Either String [Text]+lookupArray name key (Ini {iniSections = secs}) =+  case M.lookup name secs of+    Nothing -> Left ("Couldn't find section: " ++ T.unpack name)+    Just section ->+      case mapMaybe+             (\(k, v) ->+                if k == key+                  then Just v+                  else Nothing)+             section of+        [] -> Left ("Couldn't find key: " ++ T.unpack key)+        values -> return values+ -- | Get the sections in the config.+--+-- Example:+--+-- >>> sections <$> parseIni "[SERVER]\nport: 6667\nhostname: localhost"+-- Right ["SERVER"] sections :: Ini -> [Text]-sections (Ini ini) = M.keys ini+sections = M.keys . iniSections  -- | Get the keys in a section.-keys :: Text -> Ini -> Either String [Text]-keys name (Ini ini) =-  case M.lookup name ini of+--+-- Example:+--+-- >>> parseIni "[SERVER]\nport: 6667\nhostname: localhost" >>= keys "SERVER"+-- Right ["hostname","port"]+keys :: Text -- ^ Section name+     -> Ini -> Either String [Text]+keys name i =+  case M.lookup name (iniSections i) of     Nothing -> Left ("Couldn't find section: " ++ T.unpack name)-    Just section -> Right (M.keys section)+    Just section -> Right (map fst section)  -- | Read a value using a reader from "Data.Text.Read".-readValue :: Text -> Text -> (Text -> Either String (a, Text))+readValue :: Text -- ^ Section name+          -> Text -- ^ Key+          -> (Text -> Either String (a, Text))           -> Ini           -> Either String a readValue section key f ini =   lookupValue section key ini >>= f >>= return . fst +-- | Read an array of values using a reader from "Data.Text.Read".+readArray :: Text -- ^ Section name+          -> Text -- ^ Key+          -> (Text -> Either String (a, Text))+          -> Ini+          -> Either String [a]+readArray section key f ini =+  fmap (map fst) (lookupArray section key ini >>= mapM f)+ -- | Parse a value using a reader from "Data.Attoparsec.Text".-parseValue :: Text -> Text -> Parser a+parseValue :: Text -- ^ Section name+           -> Text -- ^ Key+           -> Parser a            -> Ini            -> Either String a parseValue section key f ini =@@ -156,11 +229,11 @@  -- | Print an INI config. printIniWith :: WriteIniSettings -> Ini -> Text-printIniWith wis (Ini ini) =-  T.concat (map buildSection (M.toList ini))+printIniWith wis i =+  T.concat (map buildSection (M.toList (iniSections i)))   where buildSection (name,pairs) =           "[" <> name <> "]\n" <>-          T.concat (map buildPair (M.toList pairs))+          T.concat (map buildPair pairs)         buildPair (name,value) =           name <> separator <> value <> "\n"         separator = case writeIniKeySeparator wis of@@ -169,10 +242,13 @@  -- | Parser for an INI. iniParser :: Parser Ini-iniParser = fmap (Ini . M.fromList) (many sectionParser)+iniParser =+  (\kv secs -> Ini {iniSections = M.fromList secs, iniGlobals = kv}) <$>+  many keyValueParser <*>+  many sectionParser  -- | A section. Format: @[foo]@. Conventionally, @[FOO]@.-sectionParser :: Parser (Text,HashMap Text Text)+sectionParser :: Parser (Text,[(Text, Text)]) sectionParser =   do skipEndOfLine      skipComments@@ -182,7 +258,7 @@      _ <- char ']'      skipEndOfLine      values <- many keyValueParser-     return (T.strip name, M.fromList values)+     return (T.strip name, values)  -- | A key-value pair. Either @foo: bar@ or @foo=bar@. keyValueParser :: Parser (Text,Text)@@ -204,7 +280,7 @@  -- | Skip end of line and whitespace beyond. skipEndOfLine :: Parser ()-skipEndOfLine = skipWhile (\c -> isEndOfLine c)+skipEndOfLine = skipWhile isSpace  -- | Skip comments starting at the beginning of the line. skipComments :: Parser ()
+ test/Main.hs view
@@ -0,0 +1,67 @@+{-# LANGUAGE OverloadedStrings #-}++-- | Simple test suite.++module Main where++import qualified Data.HashMap.Strict as HM+import           Data.Ini+import           Test.Hspec++main :: IO ()+main =+  hspec+    (do describe+          "Regular files"+          (do it+                "Multi-section file with comments"+                (shouldBe+                   (parseIni+                      "# Some comment.\n\+                      \[SERVER]\n\+                      \port=6667\n\+                      \hostname=localhost\n\+                      \[AUTH]\n\+                      \user=hello\n\+                      \pass=world\n\+                      \# Salt can be an empty string.\n\+                      \salt=")+                   (Right+                      (Ini+                         { iniSections =+                             HM.fromList+                               [ ( "AUTH"+                                 , [ ("user", "hello")+                                   , ("pass", "world")+                                   , ("salt", "")+                                   ])+                               , ( "SERVER"+                                 , [("port", "6667"), ("hostname", "localhost")])+                               ]+                         , iniGlobals = []+                         })))+              it+                "File with globals"+                (shouldBe+                   (parseIni+                      "# Some comment.\n\+                      \port=6667\n\+                      \hostname=localhost\n\+                      \[AUTH]\n\+                      \user=hello\n\+                      \pass=world\n\+                      \# Salt can be an empty string.\n\+                      \salt=")+                   (Right+                      (Ini+                         { iniSections =+                             HM.fromList+                               [ ( "AUTH"+                                 , [ ("user", "hello")+                                   , ("pass", "world")+                                   , ("salt", "")+                                   ])+                               ]+                         , iniGlobals =+                             [("port", "6667"), ("hostname", "localhost")]+                         })))))