@@ -1,10 +1,14 @@
11{-# OPTIONS_GHC -Wall #-}
2−module Elm.Utils ((|>), (<|), getAsset) where
2+{-# LANGUAGE FlexibleContexts #-}
3+module Elm.Utils ((|>), (<|), getAsset, run, unwrappedRun, CommandError(..)) where
34
5+import Control.Monad.Error (MonadError, MonadIO, liftIO, throwError)
46import System.Directory (doesFileExist)
57import System.Environment (getEnv)
8+import System.Exit (ExitCode(ExitSuccess, ExitFailure))
69import System.FilePath ((</>))
710import System.IO.Error (tryIOError)
11+import System.Process (readProcessWithExitCode)
812
913
1014{-| Forward function application `x |> f == f x`. This function is useful
@@ -25,6 +29,49 @@ infixr 0 <|
2529infixl 0 |>
2630
2731
32+-- RUN EXECUTABLES
33+
34+data CommandError
35+ = MissingExe
36+ | CommandFailed String
37+
38+
39+{-| Run a command, throw an error if the command is not found or if something
40+goes wrong.
41+-}
42+run :: (MonadError String m, MonadIO m) => String -> [String] -> m String
43+run command args =
44+ do result <- liftIO (unwrappedRun command args)
45+ case result of
46+ Right out -> return out
47+ Left err ->
48+ throwError (context (message err))
49+ where
50+ context msg =
51+ "failure when running:" ++ concatMap (' ':) (command:args) ++ "\n" ++ msg
52+
53+ message err =
54+ case err of
55+ CommandFailed msg -> msg
56+ MissingExe ->
57+ "Could not find command '" ++ command ++ "'. Do you have it installed?\n\
58+ \ Can it be run from anywhere? Is it on your PATH?"
59+
60+
61+unwrappedRun :: String -> [String] -> IO (Either CommandError String)
62+unwrappedRun command args =
63+ do (exitCode, stdout, stderr) <- readProcessWithExitCode command args ""
64+ return $
65+ case exitCode of
66+ ExitSuccess -> Right stdout
67+ ExitFailure code
68+ | code == 127 -> Left MissingExe -- UNIX
69+ | code == 9009 -> Left MissingExe -- Windows
70+ | otherwise -> Left (CommandFailed stderr)
71+
72+
73+-- GET STATIC ASSETS
74+
2875{-| Get the absolute path to a data file. If you install with cabal it will look
2976-}
3077getAsset :: String -> (FilePath -> IO FilePath) -> FilePath -> IO FilePath