on master help sign in
PackageIndex
658a52bMichael Snoyman
Stackage2/PackageIndex.hsnew file+96
@@ -0,0 +1,96 @@
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.
8+module Stackage2.PackageIndex
9+ ( sourcePackageIndex
10+ ) where
11+
12+import qualified Codec.Archive.Tar as Tar
13+import qualified Codec.Archive.Tar.Entry as TarEntry
14+import Data.Conduit.Lazy (MonadActive,
15+ lazyConsume)
16+import qualified Data.Text as T
17+import Distribution.PackageDescription (GenericPackageDescription)
18+import Distribution.PackageDescription.Parse (ParseResult (..),
19+ parsePackageDescription)
20+import Distribution.ParseUtils (PError)
21+import Stackage2.Prelude
22+import System.Directory (getAppUserDataDirectory)
23+
24+-- | Name of the 00-index.tar downloaded from Hackage.
25+getPackageIndexPath :: MonadIO m => m FilePath
26+getPackageIndexPath = liftIO $ do
27+ c <- getCabalRoot
28+ configLines <- runResourceT $ sourceFile (c </> "config")
29+ $$ decodeUtf8C
30+ =$ linesUnboundedC
31+ =$ concatMapC getRemoteCache
32+ =$ sinkList
33+ case configLines of
34+ [x] -> return $ x </> "hackage.haskell.org" </> "00-index.tar"
35+ [] -> error $ "No remote-repo-cache found in Cabal config file"
36+ _ -> error $ "Multiple remote-repo-cache entries found in Cabal config file"
37+ where
38+ getCabalRoot :: IO FilePath
39+ getCabalRoot = fpFromString <$> getAppUserDataDirectory "cabal"
40+
41+ getRemoteCache s = do
42+ ("remote-repo-cache", stripPrefix ":" -> Just v) <- Just $ break (== ':') s
43+ Just $ fpFromText $ T.strip v
44+
45+data UnparsedCabalFile = UnparsedCabalFile
46+ { ucfName :: PackageName
47+ , ucfVersion :: Version
48+ , ucfParse :: forall m. MonadThrow m => m GenericPackageDescription
49+ }
50+
51+sourcePackageIndex :: (MonadThrow m, MonadResource m, MonadActive m, MonadBaseControl IO m)
52+ => Producer m UnparsedCabalFile
53+sourcePackageIndex = do
54+ fp <- getPackageIndexPath
55+ -- yay for the tar package. Use lazyConsume instead of readFile to get some
56+ -- kind of resource protection
57+ lbs <- lift $ fromChunks <$> lazyConsume (sourceFile fp)
58+ loop (Tar.read lbs)
59+ where
60+ loop (Tar.Next e es) = goE e >> loop es
61+ loop Tar.Done = return ()
62+ loop (Tar.Fail e) = throwM e
63+
64+ goE e
65+ | Just front <- stripSuffix ".cabal" $ pack $ Tar.entryPath e
66+ , Tar.NormalFile lbs _size <- Tar.entryContent e = do
67+ (name, version) <- parseNameVersion front
68+ yield UnparsedCabalFile
69+ { ucfName = name
70+ , ucfVersion = version
71+ , ucfParse = goContent (Tar.entryPath e) lbs
72+ }
73+ | otherwise = return ()
74+
75+ goContent fp lbs =
76+ case parsePackageDescription $ unpack $ decodeUtf8 lbs of
77+ ParseFailed e -> throwM $ CabalParseException (fpFromString fp) e
78+ ParseOk _warnings gpd -> return gpd
79+
80+ parseNameVersion t1 = do
81+ let (p', t2) = break (== '/') t1
82+ p <- simpleParse p'
83+ t3 <- maybe (throwM $ InvalidCabalPath t1 "no slash") return
84+ $ stripPrefix "/" t2
85+ let (v', t4) = break (== '/') t3
86+ v <- simpleParse v'
87+ when (t4 /= cons '/' p') $ throwM $ InvalidCabalPath t1 $ "Expected at end: " ++ p'
88+ return (p, v)
89+
90+data InvalidCabalPath = InvalidCabalPath Text Text
91+ deriving (Show, Typeable)
92+instance Exception InvalidCabalPath
93+
94+data CabalParseException = CabalParseException FilePath PError
95+ deriving (Show, Typeable)
96+instance Exception CabalParseException
Stackage2/Prelude.hs+3−3
@@ -28,7 +28,7 @@ simpleParse :: (MonadThrow m, DT.Text a, Typeable a, MonoFoldable text, Element
28 => text -> m a
28 => text -> m a
29simpleParse orig = withTypeRep $ \rep ->
29simpleParse orig = withTypeRep $ \rep ->
30 case DT.simpleParse str of
30 case DT.simpleParse str of
31− Nothing -> throwM (ParseFailed rep (pack str))
31+ Nothing -> throwM (ParseFailedException rep (pack str))
32 Just v -> return v
32 Just v -> return v
33 where
33 where
34 str = unpack orig
34 str = unpack orig
@@ -42,9 +42,9 @@ simpleParse orig = withTypeRep $ \rep ->
42 unwrap :: m a -> a
42 unwrap :: m a -> a
43 unwrap _ = error "unwrap"
43 unwrap _ = error "unwrap"
44
44
45−data ParseFailed = ParseFailed TypeRep Text
45+data ParseFailedException = ParseFailedException TypeRep Text
46 deriving (Show, Typeable)
46 deriving (Show, Typeable)
47−instance Exception ParseFailed
47+instance Exception ParseFailedException
48
48
49data ProcessExitedUnsuccessfully = ProcessExitedUnsuccessfully CreateProcess ExitCode
49data ProcessExitedUnsuccessfully = ProcessExitedUnsuccessfully CreateProcess ExitCode
50 deriving Typeable
50 deriving Typeable
stackage.cabal+3
@@ -34,6 +34,7 @@ library
34 Stackage2.Prelude
34 Stackage2.Prelude
35 Stackage2.ProposedPlan
35 Stackage2.ProposedPlan
36 Stackage2.CorePackages
36 Stackage2.CorePackages
37+ Stackage2.PackageIndex
37 build-depends: base >= 4 && < 5
38 build-depends: base >= 4 && < 5
38 , containers
39 , containers
39 , Cabal >= 1.14
40 , Cabal >= 1.14
@@ -51,6 +52,7 @@ library
51 , conduit-extra
52 , conduit-extra
52 , classy-prelude-conduit
53 , classy-prelude-conduit
53 , text
54 , text
55+ , system-fileio
54
56
55executable stackage
57executable stackage
56 default-language: Haskell2010
58 default-language: Haskell2010
@@ -66,6 +68,7 @@ test-suite spec
66 hs-source-dirs: test
68 hs-source-dirs: test
67 main-is: Spec.hs
69 main-is: Spec.hs
68 other-modules: Stackage2.CorePackagesSpec
70 other-modules: Stackage2.CorePackagesSpec
71+ Stackage2.PackageIndexSpec
69 build-depends: base
72 build-depends: base
70 , stackage
73 , stackage
71 , hspec
74 , hspec
test/Stackage2/PackageIndexSpec.hsnew file+9
@@ -0,0 +1,9 @@
1+{-# LANGUAGE OverloadedStrings, NoImplicitPrelude #-}
2+module Stackage2.PackageIndexSpec (spec) where
3+
4+import Stackage2.PackageIndex
5+import Stackage2.Prelude
6+import Test.Hspec
7+
8+spec :: Spec
9+spec = it "works" $ (runResourceT $ sourcePackageIndex $$ sinkNull :: IO ())