aeson-compat 0.3.6 → 0.3.7
raw patch · 6 files changed
+58/−269 lines, 6 filesdep +attoparsec-iso8601dep ~QuickCheckdep ~aesondep ~attoparsecPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: attoparsec-iso8601
Dependency ranges changed: QuickCheck, aeson, attoparsec, base, base-compat, bytestring, containers, exceptions, hashable, nats, quickcheck-instances, scientific, semigroups, tagged, text, time, time-locale-compat, unordered-containers, vector
API changes (from Hackage documentation)
- Data.Aeson.Compat: class GToEncoding arity (f :: * -> *)
- Data.Aeson.Compat: class GToJSON arity (f :: * -> *)
- Data.Aeson.Compat: gParseJSON :: Options -> FromArgs arity a -> Value -> Parser (f a)
- Data.Aeson.Compat: gToEncoding :: Options -> ToArgs Encoding arity a -> f a -> Encoding
- Data.Aeson.Compat: gToJSON :: Options -> ToArgs Value arity a -> f a -> Value
+ Data.Aeson.Compat: type GToEncoding = GToJSON Encoding
+ Data.Aeson.Compat: type GToJSON = GToJSON Value
- Data.Aeson.Compat: (.=) :: ToJSON v => Text -> v -> kv
+ Data.Aeson.Compat: (.=) :: (KeyValue kv, ToJSON v) => Text -> v -> kv
- Data.Aeson.Compat: genericToEncoding :: (Generic a, GToEncoding Zero (Rep a)) => Options -> a -> Encoding
+ Data.Aeson.Compat: genericToEncoding :: (Generic a, GToJSON Encoding Zero (Rep a)) => Options -> a -> Encoding
- Data.Aeson.Compat: genericToJSON :: (Generic a, GToJSON Zero (Rep a)) => Options -> a -> Value
+ Data.Aeson.Compat: genericToJSON :: (Generic a, GToJSON Value Zero (Rep a)) => Options -> a -> Value
- Data.Aeson.Compat: parseJSON :: Value -> Parser a
+ Data.Aeson.Compat: parseJSON :: FromJSON a => Value -> Parser a
- Data.Aeson.Compat: parseJSONList :: Value -> Parser [a]
+ Data.Aeson.Compat: parseJSONList :: FromJSON a => Value -> Parser [a]
- Data.Aeson.Compat: toEncoding :: a -> Encoding
+ Data.Aeson.Compat: toEncoding :: ToJSON a => a -> Encoding
- Data.Aeson.Compat: toEncodingList :: [a] -> Encoding
+ Data.Aeson.Compat: toEncodingList :: ToJSON a => [a] -> Encoding
- Data.Aeson.Compat: toJSON :: a -> Value
+ Data.Aeson.Compat: toJSON :: ToJSON a => a -> Value
- Data.Aeson.Compat: toJSONList :: [a] -> Value
+ Data.Aeson.Compat: toJSONList :: ToJSON a => [a] -> Value
Files
- CHANGELOG.md +5/−0
- aeson-compat.cabal +34/−34
- src/Data/Aeson/Compat.hs +16/−10
- src/Data/Aeson/Compat/Time.hs +0/−161
- test/Orphans.hs +0/−62
- test/Tests.hs +3/−2
CHANGELOG.md view
@@ -1,3 +1,8 @@+# 0.3.7++- Use `attoparsec-iso8601` time parsers.+- Don't export `GToJSON` etc. members.+ # 0.3.6 - Fix accidental removal of `AesonException` export
aeson-compat.cabal view
@@ -1,9 +1,5 @@--- This file has been generated from package.yaml by hpack version 0.14.1.------ see: https://github.com/sol/hpack- name: aeson-compat-version: 0.3.6+version: 0.3.7 synopsis: Compatibility layer for aeson description: Compatibility layer for @aeson@ category: Web@@ -13,9 +9,16 @@ 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.3, GHC==8.0.1 build-type: Simple cabal-version: >= 1.10+tested-with:+ GHC==7.6.3,+ GHC==7.8.4,+ GHC==7.10.2,+ GHC==7.10.3,+ GHC==8.0.1,+ GHC==8.0.2,+ GHC==8.2.1 extra-source-files: CHANGELOG.md@@ -30,10 +33,11 @@ src ghc-options: -Wall build-depends:- base >=4.6 && <4.10+ base >=4.6 && <4.11 , base-compat >=0.6.0 && <0.10- , aeson >=0.7.0.6 && <1.1+ , aeson >=0.7.0.6 && <1.3 , attoparsec >=0.12 && <0.14+ , attoparsec-iso8601 >=1.0.0.0 && <1.1 , bytestring >=0.10 && <0.11 , containers >=0.5 && <0.6 , exceptions >=0.8 && <0.9@@ -41,16 +45,14 @@ , nats >=1 && <1.2 , scientific >=0.3 && <0.4 , text >=1.2 && <1.3- , time >=1.4.2 && <1.7+ , time >=1.4.2 && <1.9 , time-locale-compat >=0.1.0.1 && <0.2 , unordered-containers >=0.2 && <0.3- , vector >=0.10 && <0.12+ , vector >=0.10 && <0.13 , semigroups >=0.16.2.2 && <0.19 , tagged >=0.7.3 && <0.9 exposed-modules: Data.Aeson.Compat- other-modules:- Data.Aeson.Compat.Time default-language: Haskell2010 test-suite aeson-compat-test@@ -60,30 +62,28 @@ test ghc-options: -Wall build-depends:- base >=4.6 && <4.10- , base-compat >=0.6.0 && <0.10- , aeson >=0.7.0.6 && <1.1- , attoparsec >=0.12 && <0.14- , bytestring >=0.10 && <0.11- , containers >=0.5 && <0.6- , exceptions >=0.8 && <0.9- , hashable >=1.2 && <1.3- , nats >=1 && <1.2- , scientific >=0.3 && <0.4- , text >=1.2 && <1.3- , time >=1.4.2 && <1.7- , time-locale-compat >=0.1.0.1 && <0.2- , unordered-containers >=0.2 && <0.3- , vector >=0.10 && <0.12- , semigroups >=0.16.2.2 && <0.19- , tagged >=0.7.3 && <0.9+ base+ , base-compat+ , aeson+ , attoparsec+ , bytestring+ , containers+ , exceptions+ , hashable+ , nats+ , scientific+ , text+ , time+ , time-locale-compat+ , unordered-containers+ , vector+ , semigroups+ , tagged , aeson-compat- , base-orphans >=0.4.5 && <0.6+ , base-orphans >=0.4.5 && <0.7 , tasty >=0.10 && <0.12 , tasty-hunit >=0.9 && <0.10 , tasty-quickcheck >=0.8 && <0.9- , QuickCheck >=2.7.6 && <2.9.2- , quickcheck-instances >=0.3 && <0.3.13- other-modules:- Orphans+ , QuickCheck >=2.9.2 && <2.9.3+ , quickcheck-instances >=0.3.14 && <0.3.15 default-language: Haskell2010
src/Data/Aeson/Compat.hs view
@@ -14,8 +14,8 @@ -- -- * '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@+-- * '.:?' works as in @aeson <0.10 || >=0.11@+-- * '.:!' works as in @aeson <0.10 || >=0.11@ and as '.:?' did in @aeson ==0.10.*@ -- * Orphan instances 'FromJSON' 'Day' and 'FromJSON' 'LocalTime' for @aeson <0.10@ -- * 'Encoding' related functionality is not added. It's present only with @aeson >=0.10@ --@@ -54,11 +54,11 @@ (.=), #endif -- ** Generic JSON classes and options- GFromJSON(..),- GToJSON(..),+ GFromJSON,+ GToJSON, #if MIN_VERSION_aeson(0,11,0) -- GToEncoding is introduced in 0.11.0.0- GToEncoding(..),+ GToEncoding, #endif genericToJSON, #if MIN_VERSION_aeson(0,10,0)@@ -123,7 +123,8 @@ #if !MIN_VERSION_aeson(0,10,0) import Data.Time (Day, LocalTime, formatTime, NominalDiffTime) import Data.Time.Locale.Compat (defaultTimeLocale)-import qualified Data.Aeson.Compat.Time as CompatTime+import qualified Data.Attoparsec.Text as Atto+import qualified Data.Attoparsec.Time as CompatTime #endif #if !(MIN_VERSION_aeson(0,11,0) && MIN_VERSION_base(4,8,0))@@ -270,11 +271,16 @@ ----------------------------------------------------------------------- #if !MIN_VERSION_aeson(0,10,0)+attoRun :: Atto.Parser a -> Text -> Parser a+attoRun p t = case Atto.parseOnly (p <* Atto.endOfInput) t of+ Left err -> fail $ "could not parse date: " ++ err+ Right r -> return r+ instance FromJSON Day where- parseJSON = withText "Day" (CompatTime.run CompatTime.day)+ parseJSON = withText "Day" (attoRun CompatTime.day) instance FromJSON LocalTime where- parseJSON = withText "LocalTime" (CompatTime.run CompatTime.localTime)+ parseJSON = withText "LocalTime" (attoRun CompatTime.localTime) instance ToJSON Day where toJSON = toJSON . T.pack . formatTime defaultTimeLocale "%F"@@ -285,12 +291,12 @@ instance ToJSON NominalDiffTime where toJSON = Number . realToFrac {-# INLINE toJSON #-}- + #if MIN_VERSION_aeson(0,10,0) toEncoding = Encoding . E.number . realToFrac {-# INLINE toEncoding #-} #endif- + -- | /WARNING:/ Only parse lengths of time from trusted input -- since an attacker could easily fill up the memory of the target -- system by specifying a scientific number with a big exponent like
− src/Data/Aeson/Compat/Time.hs
@@ -1,161 +0,0 @@-{-# LANGUAGE BangPatterns, CPP, ScopedTypeVariables #-}---- |--- Module: Data.Aeson.Compat.Time (Data.Aeson.Parser.Time)--- Copyright: (c) 2015 Bryan O'Sullivan--- License: BSD3--- Maintainer: Bryan O'Sullivan <bos@serpentine.com>--- Stability: experimental--- Portability: portable------ Parsers for parsing dates and times.--module Data.Aeson.Compat.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- m <- char ':' *> twoDigits- s <- option 0 (char ':' *> 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[.SSS]]@.--- The space may be replaced with a @T@. The number of seconds is optional--- and 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 Z@--- @YYYY-MM-DD HH:MM:SS Z@--- @YYYY-MM-DD HH:MM:SS.SSS 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 ""
− test/Orphans.hs
@@ -1,62 +0,0 @@-{-# LANGUAGE CPP #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}-module Orphans where--import Prelude ()-import Prelude.Compat--import Test.Tasty.QuickCheck--import Data.Orphans ()-import Test.QuickCheck.Instances ()--#if !MIN_VERSION_quickcheck_instances(0,3,12)-import Data.Vector as V-#endif--#if !(MIN_VERSION_QuickCheck(2,8,0) && MIN_VERSION_base(4,8,0)) && !(MIN_VERSION_QuickCheck(2,9,0))-import Numeric.Natural (Natural)-#endif--#if !MIN_VERSION_QuickCheck(2,9,0)-import Control.Applicative (Const (..))-import Data.List.NonEmpty (NonEmpty (..))-import Data.Version (Version (..))-#endif--import Data.Proxy (Proxy (..))-import Data.Tagged (Tagged (..))--#if !MIN_VERSION_quickcheck_instances(0,3,12)-instance Arbitrary a => Arbitrary (Vector a) where- arbitrary = V.fromList <$> arbitrary- shrink = fmap V.fromList . shrink . V.toList-#endif--#if !(MIN_VERSION_QuickCheck(2,8,0) && MIN_VERSION_base(4,8,0)) && !(MIN_VERSION_QuickCheck(2,9,0))-instance Arbitrary Natural where- arbitrary = fmap (fromInteger . abs) arbitrary-#endif--#if !MIN_VERSION_QuickCheck(2,9,0)-instance Arbitrary a => Arbitrary (NonEmpty a) where- arbitrary = do- x <- arbitrary- xs <- arbitrary- return (x :| xs)--instance Arbitrary a => Arbitrary (Const a b) where- arbitrary = fmap Const arbitrary--instance Arbitrary Version where- arbitrary = do- x <- fmap abs arbitrary- xs <- (fmap . fmap) abs arbitrary- return $ Version (x : xs) []-#endif--instance Arbitrary a => Arbitrary (Tagged t a) where- arbitrary = fmap Tagged arbitrary--instance Arbitrary (Proxy a) where- arbitrary = return Proxy
test/Tests.hs view
@@ -7,7 +7,7 @@ import Control.Applicative (Const) import Data.List.NonEmpty (NonEmpty)-import Data.Proxy (Proxy)+import Data.Proxy (Proxy (..)) import Data.Tagged (Tagged) import Data.Text (Text) import Data.Time (Day, LocalTime, NominalDiffTime)@@ -23,7 +23,8 @@ import Data.Aeson.Compat -import Orphans ()+instance Arbitrary (Proxy a) where+ arbitrary = pure Proxy main :: IO () main = defaultMain $ testGroup "Tests"