packages feed

aeson-extra 0.2.3.0 → 0.3.0.0

raw patch · 7 files changed

+67/−429 lines, 7 filesdep +aeson-compatdep +base-compatdep +parsecdep −attoparsecdep ~time

Dependencies added: aeson-compat, base-compat, parsec, time-parsers

Dependencies removed: attoparsec

Dependency ranges changed: time

Files

CHANGELOG.md view
@@ -1,3 +1,9 @@+# 0.3.0.0 (2015-12-25)++- `Data.Time.TH` moved to [`time-parsers`](http://hackage.haskell.org/package/time-parsers)+- `Data.Aeson.Compat` moved to [`aeson-compat`](http://hackage.haskell.org/package/aeson-compat)+- The modules aren't re-exported, as that would require `Cabal >= 1.21` restriction+ # 0.2.3.0 (2015-12-08)  - `Data.Time.TH` module with `mkUTCTime`
README.md view
@@ -2,8 +2,9 @@  [![Build Status](https://travis-ci.org/phadej/aeson-extra.svg?branch=master)](https://travis-ci.org/phadej/aeson-extra) [![Hackage](https://img.shields.io/hackage/v/aeson-extra.svg)](http://hackage.haskell.org/package/aeson-extra)--The package motivation is twofold:+[![Stackage LTS 3](http://stackage.org/package/monad-http/badge/lts-3)](http://stackage.org/lts-3/package/aeson-extra)+[![Stackage Nightly](http://stackage.org/package/monad-http/badge/nightly)](http://stackage.org/nightly/package/aeson-extra) -- provide compatibility layer for [`aeson`](http://hackage.haskell.org/package/aeson)-- provide extra combinators+Package provides extra funcitonality on top of+[`aeson`](https://hackage.haskell.org/package/aeson) and+[`aeson-compat`](https://hackage.haskell.org/package/aeson-compat)
aeson-extra.cabal view
@@ -3,13 +3,9 @@ -- see: https://github.com/sol/hpack  name:           aeson-extra-version:        0.2.3.0+version:        0.3.0.0 synopsis:       Extra goodies for aeson-description:    The package motivation is twofold:-                .-                * provide compatibility layer for @aeson@-                .-                * provide extra combinators+description:    Package provides extra funcitonality on top of @aeson@ and @aeson-compat@ category:       Web homepage:       https://github.com/phadej/aeson-extra#readme bug-reports:    https://github.com/phadej/aeson-extra/issues@@ -17,7 +13,7 @@ maintainer:     Oleg Grenrus <oleg.grenrus@iki.fi> license:        BSD3 license-file:   LICENSE-tested-with:    GHC==7.6.3, GHC==7.8.4, GHC==7.10.2+tested-with:    GHC==7.6.3, GHC==7.8.4, GHC==7.10.3 build-type:     Simple cabal-version:  >= 1.10 @@ -35,24 +31,23 @@   ghc-options: -Wall   build-depends:       base                     >=4.6  && <4.9+    , base-compat              >=0.6.0   && <0.9     , aeson                    >=0.7.0.6 && <0.11-    , attoparsec               >=0.12 && <0.14+    , aeson-compat             >=0.3.0.0 && <0.4     , bytestring               >=0.10 && <0.11     , containers               >=0.5  && <0.6     , exceptions               >=0.8  && <0.9     , hashable                 >=1.2  && <1.3+    , parsec                   >=3.1.9 && <3.2     , scientific               >=0.3  && <0.4     , template-haskell         >=2.8  && <2.11     , text                     >=1.2  && <1.3-    , time                     >=1.4.2 && <1.6+    , time                     >=1.4.2 && <1.7+    , time-parsers             >=0.1.0.0 && <0.2     , unordered-containers     >=0.2  && <0.3     , vector                   >=0.10 && <0.12   exposed-modules:-      Data.Aeson.Compat       Data.Aeson.Extra-      Data.Time.TH-  other-modules:-      Data.Aeson.Extra.Time   default-language: Haskell2010  test-suite aeson-extra-test@@ -63,16 +58,19 @@   ghc-options: -Wall   build-depends:       base                     >=4.6  && <4.9+    , base-compat              >=0.6.0   && <0.9     , aeson                    >=0.7.0.6 && <0.11-    , attoparsec               >=0.12 && <0.14+    , aeson-compat             >=0.3.0.0 && <0.4     , bytestring               >=0.10 && <0.11     , containers               >=0.5  && <0.6     , exceptions               >=0.8  && <0.9     , hashable                 >=1.2  && <1.3+    , parsec                   >=3.1.9 && <3.2     , scientific               >=0.3  && <0.4     , template-haskell         >=2.8  && <2.11     , text                     >=1.2  && <1.3-    , time                     >=1.4.2 && <1.6+    , time                     >=1.4.2 && <1.7+    , time-parsers             >=0.1.0.0 && <0.2     , unordered-containers     >=0.2  && <0.3     , vector                   >=0.10 && <0.12     , aeson-extra
− src/Data/Aeson/Compat.hs
@@ -1,194 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DeriveDataTypeable #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}--------------------------------------------------------------------------------- |--- Module      :  Data.Aeson.Compat--- Copyright   :  (C) 2015 Oleg Grenrus--- License     :  BSD3--- Maintainer  :  Oleg Grenrus <oleg.grenrus@iki.fi>------ Compatibility notices------   * 'decode' etc. work as in @aeson >=0.9@---   * but it is generalised to work in any 'MonadThrow' (that is extra)---   * '.:?' works as in @aeson <0.10@---   * '.:!' works as '.:?' in @aeson ==0.10@-module Data.Aeson.Compat (-  -- * Generic decoding functions-  decode,-  decode',-  decodeStrict,-  decodeStrict',-  AesonException(..),-  -- * Either decoding functions-  eitherDecode, eitherDecode', eitherDecodeStrict, eitherDecodeStrict',-  -- * Operators-  (.:?), (.:!),-  -- * Re-exports-  -- | Original 'Data.Aeson..:?' operator is not re-exported-  module Data.Aeson,-  ) where--#if !MIN_VERSION_base(4,8,0)-import           Control.Applicative-#endif--#if MIN_VERSION_aeson(0,10,0)-import           Data.Monoid-#endif--import           Data.Aeson hiding-  ((.:?), decode, decode', decodeStrict, decodeStrict'-#if !MIN_VERSION_aeson (0,9,0)-  , eitherDecode, eitherDecode', eitherDecodeStrict, eitherDecodeStrict'-#endif-  )--#if !MIN_VERSION_aeson (0,9,0)-import           Data.Aeson.Parser (value, value')-import qualified Data.Attoparsec.ByteString as A-import qualified Data.Attoparsec.ByteString.Char8 as A (skipSpace)-import qualified Data.Attoparsec.Lazy as L-#endif--import           Control.Monad.Catch-import           Data.Aeson.Types hiding ((.:?))-import           Data.ByteString as B-import           Data.ByteString.Lazy as L-import qualified Data.HashMap.Strict as H-import           Data.Text as T-import           Data.Typeable (Typeable)--#if !MIN_VERSION_aeson(0,10,0)-import           Data.Time (Day, LocalTime)-import qualified Data.Aeson.Extra.Time as ExtraTime-#endif---- | Exception thrown by 'decode' - family of functions in this module.-newtype AesonException = AesonException String-  deriving (Show, Typeable)--instance Exception AesonException--eitherAesonExc :: (MonadThrow m) => Either String a -> m a-eitherAesonExc (Left err) = throwM (AesonException err)-eitherAesonExc (Right x)  = return x---- | Like original 'Data.Aeson.decode' but in arbitrary 'MonadThrow'.------ Parse a top-level JSON value, i.e. also strings, numbers etc.-decode :: (FromJSON a, MonadThrow m) => L.ByteString -> m a-decode = eitherAesonExc . eitherDecode---- | Like original 'Data.Aeson.decode'' but in arbitrary 'MonadThrow'.-decode' :: (FromJSON a, MonadThrow m) => L.ByteString -> m a-decode' = eitherAesonExc . eitherDecode'---- | Like original 'Data.Aeson.decodeStrict' but in arbitrary 'MonadThrow'.-decodeStrict :: (FromJSON a, MonadThrow m) => B.ByteString -> m a-decodeStrict = eitherAesonExc . eitherDecodeStrict---- | Like original 'Data.Aeson.decodeStrict'' but in arbitrary 'MonadThrow'.-decodeStrict' :: (FromJSON a, MonadThrow m) => B.ByteString -> m a-decodeStrict' = eitherAesonExc . eitherDecodeStrict'---- | Retrieve the value associated with the given key of an 'Object'.--- The result is 'Nothing' if the key is not present, or 'empty' if--- the value cannot be converted to the desired type.------ This accessor is most useful if the key and value can be absent--- from an object without affecting its validity.  If the key and--- value are mandatory, use '.:' instead.------ This operator is consistent in 'aeson >=0.8 && <0.11'-(.:?) :: (FromJSON a) => Object -> Text -> Parser (Maybe a)-obj .:? key = case H.lookup key obj of-                Nothing -> pure Nothing-                Just v  ->-#if MIN_VERSION_aeson(0,10,0)-                  modifyFailure addKeyName $ parseJSON v -- <?> Key key-  where-    addKeyName = (("failed to parse field " <> T.unpack key <> ": ") <>)-#else-                  parseJSON v-#endif-{-# INLINE (.:?) #-}---- | Like '.:?', but the resulting parser will fail,--- if the key is present but is 'Null'.-(.:!) :: (FromJSON a) => Object -> Text -> Parser (Maybe a)-obj .:! key = case H.lookup key obj of-                Nothing -> pure Nothing-                Just v  ->-#if MIN_VERSION_aeson(0,10,0)-                  modifyFailure addKeyName $ Just <$> parseJSON v -- <?> Key key-  where-    addKeyName = (("failed to parse field " <> T.unpack key <> ": ") <>)-#else-                  Just <$> parseJSON v-#endif-{-# INLINE (.:!) #-}--#if !MIN_VERSION_aeson(0,9,0)--- From Parser.Internal---- | Parse a top-level JSON value followed by optional whitespace and--- end-of-input.  See also: 'json'.-jsonEOF :: A.Parser Value-jsonEOF = value <* A.skipSpace <* A.endOfInput---- | Parse a top-level JSON value followed by optional whitespace and--- end-of-input.  See also: 'json''.-jsonEOF' :: A.Parser Value-jsonEOF' = value' <* A.skipSpace <* A.endOfInput---- | Like 'decode' but returns an error message when decoding fails.-eitherDecode :: (FromJSON a) => L.ByteString -> Either String a-eitherDecode = eitherDecodeWith jsonEOF fromJSON-{-# INLINE eitherDecode #-}---- | Like 'decodeStrict' but returns an error message when decoding fails.-eitherDecodeStrict :: (FromJSON a) => B.ByteString -> Either String a-eitherDecodeStrict = eitherDecodeStrictWith jsonEOF fromJSON-{-# INLINE eitherDecodeStrict #-}---- | Like 'decode'' but returns an error message when decoding fails.-eitherDecode' :: (FromJSON a) => L.ByteString -> Either String a-eitherDecode' = eitherDecodeWith jsonEOF' fromJSON-{-# INLINE eitherDecode' #-}---- | Like 'decodeStrict'' but returns an error message when decoding fails.-eitherDecodeStrict' :: (FromJSON a) => B.ByteString -> Either String a-eitherDecodeStrict' = eitherDecodeStrictWith jsonEOF' fromJSON-{-# INLINE eitherDecodeStrict' #-}--eitherDecodeWith :: L.Parser Value -> (Value -> Result a) -> L.ByteString-                 -> Either String a-eitherDecodeWith p to s =-    case L.parse p s of-      L.Done _ v -> case to v of-                      Success a -> Right a-                      Error msg -> Left msg-      L.Fail _ _ msg -> Left msg-{-# INLINE eitherDecodeWith #-}--eitherDecodeStrictWith :: A.Parser Value -> (Value -> Result a) -> B.ByteString-                       -> Either String a-eitherDecodeStrictWith p to s =-    case either Error to (A.parseOnly p s) of-      Success a -> Right a-      Error msg -> Left msg-{-# INLINE eitherDecodeStrictWith #-}--#endif--#if !MIN_VERSION_aeson(0,10,0)--- | /Since: aeson-extra-0.2.2.0/-instance FromJSON Day where-  parseJSON = withText "Day" (ExtraTime.run ExtraTime.day)---- | /Since: aeson-extra-0.2.2.0/-instance FromJSON LocalTime where-  parseJSON = withText "LocalTime" (ExtraTime.run ExtraTime.localTime)-#endif
src/Data/Aeson/Extra.hs view
@@ -1,14 +1,14 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE DeriveFoldable #-}-{-# LANGUAGE DeriveTraversable #-}-{-# LANGUAGE KindSignatures #-}-{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE CPP                    #-}+{-# LANGUAGE DataKinds              #-}+{-# LANGUAGE DeriveDataTypeable     #-}+{-# LANGUAGE DeriveFoldable         #-}+{-# LANGUAGE DeriveFunctor          #-}+{-# LANGUAGE DeriveTraversable      #-}+{-# LANGUAGE FlexibleInstances      #-} {-# LANGUAGE FunctionalDependencies #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE KindSignatures         #-}+{-# LANGUAGE ScopedTypeVariables    #-}+{-# LANGUAGE UndecidableInstances   #-} ----------------------------------------------------------------------------- -- | -- Module      :  Data.Aeson.Extra@@ -46,36 +46,39 @@   module Data.Aeson.Compat,   ) where -#if !MIN_VERSION_base(4,8,0)-import           Data.Foldable (Foldable)-import           Data.Traversable (Traversable, traverse)-#endif+import Prelude        ()+import Prelude.Compat -import           Control.Applicative-import           Data.Monoid-import qualified Data.ByteString as BS+import Control.Applicative (Alternative(..))+import Data.Aeson.Compat+import Data.Aeson.Types    hiding ((.:?))+import Data.Hashable       (Hashable)+import Data.Monoid+import Data.Text           (Text)+import Data.Time           (UTCTime, ZonedTime)++import qualified Data.ByteString      as BS import qualified Data.ByteString.Lazy as LBS-import           Data.Aeson.Compat-import           Data.Aeson.Types hiding ((.:?))-import qualified Data.Foldable as Foldable-import qualified Data.HashMap.Strict as H-import           Data.Hashable (Hashable)-import qualified Data.Map as Map-import           Data.Text as T-import qualified Data.Text.Lazy as TL-import           Data.Time (UTCTime, ZonedTime)-import qualified Data.Text.Read as T+import qualified Data.Foldable        as Foldable+import qualified Data.HashMap.Strict  as H+import qualified Data.Map             as Map+import qualified Data.Text            as T+import qualified Data.Text.Lazy       as TL+import qualified Data.Text.Read       as T  #if MIN_VERSION_base(4,7,0)-import           Data.Proxy-import           GHC.TypeLits+import Data.Proxy+import GHC.TypeLits #endif  #if !MIN_VERSION_aeson (0,10,0)-import qualified Data.Aeson.Extra.Time as ExtraTime+import qualified Text.Parsec       as Parsec+import qualified Data.Time.Parsers as TimeParsers #endif  -- | Like 'encode', but produces strict 'BS.ByteString'.+--+-- /Since: 0.2.3.0/ encodeStrict :: ToJSON a => a -> BS.ByteString encodeStrict = LBS.toStrict . encode @@ -293,7 +296,7 @@ #if MIN_VERSION_aeson (0,10,0)   parseJSON = fmap U . parseJSON #else-  parseJSON = withText "UTCTime" (fmap U . ExtraTime.run ExtraTime.utcTime)+  parseJSON = withText "UTCTime" (fmap U . run TimeParsers.utcTime) #endif  -- | A type to parse 'ZonedTime'@@ -312,5 +315,13 @@ #if MIN_VERSION_aeson (0,10,0)   parseJSON = fmap Z . parseJSON #else-  parseJSON = withText "ZonedTime" (fmap Z . ExtraTime.run ExtraTime.zonedTime)+  parseJSON = withText "ZonedTime" (fmap Z . run TimeParsers.zonedTime)+#endif++#if !MIN_VERSION_aeson (0,10,0)+-- | Run a 'parsers' parser as an aeson parser.+run :: Parsec.Parsec Text () a -> Text -> Parser a+run p t = case Parsec.parse (p <* Parsec.eof) "" t of+            Left err -> fail $ "could not parse date: " ++ show err+            Right r  -> return r #endif
− src/Data/Aeson/Extra/Time.hs
@@ -1,159 +0,0 @@-{-# LANGUAGE BangPatterns, CPP, ScopedTypeVariables #-}---- |--- Module:      Data.Aeson.Extra.Time (Data.Aeson.Parser.Time)--- Copyright:   (c) 2015 Bryan O'Sullivan--- License:     Apache--- Maintainer:  Bryan O'Sullivan <bos@serpentine.com>--- Stability:   experimental--- Portability: portable------ Parsers for parsing dates and times.--module Data.Aeson.Extra.Time-    (-      run-    , day-    , localTime-    , timeOfDay-    , timeZone-    , utcTime-    , zonedTime-    ) where--import Control.Monad (when, void)-import Data.Attoparsec.Text as A-import Data.Bits ((.&.))-import Data.Char (isDigit, ord)-import Data.Fixed (Pico)-import Data.Int (Int64)-import Data.Maybe (fromMaybe)-import Data.Text (Text)-import Data.Time.Calendar (Day, fromGregorianValid)-import Data.Time.Clock (UTCTime(..))-import qualified Data.Aeson.Types as Aeson-import qualified Data.Text as T-import qualified Data.Time.LocalTime as Local--#if !MIN_VERSION_base(4,8,0)-import Control.Applicative ((<$>), (<*>), (<*), (*>))-#endif---- from Data.Aeson.Internal.Time-import Unsafe.Coerce (unsafeCoerce)--toPico :: Integer -> Pico-toPico = unsafeCoerce---- | Run an attoparsec parser as an aeson parser.-run :: Parser a -> Text -> Aeson.Parser a-run p t = case A.parseOnly (p <* endOfInput) t of-            Left err -> fail $ "could not parse date: " ++ err-            Right r  -> return r---- | Parse a date of the form @YYYY-MM-DD@.-day :: Parser Day-day = do-  y <- decimal <* char '-'-  m <- twoDigits <* char '-'-  d <- twoDigits-  maybe (fail "invalid date") return (fromGregorianValid y m d)---- | Parse a two-digit integer (e.g. day of month, hour).-twoDigits :: Parser Int-twoDigits = do-  a <- digit-  b <- digit-  let c2d c = ord c .&. 15-  return $! c2d a * 10 + c2d b---- | Parse a time of the form @HH:MM:SS[.SSS]@.-timeOfDay :: Parser Local.TimeOfDay-timeOfDay = do-  h <- twoDigits <* char ':'-  m <- twoDigits <* char ':'-  s <- seconds-  if h < 24 && m < 60 && s < 61-    then return (Local.TimeOfDay h m s)-    else fail "invalid time"--data T = T {-# UNPACK #-} !Int {-# UNPACK #-} !Int64---- | Parse a count of seconds, with the integer part being two digits--- long.-seconds :: Parser Pico-seconds = do-  real <- twoDigits-  mc <- peekChar-  case mc of-    Just '.' -> do-      t <- anyChar *> takeWhile1 isDigit-      return $! parsePicos real t-    _ -> return $! fromIntegral real- where-  parsePicos a0 t = toPico (fromIntegral (t' * 10^n))-    where T n t'  = T.foldl' step (T 12 (fromIntegral a0)) t-          step ma@(T m a) c-              | m <= 0    = ma-              | otherwise = T (m-1) (10 * a + fromIntegral (ord c) .&. 15)---- | Parse a time zone, and return 'Nothing' if the offset from UTC is--- zero. (This makes some speedups possible.)-timeZone :: Parser (Maybe Local.TimeZone)-timeZone = do-  let maybeSkip c = do ch <- peekChar'; when (ch == c) (void anyChar)-  maybeSkip ' '-  ch <- satisfy $ \c -> c == 'Z' || c == '+' || c == '-'-  if ch == 'Z'-    then return Nothing-    else do-      h <- twoDigits-      mm <- peekChar-      m <- case mm of-             Just ':'           -> anyChar *> twoDigits-             Just d | isDigit d -> twoDigits-             _                  -> return 0-      let off | ch == '-' = negate off0-              | otherwise = off0-          off0 = h * 60 + m-      case undefined of-        _   | off == 0 ->-              return Nothing-            | off < -720 || off > 840 || m > 59 ->-              fail "invalid time zone offset"-            | otherwise ->-              let !tz = Local.minutesToTimeZone off-              in return (Just tz)---- | Parse a date and time, of the form @YYYY-MM-DD HH:MM:SS@.--- The space may be replaced with a @T@.  The number of seconds may be--- followed by a fractional component.-localTime :: Parser Local.LocalTime-localTime = Local.LocalTime <$> day <* daySep <*> timeOfDay-  where daySep = satisfy (\c -> c == 'T' || c == ' ')---- | Behaves as 'zonedTime', but converts any time zone offset into a--- UTC time.-utcTime :: Parser UTCTime-utcTime = do-  lt@(Local.LocalTime d t) <- localTime-  mtz <- timeZone-  case mtz of-    Nothing -> let !tt = Local.timeOfDayToTime t-               in return (UTCTime d tt)-    Just tz -> return $! Local.localTimeToUTC tz lt---- | Parse a date with time zone info. Acceptable formats:------ @YYYY-MM-DD HH:MM:SS Z@------ The first space may instead be a @T@, and the second space is--- optional.  The @Z@ represents UTC.  The @Z@ may be replaced with a--- time zone offset of the form @+0000@ or @-08:00@, where the first--- two digits are hours, the @:@ is optional and the second two digits--- (also optional) are minutes.-zonedTime :: Parser Local.ZonedTime-zonedTime = Local.ZonedTime <$> localTime <*> (fromMaybe utc <$> timeZone)--utc :: Local.TimeZone-utc = Local.TimeZone 0 False ""
− src/Data/Time/TH.hs
@@ -1,25 +0,0 @@-{-# LANGUAGE TemplateHaskell #-}--- | Template Haskell extras for `Data.Time`.------ This module is in a wrong place, but it reuses the same parser as aeson parser.-module Data.Time.TH (mkUTCTime) where--import Control.Applicative ((<*))-import Data.Time (UTCTime(..), Day(..))-import qualified Data.Text as T-import qualified Data.Attoparsec.Text as A-import Language.Haskell.TH-import Data.Aeson.Extra.Time (utcTime)---- | Make  a 'UTCTime'. Accepts the same strings as aeson `UTCTime` parser accepts.------ > t :: UTCTime--- > t = $(mkUTCTime "2014-05-12 00:02:03.456000Z")-mkUTCTime :: String -> Q Exp-mkUTCTime s =-    case A.parseOnly (utcTime <* A.endOfInput) (T.pack s) of-        Left err -> fail $ "could not parse UTCTime: " ++ err-        Right (UTCTime (ModifiedJulianDay d) dt) ->-            [| UTCTime (ModifiedJulianDay $(d')) $(dt') :: UTCTime |]-          where d'  = return $ LitE $ integerL d-                dt' = return $ LitE $ rationalL $ toRational dt