From ca1a3d1e6e181fffd1030c6c49853751be8e9e43 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Hamal=20Dvo=C5=99=C3=A1k?= <mordae@anilinux.org>
Date: Thu, 13 Jan 2022 14:48:00 +0100
Subject: [PATCH] Get ready for GHC 9.2
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Jan Hamal Dvořák <mordae@anilinux.org>
---
 hikaru.cabal           | 204 ++++++++++++++++++++---------------------
 lib/Hikaru/Action.hs   | 101 ++++++++++----------
 lib/Hikaru/CSRF.hs     |  36 +++++---
 lib/Hikaru/Form.hs     |  28 +++---
 lib/Hikaru/Localize.hs |   4 +-
 lib/Hikaru/Media.hs    |  10 +-
 lib/Hikaru/Route.hs    |   7 +-
 7 files changed, 195 insertions(+), 195 deletions(-)

diff --git a/hikaru.cabal b/hikaru.cabal
index 1107d44..8559e1f 100644
--- a/hikaru.cabal
+++ b/hikaru.cabal
@@ -1,17 +1,14 @@
-cabal-version: 3.0
-
-name: hikaru
-version: 0.2.0.0
-homepage: https://github.com/mordae/hikaru#readme
-bug-reports: https://github.com/mordae/hikaru/issues
-
-license: MIT
-license-file: LICENSE
-copyright: Jan Hamal Dvořák
-maintainer: mordae@anilinux.org
-author: Jan Hamal Dvořák
-
-synopsis: Haskell web application framework
+cabal-version:      3.0
+name:               hikaru
+version:            0.3.0
+license:            MIT
+license-file:       LICENSE
+copyright:          Jan Hamal Dvořák
+maintainer:         mordae@anilinux.org
+author:             Jan Hamal Dvořák
+homepage:           https://github.com/mordae/hikaru#readme
+bug-reports:        https://github.com/mordae/hikaru/issues
+synopsis:           Haskell web application framework
 description:
     Hikaru is a small framework for web applications.
     It provides applicative routing, content negotiation
@@ -20,87 +17,15 @@ description:
     (It is also a Star Trek reference, which is apparently
     a requirement for this class of software.)
 
-category: Web
-build-type: Simple
+category:           Web
+build-type:         Simple
 extra-source-files: README.md
 
 source-repository head
-    type: git
+    type:     git
     location: https://github.com/mordae/hikaru
 
-common common
-    default-language: Haskell2010
-
-    default-extensions:
-        AllowAmbiguousTypes
-        BangPatterns
-        BlockArguments
-        ConstraintKinds
-        DataKinds
-        DefaultSignatures
-        DeriveGeneric
-        DuplicateRecordFields
-        EmptyDataDecls
-        FlexibleContexts
-        FlexibleInstances
-        GeneralizedNewtypeDeriving
-        ImportQualifiedPost
-        KindSignatures
-        LambdaCase
-        MultiParamTypeClasses
-        NamedFieldPuns
-        NoImplicitPrelude
-        OverloadedStrings
-        PolyKinds
-        RankNTypes
-        RecordWildCards
-        ScopedTypeVariables
-        StandaloneDeriving
-        TupleSections
-        TypeApplications
-        TypeFamilies
-        TypeOperators
-        UndecidableInstances
-
-    ghc-options:
-        -Wall
-        -Wcompat
-        -Wincomplete-uni-patterns
-        -Wincomplete-record-updates
-        -Widentities
-        -Wredundant-constraints
-
-    build-depends:
-      , aeson              >=1.4  && <1.6
-      , attoparsec         >=0.13 && <0.15
-      , base               >=4.13 && <5
-      , binary             >=0.8  && <0.9
-      , bytestring         >=0.10 && <0.12
-      , case-insensitive   >=1.2  && <1.3
-      , containers         >=0.6  && <0.7
-      , cookie             >=0.4  && <0.5
-      , cryptonite         >=0.26 && <0.29
-      , foreign-store      >=0.2  && <0.3
-      , http-types         >=0.12 && <0.13
-      , hvect              >=0.4  && <0.5
-      , lucid              >=2.9  && <2.10
-      , memory             >=0.15 && <0.16
-      , mtl                >=2.2  && <2.3
-      , praha              >=0.1  && <0.2
-      , praha-config       >=0.1  && <0.2
-      , resourcet          >=1.2  && <1.3
-      , text               >=1.2  && <1.3
-      , time               >=1.9  && <1.12
-      , unliftio           >=0.2  && <0.3
-      , wai                >=3.2  && <3.3
-      , wai-extra          >=3.0  && <3.2
-      , wai-websockets     >=3.0  && <3.1
-      , websockets         >=0.12 && <0.13
-
-
 library
