packages feed

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

-- | A repository located on localhost
{-# LANGUAGE FlexibleInstances, OverloadedStrings, PackageImports, StandaloneDeriving, ScopedTypeVariables, TemplateHaskell, TypeSynonymInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Debian.Repo.State.Repository
    ( readLocalRepository
    , prepareLocalRepository
    , prepareLocalRepository'
    , prepareRemoteRepository
    , repairLocalRepository
    , foldRepository
    ) where

import Control.Monad (filterM, when)
import Control.Monad.Trans (liftIO, MonadIO)
import Data.Maybe (catMaybes)
import Debian.Release (ReleaseName(ReleaseName), Section(Section))
import Debian.Repo.EnvPath (EnvPath, EnvPath(EnvPath), EnvRoot(EnvRoot), outsidePath)
import Debian.Repo.Internal.Repos (MonadRepos(..), repoByURI, putRepo)
import Debian.Repo.LocalRepository (Layout(..), LocalRepository(..), readLocalRepo)
import Debian.Repo.Release (getReleaseInfoRemote, parseArchitectures, Release(Release, releaseAliases, releaseArchitectures, releaseComponents, releaseName))
import Debian.Repo.RemoteRepository (RemoteRepository, RemoteRepository(RemoteRepository))
import Debian.Repo.Repo (RepoKey(..))
import Debian.URI (fromURI', toURI', URI(uriPath), URI')
import Network.URI (URI(..))
import System.Directory (createDirectoryIfMissing, doesDirectoryExist, getDirectoryContents)
import System.FilePath ((</>))
import System.IO.Unsafe (unsafeInterleaveIO)
import qualified System.Posix.Files as F (fileMode, getFileStatus, setFileMode)
import Text.Regex (matchRegex, mkRegex)

repairLocalRepository :: MonadIO m => LocalRepository -> m LocalRepository
repairLocalRepository r = prepareLocalRepository (repoRoot r) (repoLayout r) (repoReleaseInfoLocal r)

createLocalRepository :: MonadIO m => EnvPath -> Maybe Layout -> m (Maybe Layout)
createLocalRepository root layout = do
  mapM_ (liftIO . initDir)
            [(".", 0o40755),
             ("dists", 0o40755),
             ("incoming", 0o41755),
             ("removed", 0o40750),
             ("reject", 0o40750)]
  -- If repo exists compute its actual layout
  layout' <- liftIO (computeLayout (outsidePath root)) >>= return . maybe layout Just
  -- >>= return . maybe (maybe (error "No layout specified for new repository") id layout) id
  mapM_ (liftIO . initDir)
            (case layout' of
               Just Pool -> [("pool", 0o40755), ("installed", 0o40755)]
               Just Flat -> []
               Nothing -> [])
  return layout'
    where
      initDir (name, mode) =
          do let path = outsidePath root </> name
             filterM (\ f -> doesDirectoryExist f >>= return . not) [path] >>=
                     mapM_ (\ f -> createDirectoryIfMissing True f)
             actualMode <- F.getFileStatus path >>= return . F.fileMode
             when (mode /= actualMode) (F.setFileMode path mode)

readLocalRepository :: MonadIO m => EnvPath -> Maybe Layout -> m (Maybe LocalRepository)
readLocalRepository root layout = createLocalRepository root layout >>= readLocalRepo root

-- | Create or verify the existance of the directories which will hold
-- a repository on the local machine.  Verify the index files for each of
-- its existing releases.
prepareLocalRepository :: MonadIO m => EnvPath -> Maybe Layout -> [Release] -> m LocalRepository
prepareLocalRepository root layout releases =
    readLocalRepository root layout >>= maybe (return $ makeLocalRepo root layout releases) return

prepareLocalRepository' :: MonadIO m => EnvPath -> Maybe Layout -> m LocalRepository
prepareLocalRepository' root layout =
    prepareLocalRepository root layout [Release { releaseName = ReleaseName "precise-seereason"
                                                , releaseAliases = []
                                                , releaseArchitectures = parseArchitectures "amd64, i386"
                                                , releaseComponents = [Section "main"] }]

makeLocalRepo :: EnvPath -> Maybe Layout -> [Release] -> LocalRepository
makeLocalRepo root layout releases =
    LocalRepository { repoRoot = root
                    , repoLayout = layout
                    , repoReleaseInfoLocal = releases }

-- |Try to determine a repository's layout.
computeLayout :: FilePath -> IO (Maybe Layout)
computeLayout root =
    do
      -- If there are already .dsc files in the root directory
      -- the repository layout is Flat.
      isFlat <- getDirectoryContents root >>= return . (/= []) . catMaybes . map (matchRegex (mkRegex "\\.dsc$"))
      -- If the pool directory already exists the repository layout is
      -- Pool.
      isPool <- doesDirectoryExist (root ++ "/pool")
      case (isFlat, isPool) of
        (True, _) -> return (Just Flat)
        (False, True) -> return (Just Pool)
        _ -> return Nothing

prepareRemoteRepository :: MonadRepos m => URI -> m RemoteRepository
prepareRemoteRepository uri =
    let uri' = toURI' uri in
    repoByURI uri' >>= maybe (loadRemoteRepository uri') return

-- |To create a RemoteRepo we must query it to find out the
-- names, sections, and supported architectures of its releases.
loadRemoteRepository :: MonadRepos m => URI' -> m RemoteRepository
loadRemoteRepository uri =
    do releaseInfo <- liftIO . unsafeInterleaveIO . getReleaseInfoRemote . fromURI' $ uri
       let repo = RemoteRepository uri releaseInfo
       putRepo uri repo
       return repo

-- foldRepository :: forall m r a. MonadState ReposState m => (r -> m a) -> RepoKey -> m a
-- foldRepository f key =
--     case key of
--       Local path -> prepareLocalRepository path Nothing >>= f
--       Remote uri' ->
--           let uri = fromURI' uri' in
--           case uriScheme uri of
--             "file:" -> prepareLocalRepository (EnvPath (EnvRoot "") (uriPath uri)) Nothing >>= f
--             _ -> prepareRemoteRepository uri >>= f

foldRepository :: MonadRepos m => (LocalRepository -> m a) -> (RemoteRepository -> m a) -> RepoKey -> m a
foldRepository f g key =
    case key of
      Local path -> readLocalRepository path Nothing >>= maybe (error $ "No repository at " ++ show path) f
      Remote uri' ->
          let uri = fromURI' uri' in
          case uriScheme uri of
            "file:" -> prepareLocalRepository' (EnvPath (EnvRoot "") (uriPath uri)) Nothing >>= f
            _ -> prepareRemoteRepository uri >>= g