packages feed

hoobuddy-0.1.0.0: src/Main.hs

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}

import GHC.Generics
import System.Environment (getArgs)
import System.Exit
import Data.Aeson hiding (encode)
import Data.Yaml
import Data.List
import System.Directory (doesFileExist, findExecutable, getCurrentDirectory, getHomeDirectory)
import System.FilePath.Posix
import Hoogle (defaultDatabaseLocation, mergeDatabase)
import Control.Applicative
import Control.Monad (filterM, liftM, unless)
import Data.Maybe (isJust)

import Hoobuddy
import Data.ByteString.Char8 (unpack)


-- TODOs:
-- hoo-4 : use reader monad for config ?

deriving instance Generic Hoobuddy
instance ToJSON Hoobuddy
instance FromJSON Hoobuddy


hoogleMissingError :: String
hoogleMissingError =
    unlines [ "Error: hoogle is not installed or not in path"
            , "Please install hoogle and run `hoogle data`"]

defaultPkgs :: [String]
defaultPkgs = words "Cabal.hoo array.hoo base.hoo binary.hoo bytestring.hoo containers.hoo deepseq.hoo directory.hoo filepath.hoo haskell2010.hoo haskell98.hoo hoopl.hoo hpc.hoo old-locale.hoo old-time.hoo pretty.hoo process.hoo template-haskell.hoo time.hoo unix.hoo GLURaw.hoo GLUT.hoo HTTP.hoo HUnit.hoo OpenGL.hoo OpenGLRaw.hoo QuickCheck.hoo async.hoo attoparsec.hoo case-insensitive.hoo cgi.hoo fgl.hoo hashable.hoo haskell-src.hoo html.hoo mtl.hoo network.hoo parallel.hoo parsec.hoo primitive.hoo random.hoo regex-base.hoo regex-compat.hoo regex-posix.hoo split.hoo stm.hoo syb.hoo text.hoo transformers.hoo unordered-containers.hoo vector.hoo xhtml.hoo zlib.hoo"

help :: IO ()
help = putStrLn $
    unlines [ "Usage : hoobuddy [deps|build] <cabal-file>"
            , "                 [--help]"
            , "                 [--default]"
            , ""
            , "deps         list configured dependencies"
            , "build        do stuff yet to be defined"
            , ""
            , "--default    prints the default configuration"
            , "--help       prints this help"
            ]

main :: IO ()
main = do
    exitIfHoogleMissing
    conf <- loadConfig
    args <- getArgs
    run conf args where
        run _ ["deps", file]          = deps file
        run conf  ["build", file]     = build file conf
        run _ ["--help"]              = help
        run _ ["--default"]           = defaultConfig >>= \x -> putStrLn $ unpack $ encode x
        run _ _ = do
            help
            exitWith (ExitFailure 1)

-- | Exits with error code if hoogle isn't installed
exitIfHoogleMissing :: IO ()
exitIfHoogleMissing = do
    hoogleInstalled <- liftM isJust (findExecutable "hoogle")
    unless hoogleInstalled (putStrLn hoogleMissingError >> exitWith (ExitFailure 1))


-- | Loads configuration from file or uses defaults
loadConfig :: IO Hoobuddy
loadConfig = decodeConfig >>= maybe defaultConfig return

unique :: (Ord a) => [a] -> [a]
unique = map head . group . sort

defaultConfig :: IO Hoobuddy
defaultConfig = do
    location <- defaultDatabaseLocation
    return $ Hoobuddy (location </> "databases") True []

-- | Decodes configuration from JSON
decodeConfig :: IO (Maybe Hoobuddy)
decodeConfig = do
    homeDir <- getHomeDirectory
    parseResult <- decodeFileEither $ homeDir </> ".hoobuddy.conf"
    return $ either (const Nothing) Just parseResult


build :: FilePath -> Hoobuddy -> IO ()
build cabalFile conf = do
    pkgs <- map (++ ".hoo") <$> getDeps cabalFile
    dbs <- getHooDatabases (databases conf)

    let allPkgs = (++) pkgs (if useBase conf then defaultPkgs else custom conf)
    let available = allPkgs `intersect` dbs
    let missing = filter (`notElem` available) allPkgs

    printInfo "Fetching databases for: " missing
    hoogleFetch missing >>= \x -> case x of
        Right _             -> return ()
        Left (code, stderr) -> putStrLn ("hoogle exited with error:\n" ++ stderr) >> exitWith code

    putStrLn "Merging databases ..."
    currDir <- getCurrentDirectory
    existingDbs <- filterM doesFileExist (fmap (databases conf </>) allPkgs)
    mergeDatabase  existingDbs (currDir </> "default.hoo")

-- | Pretty printer for info output
printInfo :: String -> [String] -> IO ()
printInfo _ [] = return ()
printInfo str xs = putStrLn $ str ++ "[" ++ intercalate "," xs ++ "]"