-    import: common
-
     exposed-modules:
         Hikaru
         Hikaru.Develop
@@ -115,21 +40,57 @@ library
         Hikaru.Route
         Hikaru.Types
 
-    hs-source-dirs: lib
-    other-modules:
-        Paths_hikaru
+    hs-source-dirs:     lib
+    other-modules:      Paths_hikaru
+    default-language:   Haskell2010
+    default-extensions:
+        AllowAmbiguousTypes BangPatterns BlockArguments ConstraintKinds
+        DataKinds DefaultSignatures DeriveGeneric DuplicateRecordFields
+        EmptyDataDecls FlexibleContexts FlexibleInstances
+        GeneralizedNewtypeDeriving ImportQualifiedPost KindSignatures
+        LambdaCase MultiParamTypeClasses NamedFieldPuns NoImplicitPrelude
+        OverloadedStrings PolyKinds RankNTypes RecordWildCards
+        ScopedTypeVariables StandaloneDeriving TupleSections
+        TypeApplications TypeFamilies TypeOperators UndecidableInstances
+        DisambiguateRecordFields NoFieldSelectors OverloadedRecordDot
 
     ghc-options:
+        -Wall -Wcompat -Wincomplete-uni-patterns
+        -Wincomplete-record-updates -Widentities -Wredundant-constraints
         -Wunused-packages
 
-test-suite spec
-    import: common
+    build-depends:
+        aeson >=1.4,
+        attoparsec >=0.13,
+        base >=4.13 && <5,
+        binary >=0.8,
+        bytestring >=0.10,
+        case-insensitive >=1.2,
+        containers >=0.6,
+        cookie >=0.4,
+        foreign-store >=0.2,
+        http-types >=0.12,
+        hvect >=0.4,
+        lucid >=2.9,
+        mtl >=2.2,
+        praha >=0.1,
+        praha-config >=0.1,
+        resourcet >=1.2,
+        text >=1.2,
+        time >=1.9,
+        unliftio >=0.2,
+        wai >=3.2,
+        wai-extra >=3.0,
+        wai-websockets >=3.0,
+        websockets >=0.12,
+        HsOpenSSL >=0.11
 
-    type: exitcode-stdio-1.0
-    main-is: Spec.hs
-    build-tool-depends: hspec-discover:hspec-discover -any
-    cpp-options: -DTEST
-    hs-source-dirs: test
+test-suite spec
+    type:               exitcode-stdio-1.0
+    main-is:            Spec.hs
+    build-tool-depends: hspec-discover:hspec-discover
+    cpp-options:        -DTEST
+    hs-source-dirs:     test
     other-modules:
         Hikaru.Demo
         Hikaru.DemoSpec
@@ -137,11 +98,46 @@ test-suite spec
         Hikaru.Test
         Paths_hikaru
 
+    default-language:   Haskell2010
+    default-extensions:
+        AllowAmbiguousTypes BangPatterns BlockArguments ConstraintKinds
+        DataKinds DefaultSignatures DeriveGeneric DuplicateRecordFields
+        EmptyDataDecls FlexibleContexts FlexibleInstances
+        GeneralizedNewtypeDeriving ImportQualifiedPost KindSignatures
+        LambdaCase MultiParamTypeClasses NamedFieldPuns NoImplicitPrelude
+        OverloadedStrings PolyKinds RankNTypes RecordWildCards
+        ScopedTypeVariables StandaloneDeriving TupleSections
+        TypeApplications TypeFamilies TypeOperators UndecidableInstances
+        DisambiguateRecordFields NoFieldSelectors OverloadedRecordDot
+
     ghc-options:
