packages feed

yesod-static-streamly 0.1.1.1 → 0.1.2.1

raw patch · 4 files changed

+260/−4 lines, 4 filesdep +base64-bytestringdep +byteabledep +containersPVP ok

version bump matches the API change (PVP)

Dependencies added: base64-bytestring, byteable, containers, cryptohash, cryptonite, memory, mtl, wai-app-static, yesod-static

API changes (from Hackage documentation)

+ Yesod.Static.Streamly: staticStreamly :: FilePath -> IO Static
+ Yesod.Static.Streamly.Internal: base64Streamly :: ByteString -> String
+ Yesod.Static.Streamly.Internal: base64md5FileStreamly :: FilePath -> IO String
+ Yesod.Static.Streamly.Internal: cachedETagLookupStreamly :: FilePath -> IO ETagLookup
+ Yesod.Static.Streamly.Internal: getFileListPiecesStreamly :: FilePath -> IO [[String]]
+ Yesod.Static.Streamly.Internal: hashFileStreamly :: (MonadIO m, HashAlgorithm hash) => FilePath -> m (Digest hash)
+ Yesod.Static.Streamly.Internal: mkHashMapStreamly :: FilePath -> IO (Map FilePath ByteString)
+ Yesod.Static.Streamly.Internal: notHiddenStreamly :: FilePath -> Bool
+ Yesod.Static.Streamly.Internal: pathFromRawPiecesStreamly :: FilePath -> [String] -> FilePath
+ Yesod.Static.Streamly.Internal: sinkHashStreamly :: (Monad m, HashAlgorithm hash) => ByteString -> m (Digest hash)

Files

CHANGELOG.md view
@@ -17,3 +17,9 @@  * Third version. * First non-candidate version (version bump).++## 0.1.2.1 -- 2023-07-07++* Fourth version.+* Added staticStreamly function in Yesod.Static.Streamly.+* Added supporting functionality for staticStreamly in Yesod.Static.Streamly.Internal.
+ src/Yesod/Static/Streamly.hs view
@@ -0,0 +1,33 @@+{-# LANGUAGE FlexibleContexts  #-}+{-# LANGUAGE OverloadedLists   #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PackageImports    #-}++-- |+-- Module      :  Yesod.Static.Streamly+-- Copyright   :  (c) Matthew Mosior 2023+-- License     :  BSD-style+-- Maintainer  :  mattm.github@gmail.com+-- Portability :  portable+--+-- = Streamly-based alternative functionality for Yesod.Static.+--+-- This library utilizes [Streamly](https://hackage.haskell.org/package/streamly-core)'s superb performance characteristics to replace some of [Yesod](https://hackage.haskell.org/package/yesod)'s functionality with streamly-based functionality.++module Yesod.Static.Streamly ( -- * Yesod.Static Replacement functions+                               staticStreamly+                             ) where++import Yesod.Static.Streamly.Internal++import Network.Wai.Application.Static (webAppSettingsWithLookup)+import Yesod.Static++-- | A more performant replacement of+-- [static](https://hackage.haskell.org/package/yesod-static-1.6.1.0/docs/Yesod-Static.html#v:static)+-- found in [Yesod.Static](https://hackage.haskell.org/package/yesod-static-1.6.1.0/docs/Yesod-Static.html#g:2).+staticStreamly :: FilePath+               -> IO Static+staticStreamly dir = do+  hashLookup <- cachedETagLookupStreamly dir+  return $ Static $ webAppSettingsWithLookup dir hashLookup
+ src/Yesod/Static/Streamly/Internal.hs view
@@ -0,0 +1,200 @@+{-# LANGUAGE FlexibleContexts  #-}+{-# LANGUAGE OverloadedLists   #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PackageImports    #-}++-- |+-- Module      :  Yesod.Static.Streamly.Internal+-- Copyright   :  (c) Matthew Mosior 2023+-- License     :  BSD-style+-- Maintainer  :  mattm.github@gmail.com+-- Portability :  portable+--+-- = WARNING+--+-- This module is considered __internal__.+--+-- The Package Versioning Policy __does not apply__.+--+-- The contents of this module may change __in any way whatsoever__+-- and __without any warning__ between minor versions of this package.+--+-- Authors importing this library are expected to track development+-- closely.+--+-- All credit goes to the author(s)/maintainer(s) of the+-- [containers](https://hackage.haskell.org/package/containers) library+-- for the above warning text.+--+-- = Description+--+-- This library utilizes [Streamly](https://hackage.haskell.org/package/streamly-core)'s superb performance characteristics to replace some of [Yesod](https://hackage.haskell.org/package/yesod)'s functionality with streamly-based functionality.++module Yesod.Static.Streamly.Internal ( -- * Yesod.Static Replacement functions+                                        cachedETagLookupStreamly,+                                        mkHashMapStreamly,+                                        notHiddenStreamly,+                                        getFileListPiecesStreamly,+                                        pathFromRawPiecesStreamly,+                                        base64md5FileStreamly,+                                        base64Streamly,+                                        hashFileStreamly,+                                        sinkHashStreamly+                                      ) where++import Control.Monad.State.Lazy+import "cryptonite" Crypto.Hash (hash,Digest,MD5)+import "cryptonite" Crypto.Hash.IO (HashAlgorithm)+import "cryptohash" Crypto.Hash (hashInit,hashUpdate)+import "cryptohash" Crypto.Hash.Types+import Data.Byteable+import qualified Data.ByteArray as ByteArray+import Data.ByteString as B (ByteString)+import qualified Data.ByteString.Base64+import qualified Data.ByteString.Char8 as S8+import Data.ByteString.Lazy as L (fromStrict)+import Data.ByteString.Lazy.Internal as IL (ByteString(..))+import Data.List (foldl',sort)+import qualified Data.Map as M+import qualified Streamly.Data.Stream as S+import qualified Streamly.Data.Fold as Fold+import Streamly.External.ByteString as StreamlyByteString+import Streamly.Internal.Data.Stream.StreamD.Type (Step(..))+import Streamly.Internal.Data.Unfold.Type as StreamU (Unfold(..))+import Streamly.FileSystem.Handle as StreamlyFile (chunkReader)+import System.Directory (doesDirectoryExist,doesFileExist,getDirectoryContents)+import System.IO (openFile, IOMode(ReadMode))+import WaiAppStatic.Storage.Filesystem (ETagLookup)++-- | A replacement of+-- [cachedETagLookup](https://hackage.haskell.org/package/yesod-static-1.6.1.0/docs/src/Yesod.Static.html#cachedETagLookup).+cachedETagLookupStreamly :: FilePath+                         -> IO ETagLookup+cachedETagLookupStreamly dir = do+  etags <- mkHashMapStreamly dir+  return $ (\f -> return $ M.lookup f etags)++-- | A replacement of+-- [mkHashMap](https://hackage.haskell.org/package/yesod-static-1.6.1.0/docs/src/Yesod.Static.html#mkHashMap).+mkHashMapStreamly :: FilePath+                  -> IO (M.Map FilePath S8.ByteString)+mkHashMapStreamly dir = do+  fs <- getFileListPiecesStreamly dir+  hashAlist fs >>= return . M.fromList+    where+      hashAlist :: [[String]]+                -> IO [(FilePath,S8.ByteString)]+      hashAlist fs = mapM hashPair fs+          where+            hashPair :: [String]+                     -> IO (FilePath,S8.ByteString)+            hashPair pieces = do let file = pathFromRawPiecesStreamly dir pieces+                                 h <- base64md5FileStreamly file+                                 return (file, S8.pack h)++-- | A replacement of+-- [notHidden](https://hackage.haskell.org/package/yesod-static-1.6.1.0/docs/src/Yesod.Static.html#notHidden).+notHiddenStreamly :: FilePath+                  -> Bool+notHiddenStreamly "tmp" = False+notHiddenStreamly s     =+  case s of+    '.':_ -> False+    _     -> True++-- | A replacement of+-- [getFileListPieces](https://hackage.haskell.org/package/yesod-static-1.6.1.0/docs/src/Yesod.Static.html#getFileListPieces).+getFileListPiecesStreamly :: FilePath+                          -> IO [[String]]+getFileListPiecesStreamly = flip evalStateT M.empty . flip go id+  where+    go :: String+       -> ([String] -> [String])+       -> StateT (M.Map String String) IO [[String]]+    go fp front = do+      allContents <- liftIO $ (sort . filter notHiddenStreamly) `fmap` getDirectoryContents fp+      let fullPath :: String -> String+          fullPath f = fp ++ '/' : f+      files <- liftIO $ filterM (doesFileExist . fullPath) allContents+      let files' = map (front . return) files+      files'' <- mapM dedupe files'+      dirs <- liftIO $ filterM (doesDirectoryExist . fullPath) allContents+      dirs' <- mapM (\f -> go (fullPath f) (front . (:) f)) dirs+      return $ concat $ files'' : dirs'+    +    -- Reuse data buffers for identical strings+    dedupe :: [String]+           -> StateT (M.Map String String) IO [String]+    dedupe = mapM dedupe'++    dedupe' :: String+            -> StateT (M.Map String String) IO String+    dedupe' s = do+      m <- get+      case M.lookup s m of+        Just s' -> return s'+        Nothing -> do put $ M.insert s s m+                      return s++-- | A replacement of+-- [pathFromRawPieces](https://hackage.haskell.org/package/yesod-static-1.6.1.0/docs/src/Yesod.Static.html#pathFromRawPieces).+pathFromRawPiecesStreamly :: FilePath+                          -> [String]+                          -> FilePath+pathFromRawPiecesStreamly =+  foldl' append+    where+      append a b = a ++ '/' : b++-- | A replacement of+-- [base64md5File](https://hackage.haskell.org/package/yesod-static-1.6.1.0/docs/src/Yesod.Static.html#base64md5File).+base64md5FileStreamly :: FilePath+                      -> IO String+base64md5FileStreamly = fmap (base64Streamly . encode) . hashFileStreamly+    where encode d = ByteArray.convert (d :: Crypto.Hash.Digest MD5)++-- | A replacement of+-- [base64](https://hackage.haskell.org/package/yesod-static-1.6.1.0/docs/src/Yesod.Static.html#base64).+base64Streamly :: B.ByteString+               -> String+base64Streamly = map tr+       . take 8+       . S8.unpack+       . Data.ByteString.Base64.encode+  where+    tr '+' = '-'+    tr '/' = '_'+    tr c   = c++-- | A more performant replacement of+-- [hashFile](https://hackage.haskell.org/package/cryptohash-conduit-0.1.1/docs/src/Crypto-Hash-Conduit.html#hashFile)+-- found in [Crypto.Hash.Conduit](https://hackage.haskell.org/package/cryptohash-conduit-0.1.1/docs/Crypto-Hash-Conduit.html).+hashFileStreamly :: (MonadIO m,Crypto.Hash.IO.HashAlgorithm hash)+                 => FilePath+                 -> m (Crypto.Hash.Digest hash)+hashFileStreamly fp = do+  handle <- liftIO $ openFile fp ReadMode+  let lazyfile = S.unfold StreamlyFile.chunkReader handle+  lazyfilebs <-  S.fold (Fold.foldl' (<>) mempty) $+                   fmap StreamlyByteString.fromArray lazyfile+  sinkHashStreamly lazyfilebs+++-- | A more performant replacement of+-- [sinkHash](https://hackage.haskell.org/package/cryptohash-conduit-0.1.1/docs/src/Crypto-Hash-Conduit.html#sinkHash)+-- found in [Crypto.Hash.Conduit](https://hackage.haskell.org/package/cryptohash-conduit-0.1.1/docs/Crypto-Hash-Conduit.html).+sinkHashStreamly :: (Monad m,Crypto.Hash.IO.HashAlgorithm hash)+                 => B.ByteString+                 -> m (Crypto.Hash.Digest hash)+sinkHashStreamly bscontent = do+  let lazybsf   = S.unfold (Unfold step seed) blcontent+  let lazybsff  = fmap (\x -> StreamlyByteString.toArray $ toBytes x) lazybsf+  lazybsfff     <- S.fold (Fold.foldl' (<>) mempty) $+                     fmap StreamlyByteString.fromArray lazybsff+  return $ hash lazybsfff+    where+      blcontent          = L.fromStrict bscontent+      ctx                = hashInit :: Context MD5+      seed               = return+      step (Chunk bs bl) = return $ Yield (hashUpdate ctx bs) bl+      step Empty         = return Stop 
yesod-static-streamly.cabal view
@@ -20,14 +20,20 @@ -- PVP summary:     +-+------- breaking API changes --                  | | +----- non-breaking API additions --                  | | | +--- code changes with no API change-version:            0.1.1.1+version:            0.1.2.1  -- A short (one-line) description of the package. synopsis:           A streamly-based library providing performance-focused alternatives for functionality found in yesod-static. +-- Issue tracker.+bug-reports:        https://github.com/Matthew-Mosior/yesod-static-streamly/issues+ -- A longer description of the package. description:        API docs and the README are available at https://github.com/Matthew-Mosior/yesod-static-streamly  +-- Package homepage.+homepage:           https://github.com/Matthew-Mosior/yesod-static-streamly+ -- The license under which the package is released. license:            BSD-3-Clause @@ -61,7 +67,9 @@     import:           warnings      -- Modules exported by the library.-    exposed-modules:  Yesod.Default.Util.Streamly+    exposed-modules:  Yesod.Default.Util.Streamly,+                      Yesod.Static.Streamly.Internal,+                      Yesod.Static.Streamly      -- Modules included in this library but not exported.     -- other-modules:@@ -71,12 +79,21 @@      -- Other library packages from which modules are imported.     build-depends:    base ^>=4.17.1.0,+                      base64-bytestring >= 1.2.1 && < 1.3,+                      byteable >= 0.1.1 && < 0.2,                       bytestring >= 0.11.4 && < 0.12,+                      containers >= 0.6.7 && < 0.7,+                      cryptohash >= 0.11.9 && < 0.12,+                      cryptonite >= 0.30 && < 0.31,                       directory >= 1.3.7 && < 1.4,+                      memory >= 0.18.0 && < 0.19,+                      mtl >= 2.2.2 && < 2.3,                       streamly-bytestring >= 0.2.0 && < 0.3,                       streamly-core >= 0.1.0 && < 0.2,                       text >= 2.0.2 && < 2.1,-                      yesod-core >= 1.6.24 && < 1.7+                      wai-app-static >= 3.1.7 && < 3.2,+                      yesod-core >= 1.6.24 && < 1.7,+                      yesod-static >= 1.6.1 && < 1.7      -- Directories containing source files.     hs-source-dirs:   src@@ -85,7 +102,7 @@     default-language: Haskell2010      -- Set highest (reasonable) optimization level to compile with.-    ghc-options: -O2 -fprof-auto+    --ghc-options: -O2 -fprof-auto  test-suite yesod-static-streamly-test     -- Import common warning flags.