packages feed

cabalQuery-0.1.0.1: tools/Main.hs

module Main where

import System.Directory
import System.Environment
import System.IO

import Data.Either
import Data.List (isInfixOf, intercalate)
import Data.Map (Map)
import qualified Data.Map as Map
import Data.String.Utils
import Data.Version (showVersion)
import Distribution.Query
import Distribution.PackageDescription (GenericPackageDescription)


import qualified Paths_cabalQuery as CQ

main :: IO ()
main = do
  args <- getArgs
  eCfg <- parseArgs args
  case eCfg of
    Nothing -> printHelp
    Just cfg | ["--help"] `isInfixOf` (options cfg)    -> printHelp
             | ["--version"] `isInfixOf` (options cfg) -> printVersion
             | otherwise -> do
      let cmds = map cmdFn $ commands cfg
      case files cfg of
        []       -> do putStrLn "No .cabal files specified."
                       printHelp
        theFiles -> mapM_ (query cmds) theFiles

printVersion :: IO ()
printVersion = putStrLn ("cabalQuery-"++ showVersion CQ.version)

printHelp :: IO ()
printHelp = do
  putStrLn "Usage: cabalQuery [command] [cabalfile] [options]"
  putStrLn ""
  putStrLn "Commands, files and options can be interspersed."
  putStrLn ""
  putStrLn "Options:"
  putStrLn "  --help             Prints this help output."
  putStrLn "  --version          Nothing, yet."
  putStrLn "  --numeric-version  Nothing, yet."
  putStrLn ""
  putStrLn "Commands:"
  mapM_ (\cmd -> putStrLn ("  " ++ (cmdName cmd) ++ "\t\t" ++ (cmdDesc cmd))) commandList

commandMap :: Map String Command
commandMap = Map.fromList $ map (\cmd -> (cmdName cmd, cmd)) commandList

commandList :: [Command]
commandList =
  [ Command "numeric-version"
      "Returns the numeric version in a machine-readable fashion." numericVersion
  , Command "version" "An alias for numeric-version" numericVersion
  , Command "name"  "The package name." name
  , Command "license" "The package license." license
  , Command "copyright" "The package copyright." copyright
  , Command "homepage" "The package homepage." homepage
  , Command "pkgUrl" "The package url." pkgUrl
  , Command "bugReports" "The bug reporting url." bugReports
  , Command "synopsis" "The package synopsis." synopsis
  , Command "description" "The package description." description
  , Command "buildType" "The buildType." buildType
  , Command "dataDir" "The dataDir." dataDir
  ]

data Command = Command { cmdName :: String
                         -- ^ The name of the command, as written on the command line.
                       , cmdDesc :: String
                         -- ^ A description of the command, for help output.
                       , cmdFn :: GenericPackageDescription -> String
                         -- ^ The function over a package description
                         -- to get the string value of the thing we're requesting.
                       }

data Config = Config { files :: [FilePath]
                     -- ^ The cabal file(s) to examine. Defaults to ./*.cabal
                     , commands :: [Command]
                     -- ^ The things to show, from the cabal file.
                     -- e.g., 'numeric-version'
                     , options :: [String]
                     -- ^ Any options to pass to cabalQuery, starting with "--"
                     }

toCommand :: String -> Either String Command
toCommand str = case Map.lookup str commandMap of
                  Nothing  -> Left str
                  Just cmd -> Right cmd

parseArgs :: [String] -> IO (Maybe Config)
parseArgs args = do
  let eCmds = map toCommand $ filter isCommand args
  case (lefts eCmds, rights eCmds) of
    ([] , cmds) -> do
      theFiles <- case filter isCabalFile args of
        [] -> do fs <- getCurrentDirectory >>= getDirectoryContents
                 return (filter isCabalFile fs)
        fs -> return fs
      return (Just (Config { files = theFiles
                           , options = filter isOption args
                           , commands = cmds
                           }))
    (errs, _) -> do
      hPutStrLn stderr "Invalid commands specified:"
      mapM_ (hPrint stderr) errs
      return Nothing

isCabalFile :: FilePath -> Bool
isCabalFile file = endswith ".cabal" file

isOption :: String -> Bool
isOption str = startswith "--" str

isCommand :: String -> Bool
isCommand str = not (isOption str || isCabalFile str)


query :: [GenericPackageDescription -> String] -> FilePath -> IO ()
query cmds file = do
  eGpb <- loadDescr file
  case eGpb of
    Left  err -> hPrint stderr err
    Right gpb -> putStrLn $ intercalate ", " (map ($ gpb) cmds)