kontra-config (empty) → 0.1
raw patch · 5 files changed
+160/−0 lines, 5 filesdep +basedep +bytestringdep +data-defaultsetup-changed
Dependencies added: base, bytestring, data-default, exceptions, text, transformers-base, unjson, utf8-string, yaml
Files
- LICENSE +30/−0
- README.md +3/−0
- Setup.hs +2/−0
- kontra-config.cabal +38/−0
- src/Configuration.hs +87/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2015, Scrive++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Gracjan Polak nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,3 @@+# kontra-config [](https://hackage.haskell.org/package/kontra-config) [](http://travis-ci.org/scrive/kontra-config)++A library for reading JSON configuration files. Based on unjson.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ kontra-config.cabal view
@@ -0,0 +1,38 @@+name: kontra-config+version: 0.1+synopsis: JSON config file parsing based on unjson+description: A library for reading JSON configuration files. Based on unjson.+homepage: https://github.com/scrive/kontra-config+license: BSD3+license-file: LICENSE+author: Scrive AB+maintainer: Gracjan Polak <gracjan@scrive.com>, Jonathan Jouty <jonathan@scrive.com>, Mikhail Glushenkov <mikhail@scrive.com>+copyright: Scrive AB+category: Data+build-type: Simple+cabal-version: >=1.10+extra-source-files: README.md+tested-with: GHC == 7.4.2, GHC == 7.6.3, GHC == 7.8.4, GHC == 7.10.3, GHC == 8.0.1++source-repository head+ type: git+ location: https://github.com/scrive/kontra-config++library+ exposed-modules: Configuration++ build-depends: base < 5+ , transformers-base+ , data-default+ , exceptions >= 0.6+ , bytestring+ , utf8-string+ , text+ , unjson+ , yaml++ hs-source-dirs: src++ default-language: Haskell2010+ default-extensions: FlexibleContexts+ , ScopedTypeVariables
+ src/Configuration.hs view
@@ -0,0 +1,87 @@+module Configuration (+ readConfig+ ) where++import Control.Monad.Base+import Control.Monad.Catch+import Data.Default+import Data.Unjson+import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as BSL+import qualified Data.ByteString.Lazy.UTF8 as BSL (toString)+import qualified Data.Text as Text+import qualified Data.Yaml as Yaml++--+-- Error handling here:+-- 1. When no file: please create file and full docs+-- 2. When looks like json but not parse as json: info where it did not parse, no other info+-- 3. When does not look like json and does not readIO: full docs+-- 4. When unjson has issue, then just info about specific problems++readConfig :: forall a m . (Unjson a, Default a, MonadBase IO m, MonadCatch m) => (String -> m ()) -> FilePath -> m a+readConfig logger path = do+ logger $ "Reading configuration " ++ path ++ "..."+ bsl' <- either logExceptionAndPrintFullDocs return+ =<< try (liftBase (BSL.readFile path))++ let bsl = BSL.dropWhile (`elem` [10,13,32]) bsl'++ res <- do+ js <- either logYamlParseExceptionAndBlameJsonParser return $+ Yaml.decodeEither' (BS.concat (BSL.toChunks bsl))+ case parse ud js of+ Result value [] -> return value+ Result _ problems -> logProblems problems++ logger $ "Configuration file " ++ path ++ " read and parsed."+ return res+ where+ ud :: UnjsonDef a+ ud = unjsonDef+ logStringAndFail :: String -> m g+ logStringAndFail ex = do+ logger $ ex+ fail ex+ logYamlParseExceptionAndBlameJsonParser :: Yaml.ParseException -> m g+ logYamlParseExceptionAndBlameJsonParser ex = do+ -- sadly parsing issues in aeson as reported as badly as anything else+ logStringAndBlameJsonParser $ showNiceYamlParseException path ex+ logStringAndBlameJsonParser :: String -> m g+ logStringAndBlameJsonParser ex = do+ -- sadly parsing issues in aeson as reported as badly as anything else+ logger $ ex+ logStringAndFail $ "Configuration file '" ++ path ++ "' has syntax errors and is not a valid json"+ logExceptionAndPrintFullDocs :: SomeException -> m g+ logExceptionAndPrintFullDocs ex = logStringAndPrintFullDocs (show ex)+ logStringAndPrintFullDocs :: String -> m g+ logStringAndPrintFullDocs ex = do+ logger $ ex ++ "\n" ++ render ud ++ "\n" ++ configAsJsonString def+ fail (show ex)+ logProblem (Anchored xpath msg) = do+ case renderForPath xpath ud of+ Just moreInfo -> do+ logger $ show xpath ++ ": " ++ Text.unpack msg ++ "\n" ++ moreInfo+ Nothing -> do+ logger $ show xpath ++ ": " ++ Text.unpack msg+ logProblems problems = do+ logger $ "There were issues with the content of configuration " ++ path+ mapM_ logProblem problems+ fail $ "There were issues with the content of configuration " ++ path+ configAsJsonString :: a -> String+ configAsJsonString a = BSL.toString $ unjsonToByteStringLazy' (Options { pretty = True, indent = 4, nulls = False }) ud a++showNiceYamlParseException :: FilePath -> Yaml.ParseException -> String+showNiceYamlParseException filepath parseException =+ case parseException of+ Yaml.NonScalarKey -> filepath ++ ": non scalar key"+ Yaml.UnknownAlias anchorName -> filepath ++ ": unknown alias " ++ anchorName+ Yaml.UnexpectedEvent received expected -> filepath ++ ": unknown event received " ++ show received ++ " when expected " ++ show expected+ Yaml.InvalidYaml Nothing -> filepath ++ ": invalid yaml (no further info available)"+ Yaml.InvalidYaml (Just (Yaml.YamlException ex)) -> filepath ++ ": invalid yaml: " ++ ex+ Yaml.InvalidYaml (Just (Yaml.YamlParseException problem context (Yaml.YamlMark _index line column))) ->+ filepath ++ ":" ++ show (line+1) ++ ":" ++ show (column+1) ++ ": " ++ problem ++ " " ++ context+ Yaml.AesonException ex -> filepath ++ ": " ++ ex+ Yaml.OtherParseException ex -> filepath ++ ": " ++ show ex+ Yaml.NonStringKeyAlias anchorName value -> filepath ++ ": unknown non-string key alias " ++ show anchorName ++ ", " ++ show value+ Yaml.CyclicIncludes -> filepath ++ ": cyclic includes"