<commit_before>{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ViewPatterns #-}

-- | Dealing with Cabal.

module Stackage.Package
  (readPackage
  ,Package
  ,PackageConfig)
  where

import           Control.Exception
import           Control.Monad
import           Control.Monad.Catch
import           Control.Monad.IO.Class
import           Control.Monad.Logger (MonadLogger)
import           Control.Monad.Loops
import           Data.Data
import           Data.Function
import           Data.List
import           Data.Map.Strict (Map)
import qualified Data.Map.Strict as M
import           Data.Maybe
import           Data.Monoid
import           Data.Set (Set)
import qualified Data.Set as S
import           Data.Text (Text)
import qualified Data.Text as T
import           Data.Yaml (ParseException)
import           Distribution.Compiler
import           Distribution.InstalledPackageInfo (PError)
import           Distribution.ModuleName as Cabal
import           Distribution.Package hiding (Package,PackageName)
import           Distribution.PackageDescription
import           Distribution.PackageDescription.Parse
import           Distribution.Simple.Utils
import           Distribution.System
import           Distribution.Version
import           Path as FL
import           Prelude hiding (FilePath)
import           Stackage.Constants
import           Stackage.PackageName
import           System.Directory (doesFileExist)

-- | All exceptions thrown by the library.
data PackageException
  = PackageConfigError ParseException
  | PackageNoConfigFile
  | PackageNoCabalFile (Path Abs Dir)
  | PackageInvalidCabalFile (Path Abs File) PError
  | PackageNoDeps (Path Abs File)
  | PackageDepCycle PackageName
  | PackageMissingDep Package PackageName VersionRange
  | PackageDependencyIssues [PackageException]
  | PackageMissingTool Dependency
  | PackageCouldn'tFindPkgId PackageName
  | PackageStackagePackageVersionMismatch PackageName Version Version
  | PackageStackageDepVerMismatch PackageName Version VersionRange
  deriving (Show,Typeable)
instance Exception PackageException

-- | Some package info.
data Package =
  Package {pinfoName :: !PackageName                      -- ^ Name of the package.
          ,pinfoVersion :: !Version                       -- ^ Version of the package
          ,pinfoDir :: !(Path Abs Dir)                    -- ^ Directory of the package.
          ,pinfoFiles :: !(Set (Path Abs File))           -- ^ Files that the package depends on.
          ,pinfoDeps :: !(Map PackageName VersionRange)   -- ^ Packages that the package depends on.
          ,pinfoTools :: ![Dependency]                    -- ^ A build tool name.
          ,pinfoAllDeps :: !(Set PackageName)             -- ^ Original dependencies (not sieved).
          ,pinfoFlags :: !(Map Text Bool)                 -- ^ Flags used on package.
          }
 deriving (Show,Typeable)

-- | Package build configuration
data PackageConfig =
  PackageConfig {packageConfigEnableTests :: !Bool        -- ^ Are tests enabled?
                ,packageConfigEnableBenchmarks :: !Bool   -- ^ Are benchmarks enabled?
                ,packageConfigFlags :: !(Map Text Bool)   -- ^ Package config flags.
                }
 deriving (Show,Typeable)

-- | Compares the package name.
instance Ord Package where
  compare = on compare pinfoName

-- | Compares the package name.
instance Eq Package where
  (==) = on (==) pinfoName

-- | Reads and exposes the package information
readPackage :: (MonadLogger m, MonadIO m, MonadThrow m)
            => PackageConfig
            -> Path Abs File
            -> m Package
readPackage packageConfig cabalfp =
  do chars <-
       liftIO (Prelude.readFile (FL.toFilePath cabalfp))
     case parsePackageDescription chars of
       ParseFailed per ->
         liftedThrowIO (PackageInvalidCabalFile cabalfp per)
       ParseOk _ gpkg ->
         let pkgId =
               package (packageDescription gpkg)
             name = fromCabalPackageName (pkgName pkgId)
             pkgFlags =
               packageConfigFlags packageConfig
             pkg =
               resolvePackage packageConfig gpkg
         in case packageDependencies pkg of
              deps
                | M.null deps ->
                  liftedThrowIO (PackageNoDeps cabalfp)
                | otherwise ->
                  do let dir = FL.parentAbs cabalfp
                     pkgFiles <-
                       liftIO (packageFiles dir pkg)
                     let files = cabalfp : pkgFiles
                         deps' =
                           M.filterWithKey (const . (/= name))
                                           deps
                     return (Package {pinfoName = name
                                   ,pinfoVersion = pkgVersion pkgId
                                   ,pinfoDeps = deps'
                                   ,pinfoDir = dir
                                   ,pinfoFiles = S.fromList files
                                   ,pinfoTools = packageTools pkg
                                   ,pinfoFlags = pkgFlags
                                   ,pinfoAllDeps =
                                      S.fromList (M.keys deps')})
  where liftedThrowIO = liftIO . throwIO

-- | Get all dependencies of the package (buildable targets only).
packageDependencies :: PackageDescription -> Map PackageName VersionRange
packageDependencies =
  M.fromList .
  concatMap (map (\dep -> ((depName dep),depRange dep)) .
             targetBuildDepends) .
  allBuildInfo

-- | Get all dependencies of the package (buildable targets only).
packageTools :: PackageDescription -> [Dependency]
packageTools = concatMap buildTools . allBuildInfo

-- | Get all files referenced by the package.
packageFiles :: Path Abs Dir -> PackageDescription -> IO [Path Abs File]
packageFiles dir pkg =<commit_msg>Rename Package fields appropriately<commit_after>{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ViewPatterns #-}

-- | Dealing with Cabal.

module Stackage.Package
  (readPackage
  ,Package
  ,PackageConfig)
  where

import           Control.Exception
import           Control.Monad
import           Control.Monad.Catch
import           Control.Monad.IO.Class
import           Control.Monad.Logger (MonadLogger)
import           Control.Monad.Loops
import           Data.Data
import           Data.Function
import           Data.List
import           Data.Map.Strict (Map)
import qualified Data.Map.Strict as M
import           Data.Maybe
import           Data.Monoid
import           Data.Set (Set)
import qualified Data.Set as S
import           Data.Text (Text)
import qualified Data.Text as T
import           Data.Yaml (ParseException)
import           Distribution.Compiler
import           Distribution.InstalledPackageInfo (PError)
import           Distribution.ModuleName as Cabal
import           Distribution.Package hiding (Package,PackageName,packageName,packageVersion)
import           Distribution.PackageDescription
import           Distribution.PackageDescription.Parse
import           Distribution.Simple.Utils
import           Distribution.System
import           Distribution.Version
import           Path as FL
import           Prelude hiding (FilePath)
import           Stackage.Constants
import           Stackage.PackageName
import           System.Directory (doesFileExist)

-- | All exceptions thrown by the library.
data PackageException
  = PackageConfigError ParseException
  | PackageNoConfigFile
  | PackageNoCabalFile (Path Abs Dir)
  | PackageInvalidCabalFile (Path Abs File) PError
  | PackageNoDeps (Path Abs File)
  | PackageDepCycle PackageName
  | PackageMissingDep Package PackageName VersionRange
  | PackageDependencyIssues [PackageException]
  | PackageMissingTool Dependency
  | PackageCouldn'tFindPkgId PackageName
  | PackageStackagePackageVersionMismatch PackageName Version Version
  | PackageStackageDepVerMismatch PackageName Version VersionRange
  deriving (Show,Typeable)
instance Exception PackageException

-- | Some package info.
data Package =
  Package {packageName :: !PackageName                    -- ^ Name of the package.
          ,packageVersion :: !Version                     -- ^ Version of the package
          ,packageDir :: !(Path Abs Dir)                  -- ^ Directory of the package.
          ,packageFiles :: !(Set (Path Abs File))         -- ^ Files that the package depends on.
          ,packageDeps :: !(Map PackageName VersionRange) -- ^ Packages that the package depends on.
          ,packageTools :: ![Dependency]                  -- ^ A build tool name.
          ,packageAllDeps :: !(Set PackageName)           -- ^ Original dependencies (not sieved).
          ,packageFlags :: !(Map Text Bool)               -- ^ Flags used on package.
          }
 deriving (Show,Typeable)

-- | Package build configuration
data PackageConfig =
  PackageConfig {packageConfigEnableTests :: !Bool        -- ^ Are tests enabled?
                ,packageConfigEnableBenchmarks :: !Bool   -- ^ Are benchmarks enabled?
                ,packageConfigFlags :: !(Map Text Bool)   -- ^ Package config flags.
                }
 deriving (Show,Typeable)

-- | Compares the package name.
instance Ord Package where
  compare = on compare packageName

-- | Compares the package name.
instance Eq Package where
  (==) = on (==) packageName

-- | Reads and exposes the package information
readPackage :: (MonadLogger m, MonadIO m, MonadThrow m)
            => PackageConfig
            -> Path Abs File
            -> m Package
readPackage packageConfig cabalfp =
  do chars <-
       liftIO (Prelude.readFile (FL.toFilePath cabalfp))
     case parsePackageDescription chars of
       ParseFailed per ->
         liftedThrowIO (PackageInvalidCabalFile cabalfp per)
       ParseOk _ gpkg ->
         let pkgId =
               package (packageDescription gpkg)
             name = fromCabalPackageName (pkgName pkgId)
             pkgFlags =
               packageConfigFlags packageConfig
             pkg =
               resolvePackage packageConfig gpkg
         in case packageDependencies pkg of
              deps
                | M.null deps ->
                  liftedThrowIO (PackageNoDeps cabalfp)
                | otherwise ->
                  do let dir = FL.parentAbs cabalfp
                     pkgFiles <-
                       liftIO (packageDescFiles dir pkg)
                     let files = cabalfp : pkgFiles
                         deps' =
                           M.filterWithKey (const . (/= name))
                                           deps
                     return (Package {packageName = name
                                   ,packageVersion = pkgVersion pkgId
                                   ,packageDeps = deps'
                                   ,packageDir = dir
                                   ,packageFiles = S.fromList files
                                   ,packageTools = packageDescTools pkg
                                   ,packageFlags = pkgFlags
                                   ,packageAllDeps =
                                      S.fromList (M.keys deps')})
  where liftedThrowIO = liftIO . throwIO

-- | Get all dependencies of the package (buildable targets only).
packageDependencies :: PackageDescription -> Map PackageName VersionRange
packageDependencies =
  M.fromList .
  concatMap (map (\dep -> ((depName dep),depRange dep)) .
             targetBuildDepends) .
  allBuildInfo

-- | Get all dependencies of the package (buildable targets only).
packageDescTools :: PackageDescription -> [Dependency]
packageDescTools = concatMap buildTools . allBuildInfo

-- | Get all files referenced by the package.
packageDescFiles :: Path Abs Dir -> PackageDescription -> IO [Path Abs File]
packageDescFiles dir pkg =
{'fraction_non_alphanumeric': 0.0699428192591365, 'fraction_numerical': 0.0, 'mean_word_length': 1.9248666989820649, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 20, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6402635', 'n_tokens_mistral': 2981, 'n_tokens_neox': 2879, 'n_words': 901}