1{-# LANGUAGE DeriveDataTypeable #-}
2{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE NoImplicitPrelude #-}
4{-# LANGUAGE OverloadedStrings #-}
5{-# LANGUAGE RankNTypes #-}
6{-# LANGUAGE ViewPatterns #-}
7-- | Dealing with the 00-index file and all its cabal files.
8module Stackage.PackageIndex
10 , UnparsedCabalFile (..)
11 , getLatestDescriptions
14import qualified Codec.Archive.Tar as Tar
15import Data.Conduit.Lazy (MonadActive,
17import qualified Data.Text as T
18import Distribution.PackageDescription (package,
20import Distribution.PackageDescription.Parse (ParseResult (..),
21 parsePackageDescription)
22import Distribution.ParseUtils (PError)
23import Stackage.Prelude
24import System.Directory (getAppUserDataDirectory)
26-- | Name of the 00-index.tar downloaded from Hackage.
27getPackageIndexPath :: MonadIO m => m FilePath
28getPackageIndexPath = liftIO $ do
30 configLines <- runResourceT $ sourceFile (c </> "config")
33 =$ concatMapC getRemoteCache
36 [x] -> return $ x </> "hackage.haskell.org" </> "00-index.tar"
37 [] -> error $ "No remote-repo-cache found in Cabal config file"
38 _ -> error $ "Multiple remote-repo-cache entries found in Cabal config file"
40 getCabalRoot :: IO FilePath
41 getCabalRoot = fpFromString <$> getAppUserDataDirectory "cabal"
44 ("remote-repo-cache", stripPrefix ":" -> Just v) <- Just $ break (== ':') s
45 Just $ fpFromText $ T.strip v
47-- | A cabal file with name and version parsed from the filepath, and the
48-- package description itself ready to be parsed. It's left in unparsed form
50data UnparsedCabalFile = UnparsedCabalFile
51 { ucfName :: PackageName
52 , ucfVersion :: Version
53 , ucfParse :: forall m. MonadThrow m => m GenericPackageDescription
56-- | Stream all of the cabal files from the 00-index tar file.
57sourcePackageIndex :: (MonadThrow m, MonadResource m, MonadActive m, MonadBaseControl IO m)
58 => Producer m UnparsedCabalFile
59sourcePackageIndex = do
60 fp <- getPackageIndexPath
61 -- yay for the tar package. Use lazyConsume instead of readFile to get some
62 -- kind of resource protection
63 lbs <- lift $ fromChunks <$> lazyConsume (sourceFile fp)
66 loop (Tar.Next e es) = goE e >> loop es
67 loop Tar.Done = return ()
68 loop (Tar.Fail e) = throwM e
71 | Just front <- stripSuffix ".cabal" $ pack $ Tar.entryPath e
72 , Tar.NormalFile lbs _size <- Tar.entryContent e = do
73 (name, version) <- parseNameVersion front
74 yield UnparsedCabalFile
76 , ucfVersion = version
77 , ucfParse = goContent (Tar.entryPath e) name version lbs
79 | otherwise = return ()
81 goContent fp name version lbs =
82 case parsePackageDescription $ unpack $ decodeUtf8 lbs of
83 ParseFailed e -> throwM $ CabalParseException (fpFromString fp) e
84 ParseOk _warnings gpd -> do
85 let pd = packageDescription gpd
86 PackageIdentifier name' version' = package pd
87 when (name /= name' || version /= version') $
88 throwM $ MismatchedNameVersion (fpFromString fp)
89 name name' version version'
92 parseNameVersion t1 = do
93 let (p', t2) = break (== '/') $ T.replace "\\" "/" t1
95 t3 <- maybe (throwM $ InvalidCabalPath t1 "no slash") return
97 let (v', t4) = break (== '/') t3
99 when (t4 /= cons '/' p') $ throwM $ InvalidCabalPath t1 $ "Expected at end: " ++ p'
102data InvalidCabalPath = InvalidCabalPath Text Text
103 deriving (Show, Typeable)
104instance Exception InvalidCabalPath
106data CabalParseException = CabalParseException FilePath PError
107 | MismatchedNameVersion FilePath PackageName PackageName Version Version
108 deriving (Show, Typeable)
109instance Exception CabalParseException
111-- | Get all of the latest descriptions for name/version pairs matching the
113getLatestDescriptions :: MonadIO m
114 => (PackageName -> Version -> Bool)
115 -> (GenericPackageDescription -> IO desc)
116 -> m (Map PackageName desc)
117getLatestDescriptions f parseDesc = liftIO $ do
118 m <- runResourceT $ sourcePackageIndex $$ filterC f' =$ foldlC add mempty
119 forM m $ \ucf -> liftIO $ ucfParse ucf >>= parseDesc
121 f' ucf = f (ucfName ucf) (ucfVersion ucf)
123 case lookup name m of
124 Just ucf' | ucfVersion ucf < ucfVersion ucf' -> m
125 _ -> insertMap name ucf m
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list