-        -threaded
-        -rtsopts
-        -with-rtsopts=-N
+        -Wall -Wcompat -Wincomplete-uni-patterns
+        -Wincomplete-record-updates -Widentities -Wredundant-constraints
+        -threaded -rtsopts -with-rtsopts=-N
 
     build-depends:
-        hikaru -any
-      , hspec -any
+        aeson >=1.4,
+        attoparsec >=0.13,
+        base >=4.13 && <5,
+        binary >=0.8,
+        bytestring >=0.10,
+        case-insensitive >=1.2,
+        containers >=0.6,
+        cookie >=0.4,
+        foreign-store >=0.2,
+        http-types >=0.12,
+        hvect >=0.4,
+        lucid >=2.9,
+        mtl >=2.2,
+        praha >=0.1,
+        praha-config >=0.1,
+        resourcet >=1.2,
+        text >=1.2,
+        time >=1.9,
+        unliftio >=0.2,
+        wai >=3.2,
+        wai-extra >=3.0,
+        wai-websockets >=3.0,
+        websockets >=0.12,
+        hikaru,
+        hspec
diff --git a/lib/Hikaru/Action.hs b/lib/Hikaru/Action.hs
index 71cf179..077d1b4 100644
--- a/lib/Hikaru/Action.hs
+++ b/lib/Hikaru/Action.hs
@@ -122,7 +122,6 @@ where
   import Control.Monad.Trans.Resource
   import Data.Aeson
   import Data.Binary.Builder
-  import Data.ByteArray.Encoding
   import Data.ByteString.Char8 (words, span, drop)
   import Data.CaseInsensitive (mk)
   import Data.Dynamic
@@ -135,6 +134,7 @@ where
   import Network.HTTP.Types.Status
   import Network.Wai
   import Network.Wai.Handler.WebSockets
+  import OpenSSL.EVP.Base64
   import System.IO.Unsafe
   import UnliftIO
   import Web.Cookie
@@ -250,15 +250,15 @@ where
     bracket_ (return ()) (finalize env) do
       _   <- run env
 
-      status  <- readIORef $ aeRespStatus  $ env
-      headers <- readIORef $ aeRespHeaders $ env
-      make    <- readIORef $ aeRespMaker   $ env
+      status  <- readIORef $ env.aeRespStatus
+      headers <- readIORef $ env.aeRespHeaders
+      make    <- readIORef $ env.aeRespMaker
 
       resp (make status headers)
 
     where
       finalize :: ActionEnv -> IO ()
-      finalize = join . readIORef . aeFinalize
+      finalize = join . readIORef . (.aeFinalize)
 
 
   -- |
@@ -320,7 +320,7 @@ where
   -- Obtain the original 'Request'.
   --
   getRequest :: (MonadAction m) => m Request
-  getRequest = aeRequest <$> getActionEnv
+  getRequest = (.aeRequest) <$> getActionEnv
 
 
   -- |
@@ -553,14 +553,14 @@ where
   -- length is not known beforehand.
   --
   setBodyLimit :: (MonadAction m) => Int64 -> m ()
-  setBodyLimit = setActionField aeBodyLimit
+  setBodyLimit = setActionField (.aeBodyLimit)
 
 
   -- |
   -- Return the payload size limit set by 'setBodyLimit'.
   --
   getBodyLimit :: (MonadAction m) => m Int64
-  getBodyLimit = getActionField aeBodyLimit
+  getBodyLimit = getActionField (.aeBodyLimit)
 
 
   -- |
@@ -587,8 +587,8 @@ where
   --
   getBodyChunkIO :: (MonadAction m) => m (IO ByteString)
   getBodyChunkIO = do
-    limit    <- getActionField aeBodyLimit
-    counter  <- aeBodyCounter <$> getActionEnv
+    limit    <- getActionField (.aeBodyLimit)
+    counter  <- (.aeBodyCounter) <$> getActionEnv
     getChunk <- getRequestBodyChunk <$> getRequest
 
     return do
