Skip to content

Commit d028e4c

Browse files
authored
Merge pull request #47 from zmactep/maksbotan/fix-map-unpack-bug
Fix bug with BoltValue Map unpack
2 parents 3ec4ae0 + 87ceeab commit d028e4c

File tree

3 files changed

+36
-28
lines changed

3 files changed

+36
-28
lines changed

hasbolt.cabal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: hasbolt
2-
version: 0.1.7.1
2+
version: 0.1.7.2
33
synopsis: Haskell driver for Neo4j 3+ (BOLT protocol)
44
description:
55
Haskell driver for Neo4j 3+ (BOLT protocol).

src/Database/Bolt/Value/Instances.hs

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,29 +9,30 @@ module Database.Bolt.Value.Instances where
99
import Database.Bolt.Value.Helpers
1010
import Database.Bolt.Value.Type
1111

12-
import Control.Monad (forM, replicateM)
13-
import Control.Monad.Except (MonadError (..))
14-
import Data.Binary (Binary (..), Put, decode, encode)
12+
import Control.Monad (forM, replicateM)
13+
import Control.Monad.Except (MonadError (..))
14+
import Data.Binary (Binary (..), Put, decode, encode)
1515
import Data.Binary.Get
16-
import Data.Binary.IEEE754 (doubleToWord, wordToDouble)
17-
import Data.Binary.Put (putByteString, putWord16be, putWord32be, putWord64be,
18-
putWord8)
19-
import Data.ByteString (ByteString)
20-
import qualified Data.ByteString as B
21-
import Data.ByteString.Lazy (fromStrict, toStrict)
16+
import Data.Binary.IEEE754 (doubleToWord, wordToDouble)
17+
import Data.Binary.Put (putByteString, putWord16be, putWord32be, putWord64be,
18+
putWord8)
19+
import Data.ByteString (ByteString)
20+
import qualified Data.ByteString as B
21+
import Data.ByteString.Lazy (fromStrict, toStrict)
2222
import Data.Int
23-
import Data.Map.Strict (Map)
24-
import qualified Data.Map.Strict as M
25-
import Data.Text (Text)
26-
import Data.Text.Encoding (decodeUtf8, encodeUtf8)
23+
import Data.Map.Strict (Map)
24+
import qualified Data.Map.Strict as M
25+
import Data.Text (Text)
26+
import Data.Text.Encoding (decodeUtf8, encodeUtf8)
2727
import Data.Word
28+
import GHC.Stack (HasCallStack, callStack, prettyCallStack)
2829

2930
instance BoltValue () where
3031
pack () = putWord8 nullCode
3132

3233
unpackT = getWord8 >>= unpackByMarker
3334
where unpackByMarker m | m == nullCode = pure ()
34-
| otherwise = fail "expected null"
35+
| otherwise = failUnpack "null" m
3536

3637
instance BoltValue Bool where
3738
pack True = putWord8 trueCode
@@ -40,7 +41,7 @@ instance BoltValue Bool where
4041
unpackT = getWord8 >>= unpackByMarker
4142
where unpackByMarker m | m == trueCode = pure True
4243
| m == falseCode = pure False
43-
| otherwise = fail "expected bool"
44+
| otherwise = failUnpack "bool" m
4445

4546
instance BoltValue Int where
4647
pack int | isTinyInt int = putWord8 $ fromIntegral int
@@ -56,14 +57,14 @@ instance BoltValue Int where
5657
| m == int16Code = toInt <$> getInt16be
5758
| m == int32Code = toInt <$> getInt32be
5859
| m == int64Code = toInt <$> getInt64be
59-
| otherwise = fail "expected int"
60+
| otherwise = failUnpack "int" m
6061

6162
instance BoltValue Double where
6263
pack dbl = putWord8 doubleCode >> putWord64be (doubleToWord dbl)
6364

6465
unpackT = getWord8 >>= unpackByMarker
6566
where unpackByMarker m | m == doubleCode = wordToDouble <$> getWord64be
66-
| otherwise = fail "expected double"
67+
| otherwise = failUnpack "double" m
6768

6869
instance BoltValue Text where
6970
pack txt = mkPackedCollection (B.length bs) pbs (textConst, text8Code, text16Code, text32Code)
@@ -75,7 +76,7 @@ instance BoltValue Text where
7576
| m == text8Code = toInt <$> getInt8 >>= unpackTextBySize
7677
| m == text16Code = toInt <$> getInt16be >>= unpackTextBySize
7778
| m == text32Code = toInt <$> getInt32be >>= unpackTextBySize
78-
| otherwise = fail "expected text"
79+
| otherwise = failUnpack "text" m
7980
unpackTextBySize size = do str <- getByteString size
8081
pure $! decodeUtf8 str
8182

