module Util where
import Prelude hiding (head)
import qualified Control.Concurrent as Concurrent
import qualified Control.Concurrent.MVar as MVar
import qualified Control.Exception as Exception
import Control.Monad
import qualified Data.Function as Function
import qualified Data.List as List
import qualified Data.List.Split as Split
import qualified Data.Text as T
import qualified Data.Text.IO as Text.IO
import qualified System.Directory as Directory
import qualified System.Exit as Exit
import System.FilePath ((</>))
import qualified System.IO as IO
import qualified System.IO.Error as IO.Error
import qualified System.Process as Process
-- * list
-- | Concat a list with 'sep' in between.
join :: [a] -> [[a]] -> [a]
join = List.intercalate
-- | Split 'xs' on 'sep', dropping 'sep' from the result.
split :: (Eq a) => [a] -> [a] -> [[a]]
split = Split.splitOn
head :: [a] -> Maybe a
head [] = Nothing
head (x:_) = Just x
sortOn :: (Ord k) => (a -> k) -> [a] -> [a]
sortOn key = List.sortBy (compare `Function.on` key)
groupOn :: (Eq k) => (a -> k) -> [a] -> [[a]]
groupOn key = List.groupBy ((==) `Function.on` key)
-- * control
ifM :: (Monad m) => m Bool -> m a -> m a -> m a
ifM cond consequent alternative = do
b <- cond
if b then consequent else alternative
partitionM :: (Monad m) => (a -> m Bool) -> [a] -> m ([a], [a])
partitionM f = go [] []
where
go ts fs [] = return (ts, fs)
go ts fs (x:xs) = ifM (f x) (go (x:ts) fs xs) (go ts (x:fs) xs)
anyM :: (a -> IO Bool) -> [a] -> IO Bool
anyM _ [] = return False
anyM f (x:xs) = ifM (f x) (return True) (anyM f xs)
-- | If @op@ raised ENOENT, return Nothing.
catchENOENT :: IO a -> IO (Maybe a)
catchENOENT op = Exception.handleJust (guard . IO.Error.isDoesNotExistError)
(const (return Nothing)) (fmap Just op)
-- * file
listDir :: FilePath -> IO [FilePath]
listDir dir = fmap (map add . filter (not . (`elem` [".", ".."])))
(Directory.getDirectoryContents dir)
where add = if dir == "." then id else (dir </>)
-- | Similar to System.Process.readProcessWithExitCode but return Text instead
-- of String.
readProcessWithExitCode :: FilePath -> [String]
-> IO (Exit.ExitCode, T.Text, T.Text)
readProcessWithExitCode cmd args = do
(_, Just outh, Just errh, pid) <-
Process.createProcess (Process.proc cmd args)
{ Process.std_out = Process.CreatePipe
, Process.std_err = Process.CreatePipe
}
outMVar <- MVar.newEmptyMVar
errMVar <- MVar.newEmptyMVar
void $ Concurrent.forkIO $
MVar.putMVar outMVar =<< Text.IO.hGetContents outh
void $ Concurrent.forkIO $
MVar.putMVar errMVar =<< Text.IO.hGetContents errh
out <- MVar.takeMVar outMVar
err <- MVar.takeMVar errMVar
IO.hClose outh
IO.hClose errh
ex <- Process.waitForProcess pid
return (ex, out, err)