module Hackage.Utils where import Distribution.Package ( Dependency(..) ) import Distribution.Text ( display ) import Distribution.Simple.Utils (intercalate) import Data.List ( sortBy, groupBy ) import Control.Monad (guard) import Control.Exception (Exception, catchJust, ioErrors) import System.IO.Error (isDoesNotExistError) readFileIfExists :: FilePath -> IO (Maybe String) readFileIfExists path = catchJust fileNotFoundExceptions (fmap Just (readFile path)) (\_ -> return Nothing) fileNotFoundExceptions :: Exception -> Maybe IOError fileNotFoundExceptions e = ioErrors e >>= \ioe -> guard (isDoesNotExistError ioe) >> return ioe showDependencies :: [Dependency] -> String showDependencies = intercalate ", " . map display -- | Generic merging utility. For sorted input lists this is a full outer join. -- -- * The result list never contains @(Nothing, Nothing)@. -- mergeBy :: (a -> b -> Ordering) -> [a] -> [b] -> [MergeResult a b] mergeBy cmp = merge where merge [] ys = [ OnlyInRight y | y <- ys] merge xs [] = [ OnlyInLeft x | x <- xs] merge (x:xs) (y:ys) = case x `cmp` y of GT -> OnlyInRight y : merge (x:xs) ys EQ -> InBoth x y : merge xs ys LT -> OnlyInLeft x : merge xs (y:ys) data MergeResult a b = OnlyInLeft a | InBoth a b | OnlyInRight b duplicates :: Ord a => [a] -> [[a]] duplicates = duplicatesBy compare duplicatesBy :: (a -> a -> Ordering) -> [a] -> [[a]] duplicatesBy cmp = filter moreThanOne . groupBy eq . sortBy cmp where eq a b = case cmp a b of EQ -> True _ -> False moreThanOne (_:_:_) = True moreThanOne _ = False