packages feed

stack-run-0.1.0.3: src/Main.hs

module Main
  where

import           Control.Applicative                   ((<$>))
import           Control.Monad                         (unless)
import qualified Data.ByteString.Char8                 as ByteString
import           Data.Conduit
import qualified Data.Conduit.Binary                   as Conduit.Binary
import qualified Data.Conduit.List                     as Conduit.List
import           Data.Conduit.Process
import           Data.List
import           Data.List.Utils
import           Data.Maybe
import           Distribution.PackageDescription
import           Distribution.PackageDescription.Parse
import           System.Console.ANSI
import           System.Directory
import           System.Directory.ProjectRoot
import           System.Environment
import           System.Exit
import           System.FilePath
import           System.IO

usage :: String
usage = unlines [ ""
                , "  Usage: stack run <name|sub-command> [args]"
                , ""
                , "  Commands:"
                , ""
                , "    stack run [name] [args]      Compiles and runs the executable specified or the default"
                , "    stack run -- <name> [args]   Like stack run, but will never match a sub-command"
                , "    stack run -- -- [args]       Pass-in arguments to the default executable"
                , ""
                , "    stack run set-default <name> Sets the default executable to run"
                , ""
                , "    stack run help               Print this help message"
                , "    stack run --help"
                , "    stack run -h"
                , ""
                ]

setDefault :: String -> IO ()
setDefault name = do
    pr <- fromMaybe (error "No project root found") <$>
        getProjectRootCurrent
    writeFile (pr </> ".stack-work" </> ".stack-run-default") name

findDefault :: IO String
findDefault = do
    pr <- fromMaybe (error "No project root found") <$>
        getProjectRootCurrent
    e <- doesFileExist (pr </> ".stack-work" </> ".stack-run-default")
    if e then readFile (pr </> ".stack-work" </> ".stack-run-default")
         else findDefault' pr
  where
    findDefault' pr = do
        cfp <- fromMaybe (error "No cabal file found") <$>
            (find ((== ".cabal") . takeExtension) <$> getDirectoryContents pr)
        getPackageDescription cfp >>= getDefaultExecutable
          where
            getPackageDescription p = parsePackageDescription <$> readFile p
            getDefaultExecutable (ParseFailed _) =
                error "Failed to parse cabal file"
            getDefaultExecutable (ParseOk _ gpd) = case condExecutables gpd of
                [] -> error "No executable found"
                ((d, _):_) -> return d

stackRun :: String -> [String] -> IO b
stackRun name as = do
    pr <- fromMaybe (error "No project root found") <$> getProjectRootCurrent
    stackYmlExists <- doesFileExist (pr </> "stack.yaml")
    unless stackYmlExists $ do
        ec <- prettyRunCommand "stack init"
        case ec of
            ExitSuccess -> return ()
            f -> exitWith f
    ec <- prettyRunCommand "stack build"
    case ec of
        ExitSuccess -> do
            let cmd = "stack exec " ++ name ++ " -- " ++ join " " as
            logCommand cmd
            hSetSGR stderr [Reset]
            hFlush stderr
            ph <- runCommand cmd
            ec' <- waitForProcess ph
            exitWith ec'
        f -> exitWith f

prettyRunCommand :: String -> IO ExitCode
prettyRunCommand cmd = do
    logCommand cmd
    (Inherited, out, err, cph) <- streamingProcess (shell cmd)
    out =$= Conduit.Binary.lines $$ Conduit.List.mapM_ putLineGray
    err =$= Conduit.Binary.lines $$ Conduit.List.mapM_ putLineRed
    waitForStreamingProcess cph
  where
    putLineSGR sgr b = do
        hSetSGR stderr sgr
        hPutStr stderr "  "
        ByteString.hPutStrLn stderr b
        hSetSGR stderr [Reset]
    putLineGray = putLineSGR [SetColor Foreground Dull White]
    putLineRed = putLineSGR [SetColor Foreground Vivid Black]

logCommand :: String -> IO ()
logCommand cmd = do
    hSetSGR stderr [ SetColor Foreground Vivid White
                   , SetConsoleIntensity BoldIntensity
                   ]
    hPutStr stderr "$ "
    hSetSGR stderr [Reset]
    hSetSGR stderr [SetColor Foreground Vivid Cyan]
    hPutStrLn stderr cmd
    hSetSGR stderr [Reset]

main :: IO ()
main = do
    args <- getArgs
    case args of
        ("set-default":name:_) -> setDefault name
        ("set-default":_) -> do
            hPutStrLn stderr "Missing required parameter: <name>"
            exitFailure
        ("get-default":_) -> putStrLn =<< findDefault
        ("--help":_) -> exitUsage
        ("-h":_) -> exitUsage
        ("help":_) -> exitUsage
        ("--":"--":as) -> flip stackRun as =<< findDefault
        ("--":name:as) -> stackRun name as
        (name:as) -> stackRun name as
        [] -> flip stackRun [] =<< findDefault
  where
    exitUsage = do
        putStr usage
        exitSuccess