From 8d66b5337483b1c1d9abc3c868eef230f0f0cb9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enis=20Bayramo=C4=9Flu?= Date: Fri, 8 Dec 2023 21:49:33 +0100 Subject: [PATCH] Allow building with GHC 9.6 (#52) This PR allows us to compile `chainweb-api` with the latest GHC versions while maintaining the build with the old package set, which is still needed by obelisk projects, like block-explorer. In order to maintain the build with the old dependency tree alongside the latest packages from hackage, we had to tiptoe around the following breaking changes: * `aeson` doesn't represent its `Object`s as `HashMap`s anymore, so this PR rewrites JSON parsing code to not depend on that representation. * `aeson` now hides the `Text` representation of its object `Key`s, so we're now going from `Text` to `String` and then coming back with `fromString`, which lets the result work with both representations. * `base16-bytestring` changed the type of its `decode` function from `decode :: ByteString -> (ByteString, ByteString)` to `ByteString -> Either String ByteString`, which we had to work around by resorting to `CPP`. * Allow building with GHC 9.6 While maintaining the build with the old package set, which is still needed by obelisk projects, like block-explorer * Remove upper bounds * Fix unqualified Data.List import warning --- chainweb-api.cabal | 38 +++++++++++++++++----------------- lib/Chainweb/Api/BytesLE.hs | 11 ++++++++++ lib/Chainweb/Api/Guard.hs | 3 ++- lib/Chainweb/Api/Hash.hs | 4 +++- lib/Chainweb/Api/Payload.hs | 13 ++++++------ lib/ChainwebData/Pagination.hs | 6 +++--- 6 files changed, 45 insertions(+), 30 deletions(-) diff --git a/chainweb-api.cabal b/chainweb-api.cabal index 2035902..df90fa8 100644 --- a/chainweb-api.cabal +++ b/chainweb-api.cabal @@ -21,24 +21,24 @@ library -Wincomplete-uni-patterns -Widentities build-depends: - Decimal >= 0.4.2 && < 0.6 - , aeson ^>=1.4 - , attoparsec >= 0.13.0.2 && < 0.15 - , base >=4.7 && <5 - , base16-bytestring ^>=0.1 - , base64-bytestring ^>=1.0 - , bytestring ^>=0.10 - , cereal ^>=0.5 - , containers ^>=0.6 - , data-default ^>=0.7 - , hashable >=1.2 && < 1.4 - , readable ^>=0.3 + Decimal >= 0.4.2 + , aeson >=1.4 + , attoparsec >= 0.13.0.2 + , base >=4.7 && <5 + , base16-bytestring >=0.1 + , base64-bytestring >=1.0 + , bytestring >=0.10 + , cereal >=0.5 + , containers >=0.6 + , data-default >=0.7 + , hashable >=1.2 + , readable >=0.3 , scientific - , servant >= 0.16 && < 0.19 - , text ^>=1.2 - , time >=1.8 && < 1.11 - , unordered-containers ^>=0.2 - , vector >= 0.11.0.0 && < 0.13 + , servant >= 0.16 + , text >=1.2 + , time >=1.8 + , unordered-containers >=0.2 + , vector >= 0.11.0.0 if !impl(ghcjs) build-depends: @@ -96,6 +96,6 @@ test-suite testsuite , bytestring , cereal , neat-interpolation >= 0.5 - , tasty ^>= 1.2 - , tasty-hunit ^>= 0.10 + , tasty >= 1.2 + , tasty-hunit >= 0.10 , text diff --git a/lib/Chainweb/Api/BytesLE.hs b/lib/Chainweb/Api/BytesLE.hs index b26e805..8fafa16 100644 --- a/lib/Chainweb/Api/BytesLE.hs +++ b/lib/Chainweb/Api/BytesLE.hs @@ -1,3 +1,5 @@ +{-# LANGUAGE CPP #-} + module Chainweb.Api.BytesLE where ------------------------------------------------------------------------------ @@ -16,6 +18,14 @@ newtype BytesLE = BytesLE { unBytesLE :: ByteString } deriving (Eq,Ord,Show) +#if MIN_VERSION_base16_bytestring(1,0,0) +-- Newer version of base16-bytestring +hexToBytesLE :: Text -> Either String BytesLE +hexToBytesLE t = case B16.decode $ T.encodeUtf8 t of + Right decoded -> Right $ BytesLE decoded + Left _ -> Left $ "Invalid hex string: " <> T.unpack t +#else +-- Older version of base16-bytestring hexToBytesLE :: Text -> Either String BytesLE hexToBytesLE t = if B.null invalid @@ -23,6 +33,7 @@ hexToBytesLE t = else Left $ "Invalid hex string: " <> T.unpack t where (decoded, invalid) = B16.decode $ T.encodeUtf8 t +#endif hexFromBytesLE :: BytesLE -> Text hexFromBytesLE = T.decodeUtf8 . B16.encode . unBytesLE diff --git a/lib/Chainweb/Api/Guard.hs b/lib/Chainweb/Api/Guard.hs index d5fc8f4..2afd9c2 100644 --- a/lib/Chainweb/Api/Guard.hs +++ b/lib/Chainweb/Api/Guard.hs @@ -8,6 +8,7 @@ import Control.Applicative import Data.Aeson import Data.Maybe import Data.Set (Set) +import Data.String (IsString) import Data.Text (Text) ------------------------------------------------------------------------------ @@ -19,7 +20,7 @@ data Guard | GUser UserGuard deriving (Eq,Show) -keyNamef :: Text +keyNamef :: IsString s => s keyNamef = "keysetref" instance ToJSON Guard where diff --git a/lib/Chainweb/Api/Hash.hs b/lib/Chainweb/Api/Hash.hs index 99e1edc..525e289 100644 --- a/lib/Chainweb/Api/Hash.hs +++ b/lib/Chainweb/Api/Hash.hs @@ -8,7 +8,9 @@ import Data.Aeson.Encoding import Data.Aeson.Types import Data.ByteString (ByteString) import qualified Data.ByteString.Base16 as B16 +import Data.String (fromString) import Data.Text (Text) +import qualified Data.Text as T import qualified Data.Text.Encoding as T ------------------------------------------------------------------------------ import Chainweb.Api.Base64Url @@ -18,7 +20,7 @@ newtype Hash = Hash { unHash :: ByteString } deriving (Eq,Ord,Show,Read) instance ToJSONKey Hash where - toJSONKey = ToJSONKeyText hashB64U (text . hashB64U) + toJSONKey = ToJSONKeyText (fromString . T.unpack . hashB64U) (text . hashB64U) instance FromJSONKey Hash where fromJSONKey = FromJSONKeyTextParser hashParser diff --git a/lib/Chainweb/Api/Payload.hs b/lib/Chainweb/Api/Payload.hs index 1d40cbd..de91dbe 100644 --- a/lib/Chainweb/Api/Payload.hs +++ b/lib/Chainweb/Api/Payload.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} @@ -5,7 +6,6 @@ module Chainweb.Api.Payload where ------------------------------------------------------------------------------ import Data.Aeson -import qualified Data.HashMap.Strict as HM import Data.Text (Text) ------------------------------------------------------------------------------ @@ -58,11 +58,12 @@ instance ToJSON Payload where toJSON (ContPayload cont) = Object $ "cont" .= toJSON cont instance FromJSON Payload where - parseJSON = withObject "Payload" $ \o -> case HM.lookup "exec" o of - Just v | v /= Null -> ExecPayload <$> parseJSON v - _ -> case HM.lookup "cont" o of - Nothing -> fail "Payload must be exec or cont" - Just v -> ContPayload <$> parseJSON v + parseJSON = withObject "Payload" $ \o -> do + o .: "exec" >>= \case + Nothing -> o .: "cont" >>= \case + Nothing -> fail "Payload must be exec or cont" + Just cont -> return $ ContPayload cont + Just exec -> return $ ExecPayload exec payloadCode :: Payload -> Text payloadCode (ExecPayload e) = _exec_code e diff --git a/lib/ChainwebData/Pagination.hs b/lib/ChainwebData/Pagination.hs index 41d1c18..aa77262 100644 --- a/lib/ChainwebData/Pagination.hs +++ b/lib/ChainwebData/Pagination.hs @@ -20,7 +20,7 @@ import Data.Aeson --import Data.ByteString.Lazy (ByteString) import Data.Default import Data.Function (on) -import Data.List +import qualified Data.List as List import Data.Ord import Data.Map (Map) import qualified Data.Map as M @@ -207,8 +207,8 @@ prune -> PaginationCache k (PaginationResults v) -> PaginationCache k (PaginationResults v) prune n m = - M.fromList $ map g $ groupBy ((==) `on` fst) $ sortBy (comparing fst) $ - drop n $ sortBy (comparing $ _prTimestamp . _pvValue . snd) $ + M.fromList $ map g $ List.groupBy ((==) `on` fst) $ List.sortBy (comparing fst) $ + drop n $ List.sortBy (comparing $ _prTimestamp . _pvValue . snd) $ concatMap f $ M.toList m where f (k,vs) = map (k,) vs