packages feed

instant-aeson (empty) → 0.1

raw patch · 7 files changed

+423/−0 lines, 7 filesdep +aesondep +basedep +instant-aesonsetup-changed

Dependencies added: aeson, base, instant-aeson, instant-generics, tasty, tasty-quickcheck

Files

+ CHANGELOG.md view
@@ -0,0 +1,3 @@+# Version 0.1++* First release
+ LICENSE.txt view
@@ -0,0 +1,30 @@+Copyright (c) 2015, Renzo Carbonara++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 Renzo Carbonara 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 @@+`instant-aeson` allows you to generically derive JSON representations (from the+`aeson` package) for your types using `instant-generics`, which in particular,+supports deriving generic representations for GADTs.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ instant-aeson.cabal view
@@ -0,0 +1,38 @@+name:                instant-aeson+version:             0.1+author:              Renzo Carbonara+maintainer:          renzo@carbonara.com.ar+copyright:           Renzo Carbonara 2015+license:             BSD3+license-file:        LICENSE.txt+category:            Web+build-type:          Simple+extra-source-files:  README.md CHANGELOG.md+cabal-version:       >=1.18+synopsis:            Generic Aeson instances through instant-generics++library+  hs-source-dirs: src/lib+  default-language: Haskell2010+  exposed-modules:+      Generics.Instant.Functions.Aeson+  build-depends:+      aeson >=0.8 && <0.10+    , base >=4.8 && <4.9+    , instant-generics >=0.4 && <0.5+  ghcjs-options: -Wall -O3+  ghc-options: -Wall -O2+++test-suite tests+  default-language: Haskell2010+  type: exitcode-stdio-1.0+  hs-source-dirs: tests+  main-is: Main.hs+  build-depends:+      base >= 4 && < 5+    , aeson+    , tasty >= 0.10+    , tasty-quickcheck >= 0.8+    , instant-aeson+    , instant-generics
+ src/lib/Generics/Instant/Functions/Aeson.hs view
@@ -0,0 +1,204 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE PolyKinds #-}++module Generics.Instant.Functions.Aeson+  ( -- $defaults+    gtoJSON+  , gparseJSON+    -- * Internals+  , GToJSON+  , GFromJSON+    -- ** Even more internal+  , GSumFromJSON+  , GSumToJSON+  , GSumSize+  ) where++import qualified Data.Aeson as Ae+import qualified Data.Aeson.Types as Ae+import           Data.Bits+import           Generics.Instant++--------------------------------------------------------------------------------+-- $defaults+--+-- You can use 'gtoJSON' and 'gparseJSON' as your generic 'Ae.toJSON' and+-- 'Ae.parseJSON' implementations for any 'Representable' type as follows:+--+-- @+-- instance 'Ae.ToJSON' MyType where toJSON = 'gtoJSON'+-- instance 'Ae.FromJSON' MyType where parseJSON = 'gparseJSON'+-- @++gtoJSON :: (Representable a, GToJSON (Rep a)) => a -> Ae.Value+gtoJSON = \a -> gtoJSON' (from a)+{-# INLINABLE gtoJSON #-}++gparseJSON :: (Representable a, GFromJSON (Rep a)) => Ae.Value -> Ae.Parser a+gparseJSON = \v -> fmap to (gparseJSON' v)+{-# INLINABLE gparseJSON #-}++--------------------------------------------------------------------------------++class GFromJSON a where+  gparseJSON' :: Ae.Value -> Ae.Parser a++instance GFromJSON Z where+  gparseJSON' _ = fail+    "Generics.Instant.Functions.Aeson.GFromJSON Z gparseJSON' - impossible"++instance GFromJSON U where+  gparseJSON' v = U <$ (Ae.parseJSON v :: Ae.Parser ())+  {-# INLINABLE gparseJSON' #-}++instance GFromJSON a => GFromJSON (CEq c p p a) where+  gparseJSON' v = gparseJSON' v >>= \a -> return (C a)+  {-# INLINABLE gparseJSON' #-}++instance {-# OVERLAPPABLE #-} GFromJSON (CEq c p q a) where+  gparseJSON' _ = fail+    "Generics.Instant.Functions.Aeson.GFtomJSON (CEq c p q a) gparseJSON' - impossible"++instance Ae.FromJSON a => GFromJSON (Var a) where+  gparseJSON' v = Ae.parseJSON v >>= \a -> return (Var a)+  {-# INLINABLE gparseJSON' #-}++instance Ae.FromJSON a => GFromJSON (Rec a) where+  gparseJSON' v = Ae.parseJSON v >>= \a -> return (Rec a)+  {-# INLINABLE gparseJSON' #-}++instance (GFromJSON a, GFromJSON b) => GFromJSON (a :*: b) where+  gparseJSON' v = Ae.parseJSON v >>= \(va, vb) ->+                  gparseJSON' va >>= \a ->+                  gparseJSON' vb >>= \b ->+                  return (a :*: b)+  {-# INLINABLE gparseJSON' #-}++-- Borrowed from the "binary" package, which borrowed this from "cereal".+instance+  ( GSumFromJSON a, GSumFromJSON b, GSumSize a, GSumSize b+  , GFromJSON a, GFromJSON b+  ) => GFromJSON (a :+: b)+  where+    gparseJSON' v = Ae.parseJSON v >>= \(code, v') ->+      let size = unTagged (sumSize :: Tagged (a :+: b) Integer)+      in if code < size+         then gsumParseJSON code size v'+         else fail "Generics.Instant.Functions.Aeson.GFromJSON (a :+: b) - \+                   \Unknown constructor"+    {-# INLINABLE gparseJSON' #-}++--------------------------------------------------------------------------------++class GToJSON a where+  gtoJSON' :: a -> Ae.Value++instance GToJSON Z where+  gtoJSON' _ = error+    "Generics.Instant.Functions.Aeson.GToJSON Z gtoJSON' - impossible"++instance GToJSON U where+  gtoJSON' U = Ae.toJSON ()+  {-# INLINABLE gtoJSON' #-}++instance GToJSON a => GToJSON (CEq c p p a) where+  gtoJSON' (C a) = gtoJSON' a+  {-# INLINABLE gtoJSON' #-}++instance {-# OVERLAPPABLE #-} GToJSON a => GToJSON (CEq c p q a) where+  gtoJSON' (C a) = gtoJSON' a+  {-# INLINABLE gtoJSON' #-}++instance Ae.ToJSON a => GToJSON (Var a) where+  gtoJSON' (Var a) = Ae.toJSON a+  {-# INLINABLE gtoJSON' #-}++instance Ae.ToJSON a => GToJSON (Rec a) where+  gtoJSON' (Rec a) = Ae.toJSON a+  {-# INLINABLE gtoJSON' #-}++instance (GToJSON a, GToJSON b) => GToJSON (a :*: b) where+  gtoJSON' (a :*: b) = Ae.toJSON (gtoJSON' a, gtoJSON' b)+  {-# INLINABLE gtoJSON' #-}++-- Borrowed from the "binary" package, which borrowed this from "cereal".+instance+  ( GSumToJSON a, GSumToJSON b, GSumSize a, GSumSize b+  , GToJSON a, GToJSON b+  ) => GToJSON (a :+: b)+  where+    gtoJSON' x =+      let size = unTagged (sumSize :: Tagged (a :+: b) Integer)+      in gsumToJSON 0 size x+    {-# INLINABLE gtoJSON' #-}++--------------------------------------------------------------------------------++class GSumFromJSON a where+  gsumParseJSON :: Integer -> Integer -> Ae.Value -> Ae.Parser a++instance+  ( GSumFromJSON a, GSumFromJSON b, GFromJSON a, GFromJSON b+  ) => GSumFromJSON (a :+: b)+  where+    {-# INLINABLE gsumParseJSON #-}+    gsumParseJSON !code !size v+      | code < sizeL = L <$> gsumParseJSON code           sizeL v+      | otherwise    = R <$> gsumParseJSON (code - sizeL) sizeR v+      where+        sizeL = size `shiftR` 1+        sizeR = size - sizeL++instance GFromJSON a => GSumFromJSON (CEq c p p a) where+  gsumParseJSON _ _ v = gparseJSON' v+  {-# INLINABLE gsumParseJSON #-}++instance {-# OVERLAPPABLE #-} GSumFromJSON (CEq c p q a) where+  gsumParseJSON _ _ _ = fail+    "Generics.Instant.Functions.Aeson.GSumFromJSON (CEq c p q a) - impossible"++--------------------------------------------------------------------------------++class GSumToJSON a where+  gsumToJSON :: Integer -> Integer -> a -> Ae.Value++instance+  ( GSumToJSON a, GSumToJSON b, GToJSON a, GToJSON b+  ) => GSumToJSON (a :+: b)+  where+    {-# INLINABLE gsumToJSON #-}+    gsumToJSON !code !size x =+      let sizeL = size `shiftR` 1+          sizeR = size - sizeL+      in case x of+           L l -> gsumToJSON code           sizeL l+           R r -> gsumToJSON (code + sizeL) sizeR r++instance GToJSON a => GSumToJSON (CEq c p p a) where+  gsumToJSON !code _ ca = Ae.toJSON (code, gtoJSON' ca)+  {-# INLINABLE gsumToJSON #-}++instance {-# OVERLAPPABLE #-} GToJSON a => GSumToJSON (CEq c p q a) where+  gsumToJSON !code _ ca = Ae.toJSON (code, gtoJSON' ca)+  {-# INLINABLE gsumToJSON #-}++--------------------------------------------------------------------------------++class GSumSize a where+  sumSize :: Tagged a Integer++newtype Tagged s b = Tagged { unTagged :: b }++instance (GSumSize a, GSumSize b) => GSumSize (a :+: b) where+  {-# INLINABLE sumSize #-}+  sumSize = Tagged (unTagged (sumSize :: Tagged a Integer) ++                    unTagged (sumSize :: Tagged b Integer))++instance GSumSize (CEq c p q a) where+  {-# INLINABLE sumSize #-}+  sumSize = Tagged 1
+ tests/Main.hs view
@@ -0,0 +1,143 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++module Main where++import Test.Tasty+import Test.Tasty.QuickCheck as QC++import qualified Data.Aeson+import qualified Data.Aeson as Ae+import qualified Data.Aeson.Types as Ae+import Data.Proxy++import qualified Generics.Instant as GI+import qualified Generics.Instant.TH as GI+import Generics.Instant.Functions.Aeson (GFromJSON, gparseJSON, GToJSON, gtoJSON)++--------------------------------------------------------------------------------+-- orphans++data Unit_Unit_+instance GI.Constructor Unit_Unit_ where conName _ = "()"+instance GI.Representable () where+  type Rep () = GI.U+  from () = GI.U+  to GI.U = ()++GI.deriveAll ''Either++instance Arbitrary (Proxy a) where arbitrary = return Proxy+instance Ae.ToJSON (Proxy a) where toJSON _ = Ae.toJSON ()+instance Ae.FromJSON (Proxy a) where parseJSON v = Ae.parseJSON v >>= \() -> return Proxy++--------------------------------------------------------------------------------+-- many constructors, recursive++data ZZ = ZZ1 Int | ZZ2 Char | ZZ3 ZZ deriving (Show, Eq)+GI.deriveAll ''ZZ+instance Arbitrary ZZ where+  arbitrary = QC.oneof [ ZZ1 <$> QC.arbitrary+                       , ZZ2 <$> QC.arbitrary+                       , ZZ3 <$> QC.arbitrary ]++instance Ae.ToJSON ZZ where toJSON = gtoJSON+instance Ae.FromJSON ZZ where parseJSON = gparseJSON++--------------------------------------------------------------------------------+-- GADT++data Foo1 (a :: *) :: * where+  Foo1_1 :: Bool -> Foo1 Bool+  Foo1_2 :: a -> Foo1 a+GI.deriveAll ''Foo1+deriving instance Eq a => Eq (Foo1 a)+deriving instance Show a => Show (Foo1 a)+instance QC.Arbitrary (Foo1 Bool) where+  arbitrary = Foo1_1 <$> QC.arbitrary+instance {-# OVERLAPPABLE #-} (QC.Arbitrary a, GI.Representable a) => QC.Arbitrary (Foo1 a) where+  arbitrary = Foo1_2 <$> QC.arbitrary+++data Foo2 (a :: *) (b :: *) :: * where+  Foo2_1 :: a -> Char -> Foo2 a Int+  Foo2_2 :: a -> b -> Foo2 a b+GI.deriveAll ''Foo2+deriving instance (Eq a, Eq b) => Eq (Foo2 a b)+deriving instance (Show a, Show b) => Show (Foo2 a b)+instance (QC.Arbitrary a, GI.Representable a) => QC.Arbitrary (Foo2 a Int) where+  arbitrary = Foo2_1 <$> QC.arbitrary <*> QC.arbitrary+instance {-# OVERLAPPABLE #-} (QC.Arbitrary a, GI.Representable a, QC.Arbitrary b, GI.Representable b) => QC.Arbitrary (Foo2 a b) where+  arbitrary = Foo2_2 <$> QC.arbitrary <*> QC.arbitrary+++data Bar1 (a :: Bool) :: * where+  Bar1_1 :: Char -> Bar1 'True+  Bar1_2 :: Int -> Bar1 'False+GI.deriveAll ''Bar1+deriving instance Eq (Bar1 a)+deriving instance Show (Bar1 a)+instance QC.Arbitrary (Bar1 'True) where+  arbitrary = Bar1_1 <$> QC.arbitrary+instance QC.Arbitrary (Bar1 'False) where+  arbitrary = Bar1_2 <$> QC.arbitrary++data Bar2 (a :: k1) (b :: Bool) :: * where+  Bar2_1 :: Int -> Proxy a -> Bar2 a 'True+  Bar2_2 :: String -> Proxy a -> Bar2 a 'False+GI.deriveAll ''Bar2+deriving instance Eq (Bar2 a b)+deriving instance Show (Bar2 a b)+instance (QC.Arbitrary a) => QC.Arbitrary (Bar2 a 'True) where+  arbitrary = Bar2_1 <$> QC.arbitrary <*> QC.arbitrary+instance (QC.Arbitrary a) => QC.Arbitrary (Bar2 a 'False) where+  arbitrary = Bar2_2 <$> QC.arbitrary <*> QC.arbitrary+++--------------------------------------------------------------------------------++main :: IO ()+main = defaultMain tests++tests :: TestTree+tests = testGroup "QuickCheck - prop_IdJSON"+  [ -- QC.testProperty "()" (prop_IdJSON :: () -> Bool)+--   , QC.testProperty "Bool" (prop_IdJSON :: Bool -> Bool)+--   , QC.testProperty "Char" (prop_IdJSON :: Char -> Bool)+--   , QC.testProperty "Float" (prop_IdJSON :: Float -> Bool)+--   , QC.testProperty "Int" (prop_IdJSON :: Int -> Bool)+    QC.testProperty "Maybe ()" (prop_IdJSON :: Maybe () -> Bool)+  , QC.testProperty "Maybe Bool" (prop_IdJSON :: Maybe Bool -> Bool)+  , QC.testProperty "Maybe Char" (prop_IdJSON :: Maybe Char -> Bool)+  , QC.testProperty "Maybe Float" (prop_IdJSON :: Maybe Float -> Bool)+  , QC.testProperty "Maybe Int" (prop_IdJSON :: Maybe Int -> Bool)+  , QC.testProperty "Maybe ZZ" (prop_IdJSON :: Maybe ZZ -> Bool)+  , QC.testProperty "[()]" (prop_IdJSON :: [()] -> Bool)+  , QC.testProperty "[Bool]" (prop_IdJSON :: [Bool] -> Bool)+  , QC.testProperty "[Char]" (prop_IdJSON :: [Char] -> Bool)+  , QC.testProperty "[Float]" (prop_IdJSON :: [Float] -> Bool)+  , QC.testProperty "[Int]" (prop_IdJSON :: [Int] -> Bool)+  , QC.testProperty "[ZZ]" (prop_IdJSON :: [ZZ] -> Bool)+  , QC.testProperty "Foo1 Int" (prop_IdJSON :: Foo1 Int -> Bool)+  , QC.testProperty "Foo1 Char" (prop_IdJSON :: Foo1 Char -> Bool)+  , QC.testProperty "Foo2 Float Int" (prop_IdJSON :: Foo2 Float Int -> Bool)+  , QC.testProperty "Foo2 Bool Char" (prop_IdJSON :: Foo2 Bool Char -> Bool)+  , QC.testProperty "Bar1 'True" (prop_IdJSON :: Bar1 'True -> Bool)+  , QC.testProperty "Bar1 'False" (prop_IdJSON :: Bar1 'False -> Bool)+  , QC.testProperty "Bar2 Int 'True" (prop_IdJSON :: Bar2 Int 'True -> Bool)+  , QC.testProperty "Bar2 Float 'False" (prop_IdJSON :: Bar2 Float 'False -> Bool)+  ]++prop_IdJSON+  :: (Eq a, GI.Representable a, GToJSON (GI.Rep a), GFromJSON (GI.Rep a), Show a)+  => a -> Bool+prop_IdJSON a = maybe False (== a) $ Ae.parseMaybe gparseJSON (gtoJSON a)