@@ -652,7 +652,7 @@ where
   getJSON :: (MonadAction m, FromJSON a) => m a
   getJSON = do
     -- First check out our stash.
-    cache <- getActionField aeBody
+    cache <- getActionField (.aeBody)
 
     case cache of
       -- This is ideal, we already have what we need.
@@ -670,7 +670,7 @@ where
            else abort unsupportedMediaType415 [] "Send some JSON!"
 
         -- Taint and read.
-        setActionField aeBody BodyTainted
+        setActionField (.aeBody) BodyTainted
         body <- getBodyRaw
 
         -- Try to parse.
@@ -679,7 +679,7 @@ where
                    Right value -> return value
 
         -- Cache and return.
-        setActionField aeBody (BodyJSON value)
+        setActionField (.aeBody) (BodyJSON value)
 
         -- Parse to the output type.
         case fromJSON value of
@@ -781,7 +781,7 @@ where
   --
   getFormData :: (MonadAction m) => m FormData
   getFormData = do
-    cache <- getActionField aeBody
+    cache <- getActionField (.aeBody)
 
     case cache of
       -- This is ideal, we already have what we need.
@@ -807,7 +807,7 @@ where
             let form = adaptForm form'
 
             -- Cache and return.
-            setActionField aeBody (BodyForm form)
+            setActionField (.aeBody) (BodyForm form)
             return form
 
       -- Now this is bad. We have already read the body,
@@ -837,7 +837,7 @@ where
   --
   getBody :: (MonadAction m) => m LBS.ByteString
   getBody = do
-    cache <- getActionField aeBody
+    cache <- getActionField (.aeBody)
 
     case cache of
       -- This is ideal, we already have what we need.
@@ -846,14 +846,14 @@ where
       -- Body has not been parsed yet. This is very good.
       BodyUnparsed -> do
         -- Taint and read.
-        setActionField aeBody BodyTainted
+        setActionField (.aeBody) BodyTainted
         body <- getBodyRaw
 
         -- Force it whole.
         _len  <- LBS.length <$> pure body
 
         -- Cache and return.
-        setActionField aeBody (BodyBytes body)
+        setActionField (.aeBody) (BodyBytes body)
         return body
 
       -- Now this is bad. We have already read the body,
@@ -869,21 +869,21 @@ where
   -- Set the status to use when building our 'Response'.
   --
   setStatus :: (MonadAction m) => Status -> m ()
-  setStatus = setActionField aeRespStatus
+  setStatus = setActionField (.aeRespStatus)
 
 
   -- |
   -- Set headers to use when building our 'Response'.
   --
   setHeaders :: (MonadAction m) => ResponseHeaders -> m ()
-  setHeaders = setActionField aeRespHeaders
+  setHeaders = setActionField (.aeRespHeaders)
 
 
   -- |
   -- Append a single 'Response' header without checking.
   --
   addHeader :: (MonadAction m) => HeaderName -> ByteString -> m ()
-  addHeader n v = modifyActionField aeRespHeaders ((n, v) :)
+  addHeader n v = modifyActionField (.aeRespHeaders) ((n, v) :)
 
 
   -- |
@@ -891,7 +891,7 @@ where
   -- If the header has been given multiple times, leave only one.
   --
   setHeader :: (MonadAction m) => HeaderName -> ByteString -> m ()
-  setHeader n v = modifyActionField aeRespHeaders update
+  setHeader n v = modifyActionField (.aeRespHeaders) update
     where
       update hs = (n, v) : deleteBy headerEq (n, v) hs
 
@@ -919,7 +919,7 @@ where
   --
   modifyHeader :: (MonadAction m)
                => HeaderName -> (Maybe ByteString -> ByteString) -> m ()
-  modifyHeader n fn = modifyActionField aeRespHeaders update
+  modifyHeader n fn = modifyActionField (.aeRespHeaders) update
     where
       update hs = (n, v') : deleteBy headerEq (n, v') hs
         where v' = fn (lookup n hs)
@@ -1017,7 +1017,7 @@ where
   --
   setResponseFile :: (MonadAction m) => FilePath -> Maybe FilePart -> m ()
   setResponseFile fp mfp = do
