module General.Extra( whenRightM, allMaybeM, createDirectoryRecursive, doesFileExist_, NoShow(..), memoIO, catchIO, getProcessorCount, unionWithKeyEithers, insertWithKeyEithers ) where import Control.Exception.Extra import System.Directory import Data.IORef import qualified Data.HashMap.Strict as Map import Data.Hashable import Control.Monad.Extra import System.IO.Unsafe import Control.Concurrent import System.Environment import Data.List import System.IO.Extra import GHC.Conc(getNumProcessors) --------------------------------------------------------------------- -- Prelude newtype NoShow a = NoShow a instance Show (NoShow a) where show _ = "NoShow" --------------------------------------------------------------------- -- Control.Monad whenRightM :: Monad m => m (Either l r) -> (r -> m ()) -> m () whenRightM x act = eitherM (const $ pure ()) act x allMaybeM :: Monad m => (a -> m (Maybe b)) -> [a] -> m (Maybe [b]) allMaybeM f [] = pure $ Just [] allMaybeM f (x:xs) = do y <- f x case y of Nothing -> pure Nothing Just y -> fmap (y:) <$> allMaybeM f xs --------------------------------------------------------------------- -- Control.Exception catchIO :: IO a -> (IOException -> IO a) -> IO a catchIO = catch tryIO :: IO a -> IO (Either IOException a) tryIO = try --------------------------------------------------------------------- -- System.Directory doesFileExist_ :: FilePath -> IO Bool doesFileExist_ x = doesFileExist x `catchIO` \_ -> pure False -- | Like @createDirectoryIfMissing True@ but faster, as it avoids -- any work in the common case the directory already exists. createDirectoryRecursive :: FilePath -> IO () createDirectoryRecursive dir = do x <- tryIO $ doesDirectoryExist dir when (x /= Right True) $ createDirectoryIfMissing True dir --------------------------------------------------------------------- -- Data.Memo memoIO :: (Hashable k, Eq k) => (k -> IO v) -> IO (k -> IO v) memoIO f = do ref <- newIORef Map.empty pure $ \k -> do mp <- readIORef ref case Map.lookup k mp of Just v -> pure v Nothing -> do v <- f k atomicModifyIORef ref $ \mp -> (Map.insert k v mp, v) --------------------------------------------------------------------- -- System.Info -- Copied from Shake {-# NOINLINE getProcessorCount #-} getProcessorCount :: IO Int -- unsafePefromIO so we cache the result and only compute it once getProcessorCount = let res = unsafePerformIO act in pure res where act = if rtsSupportsBoundThreads then fromIntegral <$> getNumProcessors else do env <- lookupEnv "NUMBER_OF_PROCESSORS" case env of Just s | [(i,"")] <- reads s -> pure i _ -> do src <- readFile' "/proc/cpuinfo" `catchIO` \_ -> pure "" pure $! max 1 $ length [() | x <- lines src, "processor" `isPrefixOf` x] --------------------------------------------------------------------- -- Data.HashMap unionWithKeyEithers :: (Eq k, Hashable k) => (k -> v -> v -> Either e (Maybe v)) -> Map.HashMap k v -> Map.HashMap k v -> ([e], Map.HashMap k v) unionWithKeyEithers op lhs = insertWithKeyEithers op lhs . Map.toList insertWithKeyEithers :: (Eq k, Hashable k) => (k -> v -> v -> Either e (Maybe v)) -> Map.HashMap k v -> [(k,v)] -> ([e], Map.HashMap k v) insertWithKeyEithers op lhs = foldl' f ([], lhs) where f (es, mp) (k, v2) = case Map.lookup k mp of Nothing -> (es, Map.insert k v2 mp) Just v1 -> case op k v1 v2 of Left e -> (e:es, mp) Right Nothing -> (es, mp) Right (Just v) -> (es, Map.insert k v mp)