Compare commits
No commits in common. "39d8da7b11a80269454c2f134a5c834e0f3cb9a7" and "29eef5d5df20d6342f5fd8a490c83cd69794579d" have entirely different histories.
39d8da7b11
...
29eef5d5df
7 changed files with 57 additions and 88 deletions
|
@ -11,8 +11,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
- CHANGELOG.md
|
- CHANGELOG.md
|
||||||
- Stack integration
|
- Stack integration
|
||||||
- Borsh serialization for `HexString`
|
|
||||||
- `Read` implementation for `HexString`
|
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
haskell-hexstring
|
haskell-hexstring
|
||||||
=================
|
=================
|
||||||
|
|
||||||
|
[![Build Status](https://travis-ci.org/solatis/haskell-hexstring.png?branch=master)](https://travis-ci.org/solatis/haskell-hexstring)
|
||||||
|
[![Coverage Status](https://coveralls.io/repos/solatis/haskell-hexstring/badge.svg?branch=master)](https://coveralls.io/r/solatis/haskell-hexstring?branch=master)
|
||||||
|
[![MIT](http://b.repl.ca/v1/license-MIT-blue.png)](http://en.wikipedia.org/wiki/MIT_License)
|
||||||
|
[![Haskell](http://b.repl.ca/v1/language-haskell-lightgrey.png)](http://haskell.org)
|
||||||
|
|
||||||
Fast and safe representation of a hex string
|
Fast and safe representation of a hex string
|
||||||
|
|
|
@ -5,7 +5,7 @@ cabal-version: 1.12
|
||||||
-- see: https://github.com/sol/hpack
|
-- see: https://github.com/sol/hpack
|
||||||
|
|
||||||
name: hexstring
|
name: hexstring
|
||||||
version: 0.12.1.0
|
version: 0.12.0
|
||||||
synopsis: Fast and safe representation of a hex string
|
synopsis: Fast and safe representation of a hex string
|
||||||
description: Provides an interface for converting any object that has a 'Binary' instance to and from a hexadecimal Text representation.
|
description: Provides an interface for converting any object that has a 'Binary' instance to and from a hexadecimal Text representation.
|
||||||
author: Rene Vergara,
|
author: Rene Vergara,
|
||||||
|
@ -35,10 +35,7 @@ library
|
||||||
, base >=4.7 && <5
|
, base >=4.7 && <5
|
||||||
, base16-bytestring
|
, base16-bytestring
|
||||||
, binary
|
, binary
|
||||||
, borsh >=0.2
|
|
||||||
, bytestring
|
, bytestring
|
||||||
, foreign-rust
|
|
||||||
, generics-sop
|
|
||||||
, text
|
, text
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
name: hexstring
|
name: hexstring
|
||||||
version: 0.12.1.0
|
version: 0.12.0
|
||||||
git: "https://git.vergara.tech/Vergara_Tech/haskell-hexstring"
|
git: "https://git.vergara.tech/Vergara_Tech/haskell-hexstring"
|
||||||
license: MIT
|
license: MIT
|
||||||
author:
|
author:
|
||||||
|
@ -27,9 +27,6 @@ library:
|
||||||
- bytestring
|
- bytestring
|
||||||
- base16-bytestring
|
- base16-bytestring
|
||||||
- aeson
|
- aeson
|
||||||
- generics-sop
|
|
||||||
- borsh >= 0.2
|
|
||||||
- foreign-rust
|
|
||||||
|
|
||||||
tests:
|
tests:
|
||||||
hextring-test:
|
hextring-test:
|
||||||
|
|
|
@ -1,91 +1,69 @@
|
||||||
{-# LANGUAGE DeriveGeneric #-}
|
module Data.HexString ( HexString
|
||||||
{-# LANGUAGE DuplicateRecordFields #-}
|
, hexString
|
||||||
{-# LANGUAGE DeriveAnyClass #-}
|
, fromBinary
|
||||||
{-# LANGUAGE DerivingVia #-}
|
, toBinary
|
||||||
{-# LANGUAGE UndecidableInstances #-}
|
, fromBytes
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
, toBytes
|
||||||
|
, toText ) where
|
||||||
|
|
||||||
module Data.HexString
|
import Control.Applicative (pure)
|
||||||
( HexString(..)
|
|
||||||
, hexString
|
|
||||||
, fromRawBytes
|
|
||||||
, fromBinary
|
|
||||||
, toBytes
|
|
||||||
, fromText
|
|
||||||
, toText
|
|
||||||
) where
|
|
||||||
|
|
||||||
import Codec.Borsh
|
import Data.Aeson
|
||||||
import Control.Applicative (pure)
|
import Data.Word (Word8)
|
||||||
|
|
||||||
import Data.Aeson
|
import qualified Data.ByteString as BS
|
||||||
import Data.Word (Word8)
|
import qualified Data.ByteString.Base16 as BS16 (decodeLenient, encode)
|
||||||
|
import qualified Data.ByteString.Lazy as BSL
|
||||||
|
|
||||||
import qualified Data.ByteString as BS
|
import qualified Data.Text as T
|
||||||
import qualified Data.ByteString.Base16 as BS16 (decode, decodeLenient, encode)
|
import qualified Data.Text.Encoding as TE
|
||||||
import qualified Data.ByteString.Lazy as BSL
|
|
||||||
|
|
||||||
import Data.Structured
|
import qualified Data.Binary as B (Binary, decode, encode)
|
||||||
import qualified Data.Text as T
|
|
||||||
import qualified Data.Text.Encoding as TE
|
|
||||||
|
|
||||||
import qualified Data.Binary as B (Binary, decode, encode)
|
|
||||||
|
|
||||||
import qualified GHC.Generics as GHC
|
|
||||||
import qualified Generics.SOP as SOP
|
|
||||||
import Text.Read
|
|
||||||
|
|
||||||
-- | Represents a Hex string. Guarantees that all characters it contains
|
-- | Represents a Hex string. Guarantees that all characters it contains
|
||||||
-- are valid hex characters.
|
-- are valid hex characters.
|
||||||
newtype HexString = HexString
|
data HexString =
|
||||||
{ hexBytes :: BS.ByteString
|
HexString BS.ByteString
|
||||||
} deriving stock (Eq, GHC.Generic)
|
deriving ( Show, Eq, Ord )
|
||||||
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
|
|
||||||
deriving anyclass (Data.Structured.Show)
|
|
||||||
deriving (BorshSize, ToBorsh, FromBorsh) via AsStruct HexString
|
|
||||||
|
|
||||||
instance Prelude.Show HexString where
|
|
||||||
show = T.unpack . toText
|
|
||||||
|
|
||||||
instance Read HexString where
|
|
||||||
readsPrec _ s = [((fromText . T.pack) s, "")]
|
|
||||||
|
|
||||||
instance FromJSON HexString where
|
instance FromJSON HexString where
|
||||||
parseJSON = withText "HexString" $ pure . hexString . TE.encodeUtf8
|
parseJSON = withText "HexString" $ pure . hexString . TE.encodeUtf8
|
||||||
|
|
||||||
instance ToJSON HexString where
|
instance ToJSON HexString where
|
||||||
toJSON = Data.Aeson.String . toText
|
toJSON = String . toText
|
||||||
|
|
||||||
-- | Smart constructor which validates that all the text are actually
|
-- | Smart constructor which validates that all the text are actually
|
||||||
-- hexadecimal characters.
|
-- hexadecimal characters.
|
||||||
hexString :: BS.ByteString -> HexString
|
hexString :: BS.ByteString -> HexString
|
||||||
hexString bs =
|
hexString bs =
|
||||||
case BS16.decode bs of
|
let isValidHex :: Word8 -> Bool
|
||||||
Right s -> HexString s
|
isValidHex c
|
||||||
Left e -> error e
|
| (48 <= c) && (c < 58) = True
|
||||||
|
| (97 <= c) && (c < 103) = True
|
||||||
|
| otherwise = False
|
||||||
|
|
||||||
|
in if BS.all isValidHex bs
|
||||||
|
then HexString bs
|
||||||
|
else error ("Not a valid hex string: " ++ show bs)
|
||||||
|
|
||||||
-- | Converts a 'B.Binary' to a 'HexString' value
|
-- | Converts a 'B.Binary' to a 'HexString' value
|
||||||
fromBinary :: B.Binary a => a -> HexString
|
fromBinary :: B.Binary a => a -> HexString
|
||||||
fromBinary = HexString . BSL.toStrict . B.encode
|
fromBinary = hexString . BS16.encode . BSL.toStrict . B.encode
|
||||||
|
|
||||||
-- | Converts a 'HexString' to a 'B.Binary' value
|
-- | Converts a 'HexString' to a 'B.Binary' value
|
||||||
toBinary :: B.Binary a => HexString -> a
|
toBinary :: B.Binary a => HexString -> a
|
||||||
toBinary (HexString bs) = (B.decode . BSL.fromStrict) bs
|
toBinary (HexString bs) = B.decode . BSL.fromStrict . BS16.decodeLenient $ bs
|
||||||
|
|
||||||
-- | Reads a 'BS.ByteString' as raw bytes and converts to hex representation. We
|
-- | Reads a 'BS.ByteString' as raw bytes and converts to hex representation. We
|
||||||
-- cannot use the instance Binary of 'BS.ByteString' because it provides
|
-- cannot use the instance Binary of 'BS.ByteString' because it provides
|
||||||
-- a leading length, which is not what we want when dealing with raw bytes.
|
-- a leading length, which is not what we want when dealing with raw bytes.
|
||||||
fromRawBytes :: BS.ByteString -> HexString
|
fromBytes :: BS.ByteString -> HexString
|
||||||
fromRawBytes = HexString
|
fromBytes = hexString . BS16.encode
|
||||||
|
|
||||||
-- | Access to the raw bytes in a 'BS.ByteString' format.
|
-- | Access to the raw bytes in a 'BS.ByteString' format.
|
||||||
toBytes :: HexString -> BS.ByteString
|
toBytes :: HexString -> BS.ByteString
|
||||||
toBytes (HexString bs) = bs
|
toBytes (HexString bs) = BS16.decodeLenient bs
|
||||||
|
|
||||||
-- | Reads a human-readable hex string into a `HexString`
|
|
||||||
fromText :: T.Text -> HexString
|
|
||||||
fromText = hexString . TE.encodeUtf8
|
|
||||||
|
|
||||||
-- | Access to a 'T.Text' representation of the 'HexString'
|
-- | Access to a 'T.Text' representation of the 'HexString'
|
||||||
toText :: HexString -> T.Text
|
toText :: HexString -> T.Text
|
||||||
toText (HexString bs) = (TE.decodeUtf8 . BS16.encode) bs
|
toText (HexString bs) = TE.decodeUtf8 bs
|
||||||
|
|
|
@ -2,9 +2,3 @@ resolver: lts-21.22
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
- .
|
- .
|
||||||
|
|
||||||
extra-deps:
|
|
||||||
- git: https://github.com/well-typed/borsh.git
|
|
||||||
commit: d2fcfa159e0a844b1ec5e8ed3e232d4b380fa831
|
|
||||||
- git: https://git.vergara.tech/Vergara_Tech/haskell-foreign-rust.git
|
|
||||||
commit: 787c2e813eb3a5d16c375d4b37dfefbd2adcdf05
|
|
||||||
|
|
|
@ -1,27 +1,27 @@
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
|
||||||
|
|
||||||
module Data.HexStringSpec where
|
module Data.HexStringSpec where
|
||||||
|
|
||||||
import Data.HexString (fromRawBytes, fromText, hexString, toBytes, toText)
|
import Data.HexString ( hexString
|
||||||
|
, fromBytes
|
||||||
|
, toBytes )
|
||||||
|
|
||||||
import qualified Data.ByteString.Char8 as BS8
|
import qualified Data.ByteString.Char8 as BS8
|
||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = do
|
spec = do
|
||||||
describe "when constructing a hex string" $ do
|
describe "when constructing a hex string" $ do
|
||||||
|
it "should accept strings that fall within a valid range" $
|
||||||
|
hexString (BS8.pack "0123456789abcdef") `shouldBe` hexString (BS8.pack "0123456789abcdef")
|
||||||
|
|
||||||
it "should reject strings outside the range" $ do
|
it "should reject strings outside the range" $ do
|
||||||
print (hexString (BS8.pack "/")) `shouldThrow` anyErrorCall
|
putStrLn (show (hexString (BS8.pack "/"))) `shouldThrow` anyErrorCall
|
||||||
print (hexString (BS8.pack ":")) `shouldThrow` anyErrorCall
|
putStrLn (show (hexString (BS8.pack ":"))) `shouldThrow` anyErrorCall
|
||||||
print (hexString (BS8.pack "`")) `shouldThrow` anyErrorCall
|
putStrLn (show (hexString (BS8.pack "`"))) `shouldThrow` anyErrorCall
|
||||||
print (hexString (BS8.pack "g")) `shouldThrow` anyErrorCall
|
putStrLn (show (hexString (BS8.pack "g"))) `shouldThrow` anyErrorCall
|
||||||
|
|
||||||
describe "when interpreting a hex string" $ do
|
describe "when interpreting a hex string" $ do
|
||||||
it "should convert the hex string properly when interpreting as bytes" $
|
it "should convert the hex string properly when interpreting as bytes" $
|
||||||
toBytes (hexString "ffff") `shouldBe` BS8.pack "\255\255"
|
toBytes (hexString (BS8.pack "ffff")) `shouldBe` BS8.pack "\255\255"
|
||||||
it "should convert bytes to the proper hex string" $
|
it "should convert bytes to the proper hex string" $
|
||||||
fromRawBytes (BS8.pack "\255\255") `shouldBe` hexString (BS8.pack "ffff")
|
fromBytes (BS8.pack "\255\255") `shouldBe` hexString (BS8.pack "ffff")
|
||||||
it "should convert the hex string to text" $
|
|
||||||
toText (hexString "ffff") `shouldBe` "ffff"
|
|
||||||
it "should read text into the hex string" $
|
|
||||||
fromText "ffff" `shouldBe` hexString (BS8.pack "ffff")
|
|
||||||
|
|
Loading…
Reference in a new issue