-    setActionField aeRespMaker \st hs -> responseFile st hs fp mfp
+    setActionField (.aeRespMaker) \st hs -> responseFile st hs fp mfp
 
 
   -- |
@@ -1025,7 +1025,7 @@ where
   --
   setResponseBuilder :: (MonadAction m) => Builder -> m ()
   setResponseBuilder bld = do
-    setActionField aeRespMaker \st hs -> responseBuilder st hs bld
+    setActionField (.aeRespMaker) \st hs -> responseBuilder st hs bld
 
 
   -- |
@@ -1033,7 +1033,7 @@ where
   --
   setResponseBS :: (MonadAction m) => LBS.ByteString -> m ()
   setResponseBS bs = do
-    setActionField aeRespMaker \st hs -> responseLBS st hs bs
+    setActionField (.aeRespMaker) \st hs -> responseLBS st hs bs
 
 
   -- |
@@ -1069,7 +1069,7 @@ where
   --
   setResponseStream :: (MonadAction m) => StreamingBody -> m ()
   setResponseStream strm = do
-    setActionField aeRespMaker \st hs -> responseStream st hs strm
+    setActionField (.aeRespMaker) \st hs -> responseStream st hs strm
 
 
   -- |
@@ -1091,7 +1091,7 @@ where
                  -> Response
                  -> m ()
   setResponseRaw comm resp = do
-    setActionField aeRespMaker \_st _hs -> responseRaw comm resp
+    setActionField (.aeRespMaker) \_st _hs -> responseRaw comm resp
 
 
   -- WebSockets --------------------------------------------------------------
@@ -1107,7 +1107,7 @@ where
   -- to communicate in one way only.
   --
   setFrameLimit :: (MonadAction m) => Int64 -> m ()
-  setFrameLimit = setActionField aeFrameLimit
+  setFrameLimit = setActionField (.aeFrameLimit)
 
 
   -- |
@@ -1122,7 +1122,7 @@ where
   -- Single message may or may not consist of multiple frames.
   --
   setMessageLimit :: (MonadAction m) => Int64 -> m ()
-  setMessageLimit = setActionField aeMsgLimit
+  setMessageLimit = setActionField (.aeMsgLimit)
 
 
   -- |
@@ -1133,14 +1133,14 @@ where
   -- Sets up an automatic keep-alive with a 30s ping interval.
   --
   setResponseWS :: (MonadAction m) => WebSocket () -> m ()
-  setResponseWS ws = do
+  setResponseWS WebSocket{runWebSocket} = do
     -- First check the body situation.
-    body <- getActionField aeBody
+    body <- getActionField (.aeBody)
 
     case body of
       BodyUnparsed -> do
