packages feed

module-management-0.20.2: testdata/debian/Debian/Repo/PackageIndex.hs

-- | The 'PackageIndex' type is the result of parsing a file
-- containing control information about debian packages, either source
-- or binary.  Examples of such files include
-- @http://ftp.debian.org/debian/dists/sid/main/source/Sources.bz2@ or
-- @http://ftp.debian.org/debian/dists/sid/main/binary-amd64/Packages.bz2@.
{-# LANGUAGE FlexibleInstances, OverloadedStrings, StandaloneDeriving #-}
module Debian.Repo.PackageIndex
    ( PackageIndex(..)

    , BinaryPackage(..)
    , prettyBinaryPackage

    , SourcePackage(..)
    , SourceControl(..)
    , SourceFileSpec(..)

    , packageIndexName
    , packageIndexPath
    , packageIndexDir
    , packageIndexPaths
    , packageIndexDirs
    , packageIndexes
    , sourceIndexes
    , binaryIndexes
    , releaseDir

    , sortSourcePackages
    , sortBinaryPackages
    ) where

import Data.List as List (sortBy, map, filter)
import Data.Monoid ((<>))
import Data.Set as Set (map, filter, toList, unions)
import Data.Text (Text)
import Debian.Arch (Arch(..), ArchOS(..), ArchCPU(..), prettyArch)
import qualified Debian.Control.Text as T
import Debian.Relation (BinPkgName(..), SrcPkgName(..))
import qualified Debian.Relation as B (Relations)
import Debian.Pretty (PP(..), ppPrint)
import Debian.Release (releaseName', Section(..), sectionName')
import Debian.Repo.PackageID (PackageID(packageName, packageVersion), prettyPackageID)
import Debian.Repo.Release (Release(..))
import System.FilePath ((</>))
import System.Posix.Types (FileOffset)
import Text.PrettyPrint.HughesPJClass (Doc, Pretty(pPrint), text)

-- | A package index is identified by Section (e.g. main, contrib,
-- non-free) and architecture (e.g. source, i386, amd64.)
data PackageIndex
    = PackageIndex { packageIndexComponent :: Section
                   , packageIndexArch :: Arch
                   } deriving (Eq, Ord, Show)

instance Pretty (PP PackageIndex) where
    pPrint (PP x) = ppPrint (packageIndexComponent x) <> text "_" <> ppPrint (packageIndexArch x)

instance Pretty (PP Section) where
    pPrint (PP (Section x)) = pPrint x

instance Pretty (PP Arch) where
    pPrint (PP Source) = text "source"
    pPrint (PP All) = text "all"
    pPrint (PP (Binary ArchOSAny ArchCPUAny)) = text "any"
    pPrint (PP (Binary (ArchOS os) ArchCPUAny)) = ppPrint os
    pPrint (PP (Binary ArchOSAny (ArchCPU cpu))) = ppPrint cpu
    pPrint (PP (Binary (ArchOS os) (ArchCPU cpu))) = ppPrint (os <> "-" <> cpu)

{-
instance PackageVersion BinaryPackage where
    pkgName = binaryPackageName
    pkgVersion = packageVersion . packageID
-}

-- | The 'BinaryPackage' type adds to the 'PackageID' type the control
-- information obtained from the package index.
data BinaryPackage
    = BinaryPackage
      { packageID :: PackageID BinPkgName
      , packageInfo :: T.Paragraph
      , pDepends :: B.Relations
      , pPreDepends :: B.Relations
      , pConflicts ::B.Relations
      , pReplaces :: B.Relations
      , pProvides :: B.Relations
      } deriving (Show)

-- In a perfect world these would be valid Eq and Ord instances, but
-- they are not because different builds of the same version can occur
-- on different machines that are unaware of each other.  These should
-- be removed.  But they are currently in use.

instance Ord BinaryPackage where
    compare a b = compare (packageID a) (packageID b)

instance Eq BinaryPackage where
    a == b = (packageID a) == (packageID b)

prettyBinaryPackage :: BinaryPackage -> Doc
prettyBinaryPackage = prettyPackageID . packageID

data SourcePackage
    = SourcePackage
      { sourcePackageID :: PackageID SrcPkgName
      , sourceParagraph :: T.Paragraph
      , sourceControl :: SourceControl
      , sourceDirectory :: String
      , sourcePackageFiles :: [SourceFileSpec]
      } deriving (Show, Eq, Ord)

-- |Source package information derived from the control paragraph.
data SourceControl
    = SourceControl
      { source :: Text
      , maintainer :: NameAddr
      , uploaders :: [NameAddr]
      , packageSection :: Maybe Section' -- Should this be the same type as the Section field in a .changes file?
      , packagePriority :: Maybe Priority
      , buildDepends :: [Package]
      , buildDependsIndep :: [Package]
      , buildConflicts :: [Package]
      , buildConflictsIndep :: [Package]
      , standardsVersion :: Maybe StandardsVersion -- There are packages that don't have this
      , homepage :: Maybe Text -- There are packages that don't have this
      } deriving (Show, Eq, Ord)

type NameAddr = Text
type StandardsVersion = Text
type Section' = Text
type Priority = Text
type Package = Text

data SourceFileSpec
    = SourceFileSpec
      { sourceFileMD5sum :: String
      , sourceFileSize :: FileOffset
      , sourceFileName :: FilePath
      }
    deriving (Show, Eq, Ord)

packageIndexName :: PackageIndex -> FilePath
packageIndexName index =
    case packageIndexArch index of
      Source -> "Sources"
      _ -> "Packages"

packageIndexPath :: Release -> PackageIndex -> FilePath
packageIndexPath release index = packageIndexDir release index </> packageIndexName index

packageIndexDir :: Release -> PackageIndex -> FilePath
packageIndexDir release index =
    case packageIndexArch index of
      Source -> releaseDir release </> sectionName' (packageIndexComponent index) </> "source"
      _ -> (releaseDir release </> sectionName' (packageIndexComponent index) </>
            -- Will prettyArch give us linux-amd64 when we just want amd64?
            "binary-" ++ show (prettyArch (packageIndexArch index)))

releaseDir :: Release -> String
releaseDir release = "dists" </> (releaseName' . releaseName $ release)

packageIndexPaths :: Release -> [FilePath]
packageIndexPaths release = List.map (packageIndexPath release) . packageIndexes $ release

packageIndexDirs :: Release -> [FilePath]
packageIndexDirs release = List.map (packageIndexDir release) . packageIndexes $ release

packageIndexes :: Release -> [PackageIndex]
packageIndexes release = sourceIndexes release ++ binaryIndexes release

sourceIndexes :: Release -> [PackageIndex]
sourceIndexes release =
    List.map componentIndex (releaseComponents $ release)
    where componentIndex component = PackageIndex { packageIndexComponent = component
                                                  , packageIndexArch = Source }

binaryIndexes :: Release -> [PackageIndex]
binaryIndexes release =
    toList . unions . List.map componentIndexes $ releaseComponents release
    where
      --componentIndexes :: Section -> [PackageIndex]
      componentIndexes component =
          Set.map archIndex (Set.filter (/= Source) (releaseArchitectures release))
          where
            --archIndex :: Arch -> PackageIndex
            archIndex arch = PackageIndex { packageIndexComponent = component
                                          , packageIndexArch = arch }

-- | Return a sorted list of available source packages, newest version first.
sortSourcePackages :: [SrcPkgName] -> [SourcePackage] -> [SourcePackage]
sortSourcePackages names pkgs =
    sortBy cmp . filterNames $ pkgs
    where
      filterNames :: [SourcePackage] -> [SourcePackage]
      filterNames packages =
          List.filter (flip elem names . packageName . sourcePackageID) packages
      cmp p1 p2 =
          compare v2 v1		-- Flip args to get newest first
          where
            v1 = packageVersion . sourcePackageID $ p1
            v2 = packageVersion . sourcePackageID $ p2

sortBinaryPackages :: [BinPkgName] -> [BinaryPackage] -> [BinaryPackage]
sortBinaryPackages names pkgs =
    sortBy cmp . filterNames $ pkgs
    where
      filterNames :: [BinaryPackage] -> [BinaryPackage]
      filterNames packages =
          List.filter (flip elem names . packageName . packageID) packages
      cmp p1 p2 =
          compare v2 v1		-- Flip args to get newest first
          where
            v1 = packageVersion . packageID $ p1
            v2 = packageVersion . packageID $ p2