@@ -88,7 +89,7 @@ instance BoltValue a => BoltValue [a] where
8889
| m == list8Code = toInt <$> getInt8 >>= unpackListBySize
8990
| m == list16Code = toInt <$> getInt16be >>= unpackListBySize
9091
| m == list32Code = toInt <$> getInt32be >>= unpackListBySize
91-
| otherwise = fail "expected list"
92+
| otherwise = failUnpack "list" m
9293
unpackListBySize size = forM [1..size] $ const unpackT
9394

9495
instance BoltValue a => BoltValue (Map Text a) where
@@ -98,10 +99,10 @@ instance BoltValue a => BoltValue (Map Text a) where
9899

99100
unpackT = getWord8 >>= unpackByMarker
100101
where unpackByMarker m | isTinyDict m = unpackDictBySize (getSize m)
101-
| m == dict8Code = toInt <$> getInt16be >>= unpackDictBySize
102+
| m == dict8Code = toInt <$> getInt8 >>= unpackDictBySize
102103
| m == dict16Code = toInt <$> getInt16be >>= unpackDictBySize
103104
| m == dict32Code = toInt <$> getInt32be >>= unpackDictBySize
104-
| otherwise = fail "expected dict"
105+
| otherwise = failUnpack "dict" m
105106
unpackDictBySize = (M.fromList <$>) . unpackPairsBySize
106107
unpackPairsBySize size = forM [1..size] $ const $ do
107108
!key <- unpackT
@@ -120,7 +121,7 @@ instance BoltValue Structure where
120121
where unpackByMarker m | isTinyStruct m = unpackStructureBySize (getSize m)
121122
| m == struct8Code = toInt <$> getInt8 >>= unpackStructureBySize
122123
| m == struct16Code = toInt <$> getInt16be >>= unpackStructureBySize
123-
| otherwise = fail "expected structure"
124+
| otherwise = failUnpack "structure" m
124125
unpackStructureBySize size = Structure <$> getWord8 <*> replicateM size unpackT
125126

126127
instance BoltValue Value where
@@ -142,7 +143,7 @@ instance BoltValue Value where
142143
| isList m = L <$> unpackT
143144
| isDict m = M <$> unpackT
144145
| isStruct m = S <$> unpackT
145-
| otherwise = fail "not value"
146+
| otherwise = failUnpack "value" m
146147

147148
-- = Structure instances for Neo4j structures
148149

@@ -207,3 +208,10 @@ size4 = 2^(4 :: Int)
207208
size8 = 2^(8 :: Int)
208209
size16 = 2^(16 :: Int)
209210
size32 = 2^(32 :: Int)
211+
212+
failUnpack :: (HasCallStack, MonadFail m) => String -> Word8 -> m a
213+
failUnpack expected got = fail $
214+
"expected " <> expected <> ", got: " <> show got
215+
<> (if null cs then "" else "\n" <> cs)
216+
where
217+
cs = prettyCallStack callStack

src/Database/Bolt/Value/Type.hs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,12 @@ class ToStructure a where
7474
-- |The 'BoltValue' class describes values, that can be packed and unpacked for BOLT protocol.
7575
class BoltValue a where
7676
-- |Packs a value to 'ByteString'
77-
pack :: a -> Put
77+
pack :: HasCallStack => a -> Put
7878
-- |Unpacks in a State monad to get values from single 'ByteString'
79-
unpackT :: Get a
79+
unpackT :: HasCallStack => Get a
8080

8181
-- |Unpacks a 'ByteString' to selected value
82-
unpack :: (Monad m, BoltValue a) => ByteString -> m (Either UnpackError a)
82+
unpack :: (Monad m, BoltValue a, HasCallStack) => ByteString -> m (Either UnpackError a)
8383
unpack = pure . unpackAction unpackT . fromStrict
8484

8585
-- |Old-style unpack that runs 'fail' on error
@@ -90,7 +90,7 @@ unpackF bs = do let result = unpackAction unpackT $ fromStrict bs
9090
Left e -> Fail.fail $ show e
9191

9292
-- |Unpacks a 'ByteString' to selected value by some custom action
93-
unpackAction :: Get a -> BSL.ByteString -> Either UnpackError a
93+
unpackAction :: HasCallStack => Get a -> BSL.ByteString -> Either UnpackError a
9494
unpackAction action bs = case runGetOrFail action bs of
9595
Left (_, _, err) -> Left $ BinaryError $ T.pack err
9696
Right (_, _, a) -> Right a

0 commit comments

Comments
 (0)