on master help sign in
Merge pull request #364 from phadej/non-empty
commit9fb62b94fccca2c0579f3cfe6c05d209ac5368cf authorAdam Bergmark <adam@bergmark.nl> dateTue 1 Mar 2016, 18:13 (11 years ago) parentf69ed10077fcc4
5 files changed+31−2
Data/Aeson/Types/Instances.hs+15 aeson.cabal+5−2 tests/Instances.hs+4 tests/Properties.hs+2 tests/UnitTests.hs+5
Data/Aeson/Types/Instances.hs+15
@@ -71,6 +71,7 @@ import Data.Foldable (toList)
7171import Data.Functor.Identity (Identity(..))
7272import Data.Hashable (Hashable(..))
7373import Data.Int (Int8, Int16, Int32, Int64)
74+import Data.List.NonEmpty (NonEmpty(..))
7475import Data.Maybe (fromMaybe)
7576import Data.Monoid ((<>))
7677import Data.Monoid (Dual(..), First(..), Last(..))
@@ -489,6 +490,20 @@ instance FromJSON LT.Text where
489490 parseJSON = withText "Lazy Text" $ pure . LT.fromStrict
490491 {-# INLINE parseJSON #-}
491492
493+instance (ToJSON a) => ToJSON (NonEmpty a) where
494+ toJSON = toJSON . toList
495+ {-# INLINE toJSON #-}
496+
497+ toEncoding = toEncoding . toList
498+ {-# INLINE toEncoding #-}
499+
500+instance (FromJSON a) => FromJSON (NonEmpty a) where
501+ parseJSON = withArray "NonEmpty a" $
502+ (>>= ne) . Tr.sequence . zipWith parseIndexedJSON [0..] . V.toList
503+ where
504+ ne [] = fail "Expected a NonEmpty but got an empty list"
505+ ne (x:xs) = pure (x :| xs)
506+
492507instance OVERLAPPABLE_ (ToJSON a) => ToJSON [a] where
493508 toJSON = Array . V.fromList . map toJSON
494509 {-# INLINE toJSON #-}
aeson.cabal+5−2
@@ -108,7 +108,7 @@ library
108108
109109 if !impl(ghc >= 8.0)
110110 -- `Data.Semigroup` is available in base only since GHC 8.0 / base 4.9
111− build-depends: semigroups >= 0.16.1
111+ build-depends: semigroups >= 0.16.1 && < 0.17
112112
113113 if !impl(ghc >= 7.10)
114114 -- `Numeric.Natural` is available in base only since GHC 7.10 / base 4.8
@@ -149,7 +149,7 @@ test-suite tests
149149
150150 build-depends:
151151 HUnit,
152− QuickCheck >= 2.7,
152+ QuickCheck >= 2.7 && <2.8.3,
153153 aeson,
154154 attoparsec,
155155 base,
@@ -166,6 +166,9 @@ test-suite tests
166166 vector,
167167 quickcheck-instances >=0.3.12
168168
169+ if !impl(ghc >= 8.0)
170+ build-depends: semigroups >= 0.16.1 && < 0.17
171+
169172 if flag(old-locale)
170173 build-depends: time < 1.5, old-locale
171174 else
tests/Instances.hs+4
@@ -14,6 +14,7 @@ import Test.QuickCheck (Arbitrary(..), getNonNegative, elements,
1414 listOf1, oneof, resize)
1515import Data.Time.Clock (UTCTime(..))
1616import Data.Time (ZonedTime(..), TimeZone(..))
17+import Data.List.NonEmpty (NonEmpty(..))
1718import Data.Version
1819import Data.Aeson.Types
1920import Control.Applicative
@@ -140,6 +141,9 @@ instance (ApproxEq a) => ApproxEq [a] where
140141instance Arbitrary Version where
141142 arbitrary = makeVersion . fmap getNonNegative <$> resize 4 (listOf1 arbitrary)
142143
144+instance Arbitrary a => Arbitrary (NonEmpty a) where
145+ arbitrary = (:|) <$> arbitrary <*> arbitrary
146+
143147-- Version tags are deprecated, so we avoid using them in the Arbitrary
144148-- instance. However, the recommended constructor 'makeVersion' is not
145149-- exported by "Data.Version" until base-4.8.0.0. For previous versions,
tests/Properties.hs+2
@@ -9,6 +9,7 @@ import Data.Aeson.Parser (value)
99import Data.Aeson.Types
1010import Data.ByteString.Builder (toLazyByteString)
1111import Data.Int (Int8)
12+import Data.List.NonEmpty (NonEmpty)
1213import Data.Time (Day, LocalTime, NominalDiffTime, TimeOfDay, UTCTime,
1314 ZonedTime)
1415import Data.Version (Version)
@@ -128,6 +129,7 @@ tests = testGroup "properties" [
128129 testProperty "Bool" $ roundTripEq True
129130 , testProperty "Double" $ roundTripEq (1 :: Approx Double)
130131 , testProperty "Int" $ roundTripEq (1 :: Int)
132+ , testProperty "NonEmpty Char" $ roundTripEq (undefined :: NonEmpty Char)
131133 , testProperty "Integer" $ roundTripEq (1 :: Integer)
132134 , testProperty "String" $ roundTripEq ("" :: String)
133135 , testProperty "Text" $ roundTripEq T.empty
tests/UnitTests.hs+5
@@ -12,6 +12,7 @@ import Data.Aeson.TH (deriveJSON)
1212import Data.Aeson.Types (ToJSON(..), Value, camelTo, camelTo2, defaultOptions, omitNothingFields)
1313import Data.Char (toUpper)
1414import Data.Maybe (fromMaybe)
15+import Data.List.NonEmpty (NonEmpty(..))
1516import Data.Time (UTCTime)
1617import Data.Time.Format (parseTime)
1718import Data.Sequence (Seq)
@@ -224,6 +225,8 @@ jsonEncoding = [
224225 , assertEqual "Just" "1" $ encode (Just 1 :: Maybe Int)
225226 , assertEqual "Just Nothing" "null" $ encode (Just Nothing :: Maybe (Maybe Int))
226227 , assertEqual "Tuple" "[1,2]" $ encode ((1, 2) :: (Int, Int))
228+ , assertEqual "NonEmpty" "[1,2,3]" $ encode (1 :| [2, 3] :: NonEmpty Int)
229+ , assertEqual "()" "[]" $ encode ()
227230 ]
228231
229232jsonDecoding :: [Assertion]
@@ -231,6 +234,8 @@ jsonDecoding = [
231234 assertEqual "Nothing" (Nothing :: Maybe Int) (decode "null")
232235 , assertEqual "Just" (Just 1 :: Maybe Int) (decode "1")
233236 , assertEqual "Just Nothing" (Just Nothing :: Maybe (Maybe Int)) (decode "null")
237+ , assertEqual "NonEmpty" (Just (1 :| [2, 3]) :: Maybe (NonEmpty Int)) (decode "[1,2,3]")
238+ , assertEqual "()" (Just ()) (decode "[]")
234239 ]
235240
236241------------------------------------------------------------------------------