packages feed

hackage-server-0.4: Distribution/Server/Packages/Types.hs

{-# LANGUAGE GeneralizedNewtypeDeriving, DeriveDataTypeable, StandaloneDeriving, TemplateHaskell #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  Distribution.Server.Packages.Types
-- Copyright   :  (c) David Himmelstrup 2005
-- License     :  BSD-like
--
-- Maintainer  :  lemmih@gmail.com
-- Stability   :  provisional
-- Portability :  portable
--
-- All data types for the entire cabal-install system gathered here to avoid some .hs-boot files.
-----------------------------------------------------------------------------
module Distribution.Server.Packages.Types where

import Distribution.Server.Users.Types (UserId)
import Distribution.Server.Framework.BlobStorage (BlobId)
import Distribution.Server.Framework.Instances ()
import Distribution.Server.Framework.MemSize
import Distribution.Server.Util.Parse (unpackUTF8)

import Distribution.Package
         ( PackageIdentifier(..), Package(..) )
import Distribution.PackageDescription
         ( GenericPackageDescription(..))
import Distribution.PackageDescription.Parse
         ( parsePackageDescription, ParseResult(..) )

import qualified Data.Serialize as Serialize
import Data.Serialize (Serialize)
import Data.ByteString.Lazy (ByteString)
import Data.Time.Clock (UTCTime)
import Data.Typeable (Typeable)
import Data.List (sortBy)
import Data.Ord (comparing)
import Data.SafeCopy

newtype CabalFileText = CabalFileText { cabalFileByteString :: ByteString }
  deriving (Eq, Serialize, MemSize)

cabalFileString :: CabalFileText -> String
cabalFileString = unpackUTF8 . cabalFileByteString

instance SafeCopy CabalFileText where
  putCopy = contain . Serialize.put
  getCopy = contain Serialize.get

instance Show CabalFileText where
    show cft = "CabalFileText (Data.ByteString.Lazy.Char8.pack (Distribution.Simple.Utils.toUTF8 " ++ show (cabalFileString cft) ++ "))"

-- | The information we keep about a particular version of a package.
--
-- Previous versions of this package name and version may exist as well.
-- We normally disallow re-uploading but may make occasional exceptions.
data PkgInfo = PkgInfo {
    pkgInfoId :: !PackageIdentifier,
    -- | The .cabal file text.
    pkgData   :: !CabalFileText,
    -- | The actual package .tar.gz file. It is optional for making an incomplete
    -- mirror, e.g. using archives of just the latest packages, or perhaps for a
    -- multipart upload process.
    --
    -- The canonical tarball URL points to the most recently uploaded package.
    pkgTarball :: ![(PkgTarball, UploadInfo)],
    -- | Previous data. The UploadInfo does *not* indicate when the ByteString was
    -- uploaded, but rather when it was replaced. This way, pkgUploadData won't change
    -- even if a cabal file is changed.
    -- Should be updated whenever a tarball is uploaded (see mergePkg state function)
    pkgDataOld :: ![(CabalFileText, UploadInfo)],
    -- | When the package was created. Imports will override this with time in their logs.
    pkgUploadData :: !UploadInfo
} deriving (Eq, Typeable, Show)

instance SafeCopy PkgInfo where
  putCopy = contain . Serialize.put
  getCopy = contain Serialize.get

-- | The information held in a parsed .cabal file (used by cabal-install)
pkgDesc :: PkgInfo -> GenericPackageDescription
pkgDesc pkgInfo
     = case parsePackageDescription $ cabalFileString $ pkgData pkgInfo of
       -- We only make PkgInfos with parsable pkgDatas, so if it
       -- doesn't parse then something has gone wrong.
       ParseFailed e -> error ("Internal error: " ++ show e)
       ParseOk _ x   -> x

data PkgTarball = PkgTarball {
   pkgTarballGz   :: !BlobId,
   pkgTarballNoGz :: !BlobId
} deriving (Eq, Typeable, Show)

type UploadInfo = (UTCTime, UserId)

pkgUploadTime :: PkgInfo -> UTCTime
pkgUploadTime = fst . pkgUploadData

pkgUploadUser :: PkgInfo -> UserId
pkgUploadUser = snd . pkgUploadData

-- a small utility
descendUploadTimes :: [(a, UploadInfo)] -> [(a, UploadInfo)]
descendUploadTimes = sortBy (flip $ comparing (fst . snd))

instance Package PkgInfo where packageId = pkgInfoId

instance Serialize PkgInfo where
  put pkgInfo = do
    Serialize.put (pkgInfoId pkgInfo)
    Serialize.put (pkgData pkgInfo)
    Serialize.put (pkgTarball pkgInfo)
    Serialize.put (pkgDataOld pkgInfo)
    Serialize.put (pkgUploadData pkgInfo)

  get = do
    infoId  <- Serialize.get
    cabal   <- Serialize.get
    tarball <- Serialize.get
    old     <- Serialize.get
    updata  <- Serialize.get
    return PkgInfo {
        pkgInfoId = infoId,
        pkgUploadData = updata,
        pkgDataOld    = old,
        pkgTarball    = tarball,
        pkgData       = cabal
    }

instance Serialize PkgTarball where
    put tb = do
      Serialize.put (pkgTarballGz tb)
      Serialize.put (pkgTarballNoGz tb)
    get = do
      gz <- Serialize.get
      noGz <- Serialize.get
      return PkgTarball {
          pkgTarballGz = gz,
          pkgTarballNoGz = noGz
      }

instance SafeCopy PkgTarball where
  putCopy = contain . Serialize.put
  getCopy = contain Serialize.get

instance MemSize PkgInfo where
    memSize (PkgInfo a b c d e) = memSize5 a b c d e

instance MemSize PkgTarball where
    memSize (PkgTarball a b) = memSize2 a b