-        frameLimit   <- WS.SizeLimit <$> getActionField aeFrameLimit
-        messageLimit <- WS.SizeLimit <$> getActionField aeMsgLimit
+        frameLimit   <- WS.SizeLimit <$> getActionField (.aeFrameLimit)
+        messageLimit <- WS.SizeLimit <$> getActionField (.aeMsgLimit)
 
         let opts = WS.defaultConnectionOptions
                      { WS.connectionFramePayloadSizeLimit = frameLimit
@@ -1149,8 +1149,8 @@ where
 
         req <- getRequest
 
-        setActionField aeBody BodyWebSocket
-        setActionField aeRespMaker \_st _hs ->
+        setActionField (.aeBody) BodyWebSocket
+        setActionField (.aeRespMaker) \_st _hs ->
           case websocketsApp opts app req of
             Nothing   -> responseLBS status400 [] "WebSocket Expected"
             Just resp -> resp
@@ -1164,7 +1164,7 @@ where
         void do
           conn <- WS.acceptRequest pc
           WS.withPingThread conn 30 (return ()) do
-            runReaderT (unWebSocket ws) conn
+            runReaderT runWebSocket conn
 
 
   -- |
@@ -1172,7 +1172,7 @@ where
   --
   newtype WebSocket a
     = WebSocket
-      { unWebSocket    :: ReaderT WS.Connection IO a
+      { runWebSocket   :: ReaderT WS.Connection IO a
       }
     deriving (MonadUnliftIO, MonadIO, Monad, Applicative, Functor)
 
@@ -1247,7 +1247,7 @@ where
   -- the localization tools found in the "Hikaru.Localize" module.
   --
   getLanguages :: (MonadAction m) => m [Text]
-  getLanguages = getActionField aeLanguages
+  getLanguages = getActionField (.aeLanguages)
 
 
   -- |
@@ -1256,7 +1256,7 @@ where
   -- See 'getLanguages' above for more information.
   --
   setLanguages :: (MonadAction m) => [Text] -> m ()
-  setLanguages = setActionField aeLanguages
+  setLanguages = setActionField (.aeLanguages)
 
 
   -- Cacheing ----------------------------------------------------------------
@@ -1279,12 +1279,12 @@ where
   --
   withCache :: (MonadAction m, Typeable a) => Text -> m a -> m a
   withCache key makeValue = do
-    cache <- getActionField aeCache
+    cache <- getActionField (.aeCache)
 
     case fromDynamic =<< Map.lookup key cache of
       Nothing -> do
         value <- makeValue
-        modifyActionField aeCache (Map.insert key (toDyn value))
+        modifyActionField (.aeCache) (Map.insert key (toDyn value))
         return value
 
       Just value -> do
@@ -1296,7 +1296,7 @@ where
   --
   dropCache :: (MonadAction m) => Text -> m ()
   dropCache key = do
-    modifyActionField aeCache (Map.delete key)
+    modifyActionField (.aeCache) (Map.delete key)
 
 
   -- |
@@ -1304,7 +1304,7 @@ where
   --
   dropCaches :: (MonadAction m) => m ()
   dropCaches = do
-    modifyActionField aeCache (const Map.empty)
+    modifyActionField (.aeCache) (const Map.empty)
 
 
   -- Finalizing --------------------------------------------------------------
@@ -1316,7 +1316,7 @@ where
   --
   registerFinalizer :: (MonadAction m) => IO a -> m ()
   registerFinalizer fin = do
-    modifyActionField aeFinalize (fin >>)
+    modifyActionField (.aeFinalize) (fin >>)
 
 
   -- Misc Utilities ----------------------------------------------------------
@@ -1337,18 +1337,13 @@ where
   cs2 (x, y) = (cs x, cs y)
 
 
-  decodeBase64 :: ByteString -> Either String ByteString
-  decodeBase64 bstr = convertFromBase Base64 bstr
-
-
   parseBasicAuth :: ByteString -> Maybe (Text, Text)
   parseBasicAuth value =
     case words value of
       [method, auth] | mk method == "Basic" -> do
-        case decodeBase64 auth of
-          Left _   -> Nothing
-          Right lp -> let (l, p) = span (/= ':') lp
-                       in Just (cs l, cs (drop 1 p))
+        let lp = decodeBase64BS (cs auth)
+            (l, p) = span (/= ':') lp
+         in Just (cs l, cs (drop 1 p))
 
       _otherwise -> Nothing
 
diff --git a/lib/Hikaru/CSRF.hs b/lib/Hikaru/CSRF.hs
index 7252242..0d1f2ec 100644
--- a/lib/Hikaru/CSRF.hs
+++ b/lib/Hikaru/CSRF.hs
@@ -18,18 +18,26 @@ where
   import Praha
   import Praha.Config.Environment
 
-  import Crypto.Hash
-  import Crypto.MAC.HMAC
-  import Crypto.Random.Entropy
+  import System.IO.Unsafe
+
+  import OpenSSL
+  import OpenSSL.Random
+  import OpenSSL.EVP.Base64
+  import OpenSSL.EVP.Digest
 
   import Data.Text (splitOn)
   import Data.Time.Clock.POSIX (getPOSIXTime)
 
-  import Data.ByteArray.Encoding
-
   import Hikaru.Action
 
 
+  sha256 :: Digest
+  sha256 = case unsafePerformIO (withOpenSSL (getDigestByName "sha256")) of
+             Nothing -> error "OpenSSL does not provide sha256"
+             Just dg -> dg
+  {-# NOINLINE sha256 #-}
+
+
   -- |
   -- Generate an anti-CSRF token to be used with forms.
   --
@@ -40,7 +48,7 @@ where
     now    <- getTimestamp
     secret <- getSecret
 
-    let signature = sign now secret
+    let signature = sign secret now
      in return $ mconcat [ tshow now, ":", signature ]
 
 
@@ -61,7 +69,7 @@ where
             secret <- getConfigDefault "CSRF_SECRET" ""
 
             if timestamp + valid >= now
-               then return (sign timestamp secret == signature)
+               then return (sign secret timestamp == signature)
                else return False
 
           Nothing -> return False
@@ -91,12 +99,12 @@ where
         return secret
 
 
-  sign :: Int64 -> Text -> Text
-  sign timestamp secret = tshow $ hmacGetDigest digest
+  sign :: Text -> Int64 -> Text
+  sign secret timestamp = cs signature
     where
-      digest      = hmac timeBytes secretBytes :: HMAC SHA256
-      secretBytes = cs secret :: ByteString
-      timeBytes   = cs (show timestamp) :: ByteString
+      signature   = hmacBS sha256 secretBytes timeBytes
+      secretBytes = cs secret
+      timeBytes   = cs (show timestamp)
 
 
   -- |
@@ -105,9 +113,9 @@ where
   --
   generateSecret :: (MonadIO m) => Int -> m Text
   generateSecret n = do
-    (bstr :: ByteString) <- liftIO $ getEntropy n
+    (bstr :: ByteString) <- liftIO $ prandBytes n
 
-    let (bstr64 :: ByteString) = convertToBase Base64 bstr
+    let bstr64 = encodeBase64BS bstr
      in return (cs bstr64)
 
 
diff --git a/lib/Hikaru/Form.hs b/lib/Hikaru/Form.hs
index f3cae9b..b4d3b7e 100644
--- a/lib/Hikaru/Form.hs
+++ b/lib/Hikaru/Form.hs
@@ -177,8 +177,8 @@ where
     {-# INLINE pure #-}
 
     l <*> r = FormT do
-      l' <- runFormT l
-      r' <- runFormT r
+      l' <- l.runFormT
+      r' <- r.runFormT
       return $ l' <*> r'
     {-# INLINE (<*>) #-}
 
@@ -217,8 +217,8 @@ where
     {-# INLINE pure #-}
 
     l <*> r = ElementT do
-      l' <- runElementT l
-      r' <- runElementT r
+      l' <- l.runElementT
+      r' <- r.runElementT
       return $ l' <*> r'
     {-# INLINE (<*>) #-}
 
@@ -455,7 +455,7 @@ where
   -- @
   --
   newForm :: (Monad m) => Text -> Maybe o -> Form l m o -> m (View l)
-  newForm name orig = flip execStateT view . flip runReaderT env . runFormT
+  newForm name orig = flip execStateT view . flip runReaderT env . (.runFormT)
     where
       view = View [] []
       env  = Env { envPrefix   = name
@@ -583,7 +583,7 @@ where
 
     (res, new) <- lift $ lift do
       let base = Element label []
-       in flip runStateT base $ flip runReaderT env $ runElementT body
+       in flip runStateT base $ flip runReaderT env $ body.runElementT
 
     modify \view@View{..} ->
       view { viewElements = viewElements <> [new] }
@@ -608,14 +608,14 @@ where
     new <- lift $ lift do
       let field = InputField "hidden" Nothing text
           ctrst = ControlState name field [] [] val
-       in flip execStateT ctrst $ flip runReaderT env $ runControlT body
+       in flip execStateT ctrst $ flip runReaderT env $ body.runControlT
 
     ctrl <- lift $ lift $ buildControl env new
 
     modify \view@View{..} ->
       view { viewControls = viewControls <> [ctrl] }
 
-    return $ csValue new
+    return $ new.csValue
 
 
   -- |
@@ -636,14 +636,14 @@ where
     new <- lift $ lift do
       let field = InputField "hidden" Nothing text
           ctrst = ControlState name field [] [] val
-       in flip execStateT ctrst $ flip runReaderT env $ runControlT body
+       in flip execStateT ctrst $ flip runReaderT env $ body.runControlT
 
     ctrl <- lift $ lift $ buildControl env new
 
     modify \view@View{..} ->
       view { viewControls = viewControls <> [ctrl] }
 
-    return $ csValue new
+    return $ new.csValue
 
 
   -- |
@@ -681,14 +681,14 @@ where
     new <- lift $ lift do
       let field = InputField "text" Nothing text
           ctrst = ControlState name field [] [] val
-       in flip execStateT ctrst $ flip runReaderT env $ runControlT body
+       in flip execStateT ctrst $ flip runReaderT env $ body.runControlT
 
     ctrl <- lift $ lift $ buildControl env new
 
     modify \elt@Element{..} ->
       elt { elemControls = elemControls <> [ctrl] }
 
-    return $ csValue new
+    return $ new.csValue
 
 
   -- |
@@ -727,14 +727,14 @@ where
       let val'  = val <|> (getter <$> envValue)
           field = SelectField []
           ctrst = ControlState name field [] [] val'
-       in flip execStateT ctrst $ flip runReaderT env $ runControlT body
+       in flip execStateT ctrst $ flip runReaderT env $ body.runControlT
 
     ctrl <- lift $ lift $ buildControl env new
 
     modify \elt@Element{..} ->
       elt { elemControls = elemControls <> [ctrl] }
 
-    return $ csValue new
+    return $ new.csValue
 
 
   -- |
diff --git a/lib/Hikaru/Localize.hs b/lib/Hikaru/Localize.hs
index 3c54031..52234ea 100644
--- a/lib/Hikaru/Localize.hs
+++ b/lib/Hikaru/Localize.hs
@@ -157,8 +157,8 @@ where
     preferred <- getParamMaybe paramName
     previous  <- getCookieMaybe cookieName
     acceptable <- getAcceptLanguage
-                  <&> filter ((> 0) . quality)
-                  <&> map mainType
+                  <&> filter ((> 0) . (.quality))
+                  <&> map (.mainType)
 
     case preferred of
       Nothing   -> return ()
diff --git a/lib/Hikaru/Media.hs b/lib/Hikaru/Media.hs
index cc77e17..b8d3620 100644
--- a/lib/Hikaru/Media.hs
+++ b/lib/Hikaru/Media.hs
@@ -147,10 +147,10 @@ where
   -- False
   --
   matchMedia :: Media -> Media -> Bool
-  matchMedia l r = mainMatches && subMatches && quality r > 0.0
+  matchMedia l r = mainMatches && subMatches && r.quality > 0.0
     where
-      mainMatches = mainType l == mainType r || mainType r == "*"
-      subMatches  =  subType l == subType r  ||  subType r == "*"
+      mainMatches = l.mainType == r.mainType || r.mainType == "*"
+      subMatches  = l.subType  == r.subType  || r.subType  == "*"
 
 
   -- |
@@ -176,9 +176,9 @@ where
   selectMedia :: [Media] -> [Media] -> Maybe Media
   selectMedia ls rs = case best of
                         [ ]      -> Nothing
-                        (l, r):_ -> Just $ l { quality = quality r }
+                        (l, r):_ -> Just $ l { quality = r.quality }
     where
-      best = sortOn (negate . quality . snd) good
+      best = sortOn (negate . (.quality) . snd) good
       good = filter (uncurry matchMedia) prod
       prod = [(l, r) | l <- ls, r <- rs]
 
diff --git a/lib/Hikaru/Route.hs b/lib/Hikaru/Route.hs
index 6899bd4..cfd6b8d 100644
--- a/lib/Hikaru/Route.hs
+++ b/lib/Hikaru/Route.hs
@@ -221,9 +221,10 @@ where
   -- dispatching or with 'routeScore'.
   --
   (/?) :: Route ts a -> Appraisal -> Route ts a
-  (/?) r@Route{..} Appraisal{score = score', vary = vary'} =
-    r { vary  = nub (vary' <> vary)
-      , score = score' : score
+  (/?) r a =
+    r { vary  = nub (a.vary <> r.vary)
+      , score = a.score : r.score
+      , path  = r.path
       }
 
 
-- 
GitLab