{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE TemplateHaskell #-} module Main where import Filesystem import Control.Applicative import Control.Monad import Stackage.CLI import Options.Applicative (Parser) import Options.Applicative.Builder (strArgument, metavar, value) import Data.Monoid import Data.Text (Text) import qualified Data.Text as T import qualified Data.Text.Encoding as T import Data.Typeable (Typeable) import Network.HTTP.Client import Network.HTTP.Client.TLS (tlsManagerSettings) import Network.HTTP.Types.Status (statusCode) import Network.HTTP.Types.Header (hUserAgent) import qualified Data.ByteString.Lazy as LBS import System.Exit (exitFailure) import System.Environment (getArgs) import System.IO (hPutStrLn, stderr) import Control.Exception import qualified Paths_stackage_cabal as CabalInfo type Snapshot = String data InitException = InvalidSnapshot | SnapshotNotFound | UnexpectedHttpException HttpException | CabalConfigExists deriving (Show, Typeable) instance Exception InitException version :: String version = $(simpleVersion CabalInfo.version) header :: String header = "Initializes cabal.config" progDesc :: String progDesc = header userAgent :: Text userAgent = "stackage-init/" <> T.pack version snapshotParser :: Parser Snapshot snapshotParser = strArgument mods where mods = (metavar "SNAPSHOT" <> value "lts") toUrl :: Snapshot -> String toUrl t = "https://www.stackage.org/" <> t <> "/cabal.config" snapshotReq :: Snapshot -> IO Request snapshotReq snapshot = case parseUrl (toUrl snapshot) of Left _ -> throwIO $ InvalidSnapshot Right req -> return req { requestHeaders = [(hUserAgent, T.encodeUtf8 userAgent)] } downloadSnapshot :: Snapshot -> IO LBS.ByteString downloadSnapshot snapshot = withManager tlsManagerSettings $ \manager -> do let getResponseLbs req = do response <- httpLbs req manager return $ responseBody response let handle404 firstTry (StatusCodeException s _ _) | statusCode s == 404 = if firstTry then do req <- snapshotReq $ "snapshot/" <> snapshot getResponseLbs req `catch` handle404 False else do throwIO $ SnapshotNotFound handle404 _ e = throwIO $ UnexpectedHttpException e req <- snapshotReq snapshot getResponseLbs req `catch` handle404 True initSnapshot :: Snapshot -> IO () initSnapshot snapshot = do configExists <- isFile "cabal.config" when configExists $ throwIO $ CabalConfigExists downloadSnapshot snapshot >>= LBS.writeFile "cabal.config" handleInitExceptions :: Snapshot -> InitException -> IO () handleInitExceptions snapshot e = hPutStrLn stderr (err e) >> exitFailure where err InvalidSnapshot = "Invalid snapshot: " <> snapshot err SnapshotNotFound = "Snapshot not found: " <> snapshot err CabalConfigExists = "Warning: Cabal config already exists.\n" <> "No action taken." err (UnexpectedHttpException e) = "Unexpected http exception:\n" <> show e main = do (snapshot, ()) <- simpleOptions version header progDesc snapshotParser -- global parser empty -- subcommands initSnapshot snapshot `catch` handleInitExceptions snapshot