debian-3.23: Debian/Repo/Types.hs
module Debian.Repo.Types
( EnvRoot(..)
, EnvPath(..)
, outsidePath
, appendPath
, rootEnvPath
-- * Repository
, PackageVersion(..)
, PkgVersion(..)
, Repo(..)
, libraryCompatibilityLevel
, compatibilityFile
, Repository(..)
, LocalRepository(..)
, Layout(..)
-- * Release
, ReleaseName(..)
, parseReleaseName
, ReleaseInfo(..)
, Arch(..)
, archName
, Section(..)
, SubSection(..)
, sectionNameOfSubSection
, sectionName
, sectionName'
, parseSection
, parseSection'
, Release(..)
, releaseName'
, releaseName
, releaseComponents
, releaseArchitectures
-- * Each line of the sources.list represents a slice of a repository
, SourceType(..)
, DebSource(..)
, SliceName(..)
, Slice(..)
, SliceList(..)
, NamedSliceList(..)
-- * Package, Source and Binary Debs
, PackageIndex(..)
, PackageIndexLocal
, PackageID(..)
, BinaryPackage(..)
, SourcePackage(..)
, SourceFileSpec(..)
, PackageIDLocal
, BinaryPackageLocal
, SourcePackageLocal
-- * Cached OS Image
, AptCache(..)
, AptBuildCache(..)
, AptImage(..)
) where
import qualified Debian.Control.ByteString as B
import qualified Debian.Relation as B
import Debian.URI
import Debian.Version
import Control.Exception (throw)
import qualified Data.ByteString.Lazy.Char8 as L
import Data.Char
import Data.List
import Data.Maybe
import Network.URI
import System.FilePath ((</>))
import System.Posix.Types
--import System.Unix.Process
-- |The root directory of an OS image.
data EnvRoot = EnvRoot { rootPath :: FilePath } deriving (Ord, Eq, Read, Show)
-- |A directory inside of an OS image.
data EnvPath = EnvPath { envRoot :: EnvRoot
, envPath :: FilePath
} deriving (Ord, Eq, Read, Show)
outsidePath :: EnvPath -> FilePath
outsidePath path = rootPath (envRoot path) ++ envPath path
appendPath :: FilePath -> EnvPath -> EnvPath
appendPath suff path = path { envPath = envPath path ++ suff }
rootEnvPath :: FilePath -> EnvPath
rootEnvPath s = EnvPath { envRoot = EnvRoot "", envPath = s }
--------------------- REPOSITORY -----------------------
-- | The Repository type reprents any instance of the Repo class, so
-- it might be local or remote.
--data Repository = forall a. (Repo a) => Repository a
data Repository
= LocalRepo LocalRepository
| VerifiedRepo URIString [ReleaseInfo]
| UnverifiedRepo URIString
deriving (Read, Show)
instance Ord Repository where
compare a b = compare (repoURI a) (repoURI b)
instance Eq Repository where
a == b = compare a b == EQ
data LocalRepository
= LocalRepository
{ repoRoot :: EnvPath
, repoLayout :: (Maybe Layout)
, repoReleaseInfoLocal :: [ReleaseInfo]
} deriving (Read, Show, Ord, Eq)
-- |The possible file arrangements for a repository. An empty
-- repository does not yet have either of these attributes.
data Layout = Flat | Pool deriving (Eq, Ord, Read, Show)
instance Repo Repository where
repoURI (LocalRepo (LocalRepository path _ _)) = fromJust . parseURI $ "file://" ++ envPath path
repoURI (VerifiedRepo uri _) = fromJust (parseURI uri)
repoURI (UnverifiedRepo uri) = fromJust (parseURI uri)
{-
repoURI (Repository a) = repoURI a
repositoryCompatibilityLevel (Repository a) = repositoryCompatibilityLevel a
repoReleaseInfo (Repository a) = repoReleaseInfo a
-}
repoReleaseInfo (LocalRepo (LocalRepository _ _ info)) = info
repoReleaseInfo (VerifiedRepo _ info) = info
repoReleaseInfo (UnverifiedRepo _uri) = error "No release info for unverified repository"
instance Repo LocalRepository where
repoURI (LocalRepository path _ _) = fromJust . parseURI $ "file://" ++ envPath path
repoReleaseInfo (LocalRepository _ _ info) = info
class (Ord t, Eq t) => Repo t where
repoURI :: t -> URI
repositoryCompatibilityLevel :: t -> IO (Maybe Int)
repositoryCompatibilityLevel r =
fileFromURI uri' >>= either throw (return . parse . L.unpack)
where
uri' = uri {uriPath = uriPath uri </> compatibilityFile}
uri = repoURI r
{-
lazyCommand cmd L.empty >>= return . collectOutput >>= test cmd
where
cmd = "curl -s -g '" ++ uriToString id (repoURI r) "" </> compatibilityFile ++ "'"
test _ (out, _err, [ExitSuccess]) = return (parse (L.unpack out))
test cmd (_, _, _) = error $ "*** FAILURE: " ++ cmd
-}
parse :: String -> Maybe Int
parse text = case takeWhile isDigit text of
"" -> Nothing
s -> Just . read $ s
-- | This method returns a list of all the release in the
-- repository. This can be used to identify all of the files
-- in the repository that are not garbage.
repoReleaseInfo :: t -> [ReleaseInfo]
checkCompatibility :: t -> IO ()
checkCompatibility repo =
do level <- repositoryCompatibilityLevel repo
case level of
Nothing -> return ()
Just n | n >= libraryCompatibilityLevel -> return ()
Just n -> error ("Compatibility error: repository level " ++ show n ++
" < library level " ++ show libraryCompatibilityLevel ++ ", please upgrade.")
-- |The name of the file which holds the repository's compatibility
-- level.
compatibilityFile :: FilePath
compatibilityFile = "repository-compat"
-- | The compatibility level of this library and any applications
-- which use it. It is an error if we try to use a repository whose
-- compatibility level is higher than this, a newer version of the
-- library must be used. This value was increased from 1 to 2 due
-- to a new version number tagging policy.
libraryCompatibilityLevel :: Int
libraryCompatibilityLevel = 2
class (Eq a, Ord a) => PackageVersion a where
pkgName :: a -> B.PkgName
pkgVersion :: a -> DebianVersion
-- |This is an old type which is still used to interface with the
-- Debian.Relation module.
data PkgVersion = PkgVersion { getName :: B.PkgName
, getVersion :: DebianVersion
} deriving (Eq, Ord)
instance PackageVersion PkgVersion where
pkgName = getName
pkgVersion = getVersion
instance Show PkgVersion where
show v = getName v ++ "=" ++ show (getVersion v)
-------------------- RELEASE --------------------
-- |A distribution (aka release) name. This type is expected to refer
-- to a subdirectory of the dists directory which is at the top level
-- of a repository.
data ReleaseName = ReleaseName { relName :: String } deriving (Eq, Ord, Read, Show)
parseReleaseName :: String -> ReleaseName
parseReleaseName name = ReleaseName {relName = unEscapeString name}
releaseName' :: ReleaseName -> String
releaseName' (ReleaseName {relName = s}) = escapeURIString isAllowedInURI s
-- FIXME: The lists here should be sets so that == and compare work properly.
data ReleaseInfo = ReleaseInfo { releaseInfoName :: ReleaseName
, releaseInfoAliases :: [ReleaseName]
, releaseInfoArchitectures :: [Arch]
, releaseInfoComponents :: [Section]
} deriving (Eq, Ord, Read, Show)
-- |The types of architecture that a package can have, either Source
-- or some type of binary architecture.
data Arch = Source | Binary String deriving (Read, Show, Eq, Ord)
archName :: Arch -> String
archName Source = "source"
archName (Binary arch) = arch
-- |A section of a repository such as main, contrib, non-free,
-- restricted. The indexes for a section are located below the
-- distribution directory.
newtype Section = Section String deriving (Read, Show, Eq, Ord)
-- |A package's subsection is only evident in its control information,
-- packages from different subsections all reside in the same index.
data SubSection = SubSection { section :: Section, subSectionName :: String } deriving (Read, {-Show,-} Eq, Ord)
sectionName :: SubSection -> String
sectionName (SubSection (Section "main") y) = y
sectionName (SubSection x y) = sectionName' x ++ "/" ++ y
sectionName' :: Section -> String
sectionName' (Section s) = escapeURIString isAllowedInURI s
sectionNameOfSubSection :: SubSection -> String
sectionNameOfSubSection = sectionName' . section
-- |Parse the value that appears in the @Section@ field of a .changes file.
-- (Does this need to be unesacped?)
parseSection section =
case span (/= '/') section of
(x, "") -> SubSection (Section "main") x
("main", y) -> SubSection (Section "main") y
(x, y) -> SubSection (Section x) (tail y)
parseSection' name =
Section (unEscapeString name)
data Release = Release { releaseRepo :: Repository
, releaseInfo :: ReleaseInfo
} deriving (Eq, Ord, Show)
releaseName :: Release -> ReleaseName
releaseName = releaseInfoName . releaseInfo
--releaseAliases :: Release -> [ReleaseName]
--releaseAliases = releaseInfoAliases . releaseInfo
releaseComponents :: Release -> [Section]
releaseComponents = releaseInfoComponents . releaseInfo
releaseArchitectures :: Release -> [Arch]
releaseArchitectures = releaseInfoArchitectures . releaseInfo
----------------- SLICES (SOURCES.LIST ENTRIES) ---------------
data SourceType
= Deb | DebSrc
deriving (Eq, Ord)
data DebSource
= DebSource
{ sourceType :: SourceType
, sourceUri :: URI
, sourceDist :: Either String (ReleaseName, [Section])
} deriving (Eq, Ord)
instance Show SourceType where
show Deb = "deb"
show DebSrc = "deb-src"
instance Show DebSource where
show (DebSource thetype theuri thedist) =
(show thetype) ++ " "++ uriToString id theuri " " ++
(case thedist of
Left exactPath -> escape exactPath
Right (dist, sections) ->
releaseName' dist ++ " " ++ intercalate " " (map sectionName' sections))
where escape = escapeURIString isAllowedInURI
-- |This is a name given to a combination of parts of one or more
-- releases that can be specified by a sources.list file.
data SliceName = SliceName { sliceName :: String } deriving (Eq, Ord)
data Slice
= Slice { sliceRepo :: Repository
, sliceSource :: DebSource
} deriving (Eq, Ord)
data SliceList = SliceList {slices :: [Slice]} deriving (Eq, Ord)
data NamedSliceList
= NamedSliceList { sliceList :: SliceList
, sliceListName :: SliceName
} deriving (Eq, Ord)
instance Show Slice where
show = show . sliceSource
instance Show SliceList where
show = concat . map ((++ "\n") . show) . slices
---------------- PACKAGES AND PACKAGE INDEXES -------------
-- |The PackageIndex type represents a file containing control
-- information about debian packages, either source or binary.
-- Though the control information for a binary package does not
-- specify an architecture, the architecture here is that of
-- the environment where the package information is cached.
data PackageIndex
= PackageIndex { packageIndexRelease :: Release
, packageIndexComponent :: Section
, packageIndexArch :: Arch
} deriving (Eq, Ord, Show)
type PackageIndexLocal = PackageIndex
instance Show BinaryPackage where
show p = packageName (packageID p) ++ "-" ++ show (packageVersion (packageID p))
-- | The 'PackageID' type fully identifies a package by name, version,
-- and a 'PackageIndex' which identifies the package's release,
-- component and architecture.
data PackageID
= PackageID
{ packageIndex :: PackageIndex
, packageName :: String
, packageVersion :: DebianVersion
} deriving (Eq, Ord, Show)
-- | The 'BinaryPackage' type adds to the 'PackageID' type the control
-- information obtained from the package index.
data BinaryPackage
= BinaryPackage
{ packageID :: PackageID
, packageInfo :: B.Paragraph
, pDepends :: B.Relations
, pPreDepends :: B.Relations
, pConflicts ::B.Relations
, pReplaces :: B.Relations
, pProvides :: B.Relations
}
instance Ord BinaryPackage where
compare a b = compare (packageID a) (packageID b)
instance Eq BinaryPackage where
a == b = (packageID a) == (packageID b)
data SourcePackage
= SourcePackage
{ sourcePackageID :: PackageID
, sourceParagraph :: B.Paragraph
, sourceDirectory :: String
, sourcePackageFiles :: [SourceFileSpec]
}
data SourceFileSpec
= SourceFileSpec
{ sourceFileMD5sum :: String
, sourceFileSize :: FileOffset
, sourceFileName :: FilePath
}
type PackageIDLocal = PackageID
type BinaryPackageLocal = BinaryPackage
type SourcePackageLocal = SourcePackage
---------------------- CACHED OS IMAGE ---------------------
class (Ord t, Eq t, Show t) => AptCache t where
globalCacheDir :: t -> FilePath
-- | The directory you might chroot to.
rootDir :: t -> EnvRoot
-- | The sources.list without the local repository
aptBaseSliceList :: t -> SliceList
-- | The build architecture
aptArch :: t -> Arch
-- | Return the all source packages in this AptCache.
aptSourcePackages :: t -> [SourcePackage]
-- | Return the all binary packages for the architecture of this AptCache.
aptBinaryPackages :: t -> [BinaryPackage]
-- | Name of release
aptReleaseName :: t -> ReleaseName
class AptCache t => AptBuildCache t where
-- | The sources.list
aptSliceList :: t -> SliceList
data AptImage =
AptImage { aptGlobalCacheDir :: FilePath
, aptImageRoot :: EnvRoot
, aptImageArch :: Arch
, aptImageSliceList :: SliceList
, aptImageReleaseName :: ReleaseName
, aptImageSourcePackages :: [SourcePackage]
, aptImageBinaryPackages :: [BinaryPackage]
}