module Catalog where
import Control.Monad.Error (MonadError, throwError)
import Control.Monad.RWS (MonadIO, liftIO, MonadReader, asks)
import Data.Aeson ((.:))
import qualified Data.Aeson as Json
import qualified Data.Binary as Binary
import qualified Data.ByteString.Lazy as LBS
import qualified Data.Time.Clock as Time
import Data.Version (showVersion)
import Network.HTTP
import qualified Network.HTTP.Client as Client
import qualified Network.HTTP.Client.MultipartFormData as Multi
import System.Directory (createDirectoryIfMissing, doesFileExist)
import System.FilePath ((</>), dropFileName)
import qualified Elm.Docs as Docs
import qualified Elm.Package.Description as Desc
import qualified Elm.Package.Name as N
import qualified Elm.Package.Paths as P
import qualified Elm.Package.Version as V
import qualified Manager
import qualified Paths_elm_package as This
import qualified Utils.Http as Http
catalog
:: (MonadIO m, MonadReader Manager.Environment m, MonadError String m)
=> String
-> [(String,String)]
-> m String
catalog path vars =
do domain <- asks Manager.catalog
return $ domain ++ "/" ++ path ++ "?" ++ urlEncodeVars (version : vars)
where
version = ("elm-package-version", showVersion This.version)
versions :: N.Name -> Manager.Manager (Maybe [V.Version])
versions name =
do url <- catalog "versions" [("name", N.toString name)]
Http.send url $ \request manager -> do
response <- Client.httpLbs request manager
return $ Binary.decode $ Client.responseBody response
allPackages
:: (MonadIO m, MonadReader Manager.Environment m, MonadError String m)
=> Maybe Time.UTCTime
-> m (Maybe [(N.Name, [V.Version])])
allPackages maybeTime =
do url <- catalog "all-packages" vars
Http.send url $ \request manager -> do
response <- Client.httpLbs request manager
case Json.eitherDecode (Client.responseBody response) of
Left _ ->
return Nothing
Right summaries ->
return $ Just $ map (\(PackageSummary s) -> s) summaries
where
vars =
case maybeTime of
Nothing -> []
Just time -> [("since", show time)]
newtype PackageSummary = PackageSummary (N.Name, [V.Version])
instance Json.FromJSON PackageSummary where
parseJSON (Json.Object obj) =
do name <- obj .: "name"
versions <- obj .: "versions"
return (PackageSummary (name, versions))
parseJSON _ =
fail "package summary must be an object"
register :: N.Name -> V.Version -> Manager.Manager ()
register name version =
do url <- catalog "register" vars
Http.send url $ \request manager -> do
request' <- Multi.formDataBody files request
let request'' = request' { Client.responseTimeout = Nothing }
Client.httpLbs request'' manager
return ()
where
vars =
[ ("name", N.toString name)
, ("version", V.toString version)
]
files =
[ Multi.partFileSource "documentation" P.documentation
, Multi.partFileSource "description" P.description
, Multi.partFileSource "readme" "README.md"
]
description
:: (MonadIO m, MonadReader Manager.Environment m, MonadError String m)
=> N.Name
-> V.Version
-> m Desc.Description
description name version =
getJson "description" P.description name version
documentation
:: (MonadIO m, MonadReader Manager.Environment m, MonadError String m)
=> N.Name
-> V.Version
-> m [Docs.Documentation]
documentation name version =
getJson "documentation" "documentation.json" name version
getJson
:: (MonadIO m, MonadReader Manager.Environment m, MonadError String m, Json.FromJSON a)
=> String
-> FilePath
-> N.Name
-> V.Version
-> m a
getJson metadata metadataPath name version =
do cacheDir <- asks Manager.cacheDirectory
let fullMetadataPath =
cacheDir </> N.toFilePath name </> V.toString version </> metadataPath
exists <- liftIO (doesFileExist fullMetadataPath)
content <-
case exists of
True -> liftIO (LBS.readFile fullMetadataPath)
False ->
do url <- catalog metadata [("name", N.toString name), ("version", V.toString version)]
Http.send url $ \request manager ->
do response <- Client.httpLbs request manager
createDirectoryIfMissing True (dropFileName fullMetadataPath)
LBS.writeFile fullMetadataPath (Client.responseBody response)
return (Client.responseBody response)
case Json.eitherDecode content of
Right value -> return value
Left err ->
throwError $
"Unable to get " ++ metadataPath ++ " for "
++ N.toString name ++ " " ++ V.toString version ++ "\n" ++ err