-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'carymrobbins-options' into develop
* carymrobbins-options: Add changelog Improve error reporting Support customizing TH via options Add basic test suite
- Loading branch information
Showing
5 changed files
with
180 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# Revision history for aeson-gadt-th | ||
|
||
## 0.2.0.0 | ||
|
||
* Add changelog | ||
* Add option to modify constructor tag in derived JSON | ||
* Add test suite |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
cabal-version: >=2.0 | ||
name: aeson-gadt-th | ||
version: 0.1.2.1 | ||
version: 0.2.0.0 | ||
synopsis: Derivation of Aeson instances for GADTs | ||
category: JSON | ||
description: Template Haskell for generating ToJSON and FromJSON instances for GADTs. See <https://github.com/obsidiansystems/aeson-gadt-th/blob/master/README.md README.md> for examples. | ||
|
@@ -11,6 +11,7 @@ maintainer: [email protected] | |
copyright: 2019 Obsidian Systems LLC | ||
build-type: Simple | ||
extra-source-files: README.md | ||
ChangeLog.md | ||
|
||
library | ||
exposed-modules: Data.Aeson.GADT.TH | ||
|
@@ -32,6 +33,20 @@ executable readme | |
ghc-options: -pgmL markdown-unlit -Wall | ||
build-tool-depends: markdown-unlit:markdown-unlit | ||
|
||
test-suite aeson-gadt-th-test | ||
type: exitcode-stdio-1.0 | ||
build-depends: base | ||
, aeson | ||
, aeson-qq | ||
, dependent-sum | ||
, aeson-gadt-th | ||
, hspec | ||
, HUnit | ||
default-language: Haskell2010 | ||
hs-source-dirs: test | ||
main-is: Test.hs | ||
other-modules: Expectations | ||
|
||
source-repository head | ||
type: git | ||
location: git://github.com/obsidiansystems/aeson-gadt-th.git |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
-- | Some useful helper expectations for use with Hspec. | ||
{-# LANGUAGE LambdaCase #-} | ||
{-# LANGUAGE ScopedTypeVariables #-} | ||
module Expectations where | ||
|
||
import Control.Exception (PatternMatchFail, evaluate, throwIO, try) | ||
import Data.Maybe | ||
import GHC.Stack (HasCallStack, callStack, getCallStack, SrcLoc) | ||
import Test.Hspec | ||
import qualified Test.HUnit.Lang as HUnit | ||
|
||
-- | Assert that a pattern match succeeds; may require -fno-warn-incomplete-patterns | ||
expectPattern :: (HasCallStack, Show a) => (a -> b) -> a -> IO b | ||
expectPattern f a = | ||
try (evaluate $ f a) >>= \case | ||
Right b -> pure b | ||
Left (e :: PatternMatchFail) -> | ||
throwHUnit $ "Pattern match failed, value was: " <> show a | ||
|
||
-- | Same as 'expectPattern' but with its arguments flipped. | ||
shouldMatchPattern :: (HasCallStack, Show a) => a -> (a -> b) -> IO b | ||
shouldMatchPattern = flip expectPattern | ||
|
||
-- | Same as 'shouldMatchPattern' but with the return type specialized as unit. | ||
-- Useful for pattern matching on GADTs. | ||
shouldMatchPattern_ :: (HasCallStack, Show a) => a -> (a -> ()) -> IO () | ||
shouldMatchPattern_ = shouldMatchPattern | ||
|
||
-- | Obtain the source location given a reverse call stack index. | ||
callStackLoc :: (HasCallStack) => Int -> Maybe SrcLoc | ||
callStackLoc index = fmap snd $ listToMaybe $ drop index $ reverse $ getCallStack callStack | ||
|
||
-- | Throw an test failed exception, defaulting the source location to the caller's caller. | ||
throwHUnit :: (HasCallStack) => String -> IO a | ||
throwHUnit = throwHUnitWithLoc 0 | ||
|
||
-- | Throw a test failure exception with source location determined by the supplied reverse call stack index. | ||
throwHUnitWithLoc :: (HasCallStack) => Int -> String -> IO a | ||
throwHUnitWithLoc index msg = throwIO $ HUnit.HUnitFailure (callStackLoc index) $ HUnit.Reason msg |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
{-# LANGUAGE FlexibleContexts #-} | ||
{-# LANGUAGE FlexibleInstances #-} | ||
{-# LANGUAGE GADTs #-} | ||
{-# LANGUAGE LambdaCase #-} | ||
{-# LANGUAGE QuasiQuotes #-} | ||
{-# LANGUAGE StandaloneDeriving #-} | ||
{-# LANGUAGE TemplateHaskell #-} | ||
module Main where | ||
|
||
import Data.GADT.Show | ||
import Data.Some | ||
import Data.Aeson | ||
import Data.Aeson.QQ | ||
import Data.Aeson.GADT.TH | ||
import Expectations | ||
import Test.Hspec | ||
|
||
main :: IO () | ||
main = hspec $ do | ||
describe "aeson-gadt-th" $ do | ||
it "should generate an expected ToJSON instance" $ do | ||
toJSON (Bar 'a') `shouldBe` [aesonQQ| ["Bar", "a"] |] | ||
toJSON (Baz 1.2) `shouldBe` [aesonQQ| ["Baz", 1.2] |] | ||
it "should generate an expected FromJSON Some instance" $ do | ||
fromJSON [aesonQQ| ["Bar", "a"] |] | ||
`shouldMatchPattern_` (\case Success (This (Bar 'a')) -> ()) | ||
fromJSON [aesonQQ| ["Baz", 1.2] |] | ||
`shouldMatchPattern_` (\case Success (This (Baz 1.2)) -> ()) | ||
(fromJSON [aesonQQ| ["bad", "input"] |] :: Result (Some Foo)) | ||
`shouldMatchPattern_` (\case Error "Expected tag to be one of [Bar, Baz] but got: bad" -> ()) | ||
|
||
it "should generate an expected ToJSON instance with options" $ do | ||
toJSON (Spam'Eggs 'a') `shouldBe` [aesonQQ| ["Eggs", "a"] |] | ||
toJSON (Spam'Life 1.2) `shouldBe` [aesonQQ| ["Life", 1.2] |] | ||
it "should generate an expected FromJSON Some instance with options" $ do | ||
fromJSON [aesonQQ| ["Eggs", "a"] |] | ||
`shouldMatchPattern_` (\case Success (This (Spam'Eggs 'a')) -> ()) | ||
fromJSON [aesonQQ| ["Life", 1.2] |] | ||
`shouldMatchPattern_` (\case Success (This (Spam'Life 1.2)) -> ()) | ||
(fromJSON [aesonQQ| ["bad", "input"] |] :: Result (Some Spam)) | ||
`shouldMatchPattern_` (\case Error "Expected tag to be one of [Eggs, Life] but got: bad" -> ()) | ||
|
||
data Foo a where | ||
Bar :: Char -> Foo Char | ||
Baz :: Float -> Foo Float | ||
|
||
deriving instance Show (Foo a) | ||
deriving instance Eq (Foo a) | ||
|
||
instance GShow Foo where gshowsPrec = showsPrec | ||
|
||
data Spam a where | ||
Spam'Eggs :: Char -> Spam Char | ||
Spam'Life :: Float -> Spam Float | ||
|
||
deriving instance Show (Spam a) | ||
deriving instance Eq (Spam a) | ||
|
||
instance GShow Spam where gshowsPrec = showsPrec | ||
|
||
deriveJSONGADT ''Foo | ||
|
||
deriveJSONGADTWithOptions | ||
(JSONGADTOptions { gadtConstructorModifier = drop 5 }) | ||
''Spam |