Skip to content

Commit e9c0633

Browse files
committed
Add explicit imports
Also extend Haddock documentation. Also refer to https://casa.stackage.org. Also some minor refactoring and reformatting.
1 parent 3139c8c commit e9c0633

File tree

1 file changed

+90
-80
lines changed

1 file changed

+90
-80
lines changed

casa-client/src/Casa/Client.hs

Lines changed: 90 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,81 @@
11
{-# LANGUAGE CPP #-}
22
{-# LANGUAGE DeriveLift #-}
3+
{-# LANGUAGE LambdaCase #-}
34
{-# LANGUAGE OverloadedStrings #-}
45

5-
-- |
6+
-- | Types and functions for a client for Casa (Content-addressable Storage
7+
-- Archive).
68

79
module Casa.Client
810
( blobsSource
9-
, SourceConfig(..)
11+
, SourceConfig (..)
1012
, blobsSink
1113
, CasaRepoPrefix
1214
, parseCasaRepoPrefix
1315
, thParserCasaRepo
14-
, PushException(..)
15-
, PullException(..)
16+
, PushException (..)
17+
, PullException (..)
1618
) where
1719

1820
import Casa.Types
19-
import Control.Monad
20-
import Control.Monad.Catch
21-
import Control.Monad.IO.Class
21+
( BlobKey (..), blobKeyBinaryParser, blobKeyToBuilder )
22+
import Control.Monad ( (>=>), unless )
23+
import Control.Monad.Catch ( Exception, MonadThrow, throwM )
24+
import Control.Monad.IO.Class ( MonadIO )
2225
import Control.Monad.IO.Unlift
23-
import Control.Monad.Trans.Resource
26+
( MonadUnliftIO, UnliftIO (..), askUnliftIO )
27+
import Control.Monad.Trans.Resource ( MonadResource )
2428
import qualified Crypto.Hash as Crypto
25-
import Data.Aeson
29+
import Data.Aeson ( FromJSON (..) )
2630
import qualified Data.Attoparsec.ByteString as Atto
2731
import qualified Data.ByteArray as Mem
28-
import Data.ByteString (ByteString)
32+
import Data.ByteString ( ByteString )
2933
import qualified Data.ByteString as S
3034
import qualified Data.ByteString.Builder as SB
31-
import Data.Conduit
32-
import Data.Conduit.Attoparsec
33-
import Data.Conduit.ByteString.Builder
35+
import Data.Conduit ( ConduitT, (.|), await, transPipe, yield )
36+
import Data.Conduit.Attoparsec ( ParseError, conduitParserEither )
37+
import Data.Conduit.ByteString.Builder ( builderToByteString )
3438
import qualified Data.Conduit.List as CL
35-
import Data.HashMap.Strict (HashMap)
39+
import Data.HashMap.Strict ( HashMap )
3640
import qualified Data.HashMap.Strict as HM
3741
#if !MIN_VERSION_base(4,20,0)
3842
import Data.Foldable ( foldl' )
3943
#endif
40-
import Language.Haskell.TH
41-
import Language.Haskell.TH.Lift
44+
import Language.Haskell.TH ( Exp, Q )
45+
import Language.Haskell.TH.Lift ( Lift (..) )
4246
import Network.HTTP.Client.Conduit ( requestBodySourceChunked )
4347
import Network.HTTP.Simple
44-
import Network.HTTP.Types
45-
import Network.URI
48+
( Request, getResponseBody, getResponseStatus
49+
, httpNoBody, httpSource, parseRequest, setRequestBody
50+
, setRequestBodyLBS, setRequestMethod
51+
)
52+
import Network.HTTP.Types ( Status (..) )
53+
import Network.URI ( parseURI )
4654

4755
-- | An exception from blob consuming/sending.
4856
data PullException
4957
= AttoParseError ParseError
5058
| BadHttpStatus Status
5159
| TooManyReturnedKeys Int
5260
deriving Show
61+
5362
instance Exception PullException
5463

5564
-- | An exception from blob consuming/sending.
5665
newtype PushException
5766
= PushBadHttpStatus Status
5867
deriving Show
68+
5969
instance Exception PushException
6070

61-
-- | The URL prefix for a casa repo.
62-
-- Commonly: @https://casa.fpcomplete.com@
71+
-- | The URL prefix for a Casa repository, commonly @https://casa.stackage.org@.
6372
-- Parsers will strip out a trailing slash.
6473
newtype CasaRepoPrefix =
6574
CasaRepoPrefix String
6675
deriving (Show, Lift)
6776

6877
instance FromJSON CasaRepoPrefix where
69-
parseJSON j = do
70-
s <- parseJSON j
71-
either fail pure (parseCasaRepoPrefix s)
78+
parseJSON = parseJSON >=> (either fail pure . parseCasaRepoPrefix)
7279

7380
-- | TH compile-time parser.
7481
thParserCasaRepo :: String -> Q Exp
@@ -80,7 +87,7 @@ parseCasaRepoPrefix s =
8087
case parseURI s of
8188
Nothing ->
8289
Left
83-
"Invalid URI for repo. Should be a valid URI e.g. https://casa.fpcomplete.com"
90+
"Invalid URI for repository. Should be a valid URI e.g. https://casa.stackage.org"
8491
Just {} -> pure (CasaRepoPrefix (stripTrailing s))
8592
where
8693
stripTrailing = reverse . dropWhile (== '/') . reverse
@@ -91,11 +98,13 @@ casaServerVersion = "v1"
9198

9299
-- | Build the URL from a repo prefix.
93100
casaRepoPushUrl :: CasaRepoPrefix -> String
94-
casaRepoPushUrl (CasaRepoPrefix uri) = uri ++ "/" ++ casaServerVersion ++ "/push"
101+
casaRepoPushUrl (CasaRepoPrefix uri) =
102+
uri <> "/" <> casaServerVersion <> "/push"
95103

96104
-- | Build the URL from a repo prefix.
97105
casaRepoPullUrl :: CasaRepoPrefix -> String
98-
casaRepoPullUrl (CasaRepoPrefix uri) = uri ++ "/" ++ casaServerVersion ++ "/pull"
106+
casaRepoPullUrl (CasaRepoPrefix uri) =
107+
uri <> "/" <> casaServerVersion <> "/pull"
99108

100109
-- | A sink to push blobs to the server. Throws 'PushException'.
101110
blobsSink ::
@@ -113,14 +122,19 @@ blobsSink casaRepoUrl blobs = do
113122
where
114123
makeRequest (UnliftIO runInIO) =
115124
fmap
116-
(setRequestBody
117-
(requestBodySourceChunked
118-
(transPipe runInIO blobs .|
119-
CL.map
120-
(\v ->
121-
SB.word64BE (fromIntegral (S.length v)) <> SB.byteString v) .|
122-
builderToByteString)) .
123-
setRequestMethod "POST")
125+
( setRequestBody
126+
( requestBodySourceChunked
127+
( transPipe runInIO blobs
128+
.| CL.map
129+
( \v ->
130+
SB.word64BE (fromIntegral (S.length v))
131+
<> SB.byteString v
132+
)
133+
.| builderToByteString
134+
)
135+
)
136+
. setRequestMethod "POST"
137+
)
124138
(parseRequest (casaRepoPushUrl casaRepoUrl))
125139

126140
-- | Configuration for sourcing blobs from the server.
@@ -131,8 +145,8 @@ data SourceConfig =
131145
, sourceConfigBlobs :: !(HashMap BlobKey Int)
132146
-- ^ The blobs to pull.
133147
, sourceConfigMaxBlobsPerRequest :: !Int
134-
-- ^ Maximum number of blobs per request; we split requests into
135-
-- chunks of this number.
148+
-- ^ Maximum number of blobs per request; we split requests into chunks of
149+
-- this number.
136150
}
137151

138152
-- | Make a source of blobs from a URL. Throws 'PullException'.
@@ -142,55 +156,51 @@ blobsSource ::
142156
-> ConduitT i (BlobKey, ByteString) m ()
143157
blobsSource sourceConfig = do
144158
skeletonRequest <- makeSkeletonRequest
145-
source skeletonRequest (HM.toList (sourceConfigBlobs sourceConfig)) .| conduit .|
146-
consumer (HM.size (sourceConfigBlobs sourceConfig))
147-
where
148-
makeSkeletonRequest =
149-
fmap
150-
(setRequestMethod "POST")
151-
(parseRequest (casaRepoPullUrl (sourceConfigUrl sourceConfig)))
152-
source skeletonRequest blobs =
153-
unless
154-
(null blobs)
155-
(do httpSource
156-
filledRequest
157-
(\response ->
158-
case getResponseStatus response of
159-
Status 200 _ -> getResponseBody response
160-
status -> throwM (BadHttpStatus status))
161-
source skeletonRequest remainingBlobs)
162-
where
163-
(filledRequest, remainingBlobs) =
164-
setRequestBlobs sourceConfig blobs skeletonRequest
165-
conduit =
166-
conduitParserEither (blobKeyValueParser (sourceConfigBlobs sourceConfig))
167-
consumer remaining = do
168-
mkeyValue <- await
169-
case mkeyValue of
170-
Nothing -> pure ()
171-
Just (Left x) -> throwM (AttoParseError x)
172-
Just (Right (_position, keyValue)) ->
173-
if remaining == 0
174-
then throwM
175-
(TooManyReturnedKeys
176-
(HM.size (sourceConfigBlobs sourceConfig)))
177-
else do
178-
yield keyValue
179-
consumer (remaining - 1)
159+
source skeletonRequest scBlobsList .| conduit .| consumer scBlobsSize
160+
where
161+
makeSkeletonRequest =
162+
fmap
163+
(setRequestMethod "POST")
164+
(parseRequest (casaRepoPullUrl (sourceConfigUrl sourceConfig)))
165+
scBlobs = sourceConfigBlobs sourceConfig
166+
scBlobsList = HM.toList scBlobs
167+
scBlobsSize = HM.size scBlobs
168+
source skeletonRequest blobs =
169+
unless (null blobs) $ do
170+
httpSource filledRequest $ \response ->
171+
case getResponseStatus response of
172+
Status 200 _ -> getResponseBody response
173+
status -> throwM (BadHttpStatus status)
174+
source skeletonRequest remainingBlobs
175+
where
176+
(filledRequest, remainingBlobs) =
177+
setRequestBlobs sourceConfig blobs skeletonRequest
178+
conduit = conduitParserEither (blobKeyValueParser scBlobs)
179+
consumer remaining = await >>= \case
180+
Nothing -> pure ()
181+
Just (Left x) -> throwM (AttoParseError x)
182+
Just (Right (_position, keyValue)) ->
183+
if remaining == 0
184+
then throwM (TooManyReturnedKeys scBlobsSize)
185+
else do
186+
yield keyValue
187+
consumer (remaining - 1)
180188

181189
-- | Fill the body of the request with max blobs per request.
182190
setRequestBlobs ::
183-
SourceConfig -> [(BlobKey, Int)] -> Request -> (Request, [(BlobKey, Int)])
191+
SourceConfig -> [(BlobKey, Int)] -> Request -> (Request, [(BlobKey, Int)])
184192
setRequestBlobs sourceConfig blobs skeletonRequest = (request, remaining)
185193
where
186194
request =
187195
setRequestBodyLBS
188-
(SB.toLazyByteString
189-
(foldl'
190-
(\a (k, v) ->
191-
a <> (blobKeyToBuilder k <> SB.word64BE (fromIntegral v)))
192-
mempty
193-
thisBatch))
196+
( SB.toLazyByteString
197+
( foldl'
198+
(\a (k, v) ->
199+
a <> (blobKeyToBuilder k <> SB.word64BE (fromIntegral v)))
200+
mempty
201+
thisBatch
202+
)
203+
)
194204
skeletonRequest
195205
(thisBatch, remaining) =
196206
splitAt (sourceConfigMaxBlobsPerRequest sourceConfig) blobs
@@ -200,12 +210,12 @@ blobKeyValueParser :: HashMap BlobKey Int -> Atto.Parser (BlobKey, ByteString)
200210
blobKeyValueParser lengths = do
201211
blobKey <- blobKeyBinaryParser
202212
case HM.lookup blobKey lengths of
203-
Nothing -> fail ("Invalid key: " <> show blobKey)
213+
Nothing -> fail $ "Invalid key: " <> show blobKey
204214
Just len -> do
205215
blob <- Atto.take len
206216
if BlobKey (sha256Hash blob) == blobKey
207217
then pure (blobKey, blob)
208-
else fail ("Content does not match SHA256 hash: " ++ show blobKey)
218+
else fail $ "Content does not match SHA256 hash: " <> show blobKey
209219

210220
-- | Hash some raw bytes.
211221
sha256Hash :: ByteString -> ByteString

0 commit comments

Comments
 (0)