packages feed

dash-haskell-1.0.0.1: src/Options.hs

{-# LANGUAGE TupleSections #-}

module Options where

import           Control.Applicative
import           Control.Monad.M
import qualified Data.List as L

import           Data.Monoid
import qualified Distribution.Package as C
import qualified Distribution.Version as CV
import           Distribution.Text
import           Options.Applicative.Builder
import           Options.Applicative.Common
import           Options.Cabal
import           Options.CabalConstraints (toConstraints, none, CabalConstraints)
import           Options.DbProvider
import           PackageId
import           Pipes
import qualified Data.Set as S
                 
data Options = Options { 
  dbprovider :: DbProvider,
  outputDir  :: FilePath,
  quiet    :: Bool,
  cabal :: Maybe FilePath,
  cabalConstraints :: CabalConstraints, 
  packages :: [C.PackageId]
} deriving Show

parser :: Parser Options
parser = 
  Options <$> 
    option toProvider 
      (long "dbprovider" 
      <> short 'p'
      <> metavar "<provider,args>"
      <> value (CabalSandbox Nothing) 
      <> help "a ghc package db provider: cabal|ghc|dir\n")
    <*>
    strOption (
     long "output" 
     <> short 'o'
     <> metavar "<dir>" 
     <> value "./docsets" 
     <> help "the directory to write created docsets to")
    <*>
    switch (long "quiet" <> short 'q' <> help "set to quiet output")
    <*>
    option (return . Just) 
     (long "cabal"
     <> short 'c'  
     <> metavar "<file.cabal>" 
     <> value Nothing
     <> help "the cabal file to retrieve package dependencies from")
    <*>
    option toConstraints
      (long "cabal-constraints" 
      <> short 'r'
      <> value none 
      <> metavar "executable=name, .."
      <> help "limit package results from a cabal file source, see documentation")
    <*>
    many (
     argument simpleParse (metavar "packages" <> 
     help "a list of packages to specifically build, e.g. either-1.0.1 text"
     ))

-- | Given two lists of package satisfying strings, 
-- return a list that is non-duplicate, the most versioned of the two.
-- e.g. if 'either-4.1.0' is in one, and 'either' is the other, 
-- the versioned is chosen. If both are versioned, both appear in
-- the final result.
reduce :: [C.PackageId] -> [C.PackageId]
reduce = fromAsc . L.sort where
  -- Here we exploit the fact that you only need to examine the next member of 
  -- an ascending list to determine a version
  fromAsc :: [C.PackageId] -> [C.PackageId]
  fromAsc []      = []
  fromAsc (p:[])  = [p] 
  fromAsc (p:nxt:rest)
    | p == nxt = -- duplicate 
      fromAsc (nxt:rest) 
    | unversioned p == unversioned nxt = 
      if unversioned p == p then
        fromAsc $ nxt:rest
      else -- both are versioned by list ordering
        p : nxt : fromAsc rest 
    | otherwise = -- they're different packages
        p : fromAsc ( nxt : rest )
  
versionless :: String -> C.PackageId
versionless n = C.PackageIdentifier (C.PackageName n) $ CV.Version [] [] 

prod_Packages :: Options -> ProducerM (S.Set String) () 
prod_Packages options = do
  cabal_dependencies <-
    lift $ case cabal options of 
      Nothing -> return [] 
      Just fp -> (S.toList . S.map versionless) 
                 <$> readPackages fp (cabalConstraints options)
  yield . S.fromList . map (show . disp) $ 
    reduce (packages options ++ cabal_dependencies)