1
1
mirror of https://github.com/aelve/guide.git synced 2024-11-25 18:56:52 +03:00

Added stackage parsing. Refactored tests.

This commit is contained in:
Boris M. Yartsev 2017-06-10 19:30:59 +02:00
parent b5c06150fb
commit ebc0941571
10 changed files with 327 additions and 222 deletions

View File

@ -15,8 +15,8 @@ cabal-version: >=1.10
library
hs-source-dirs: src
other-modules: Common, FileUtils, HttpDownload, HackageUpdate, HackageArchive, REPL, Storage, Stackage
exposed-modules: IndexProject
other-modules: Common, FileUtils, HttpDownload, HackageUpdate, REPL, Storage
exposed-modules: IndexProject, HackageArchive, Stackage
build-depends: base >= 4.7 && < 5
, directory
, containers
@ -32,6 +32,7 @@ library
, pureMD5
, aeson
, data-default
, megaparsec
, text
, mtl
, safecopy
@ -67,6 +68,8 @@ test-suite index-project-test
, index-project
, tasty
, tasty-hunit
, megaparsec
, text
ghc-options: -threaded -rtsopts -with-rtsopts=-N
default-language: Haskell2010

View File

@ -1,14 +1,25 @@
module Common(
URL,
module Common(URL,
PackageName,
PackageData,
SnapshotData(..),
UpdateArchiveException(..)
) where
import Data.Version as DV
import Data.Int(Int64)
UpdateArchiveException(..),
HackageUpdateInfo(..),
getArchive,
getArchiveClone,
getTar,
getTarClone,
parseIntEnd,
parseValEnd) where
import qualified Control.Exception as X
import qualified Data.ByteString.Lazy as BL
import qualified Data.List as DL
import Data.Version as DV
import Data.Int(Int64)
import Data.Default
import System.FilePath((</>))
type URL = String
type PackageName = String
@ -19,11 +30,59 @@ data SnapshotData = SnapshotData {
lengthFile :: Int64
} deriving (Eq, Show)
-- The exception, that is raised, when there is problems with creating the
-- snapshot
newtype UpdateArchiveException = UAE String deriving (Show, Eq)
instance X.Exception UpdateArchiveException
-- the constructor short name is really awkward in russian
data HackageUpdateInfo = IUH {
iuhUpdateDir :: FilePath,
iuhSnapshotURL :: URL,
iuhArchiveURL :: URL
} deriving (Eq, Show)
instance Default HackageUpdateInfo where
def = defaultIUH
defaultIUH :: HackageUpdateInfo
defaultIUH = IUH {
iuhUpdateDir = "hackagefiles",
iuhSnapshotURL = "https://hackage.haskell.org/snapshot.json",
iuhArchiveURL = "https://hackage.haskell.org/01-index.tar.gz"
}
archive :: FilePath
archive = "01-index.tar.gz"
archiveClone :: FilePath
archiveClone = "01-index.tar.gz.orig"
tar :: FilePath
tar = "01-index.tar"
tarClone :: FilePath
tarClone = "01-index.orig.tar"
getArchive :: HackageUpdateInfo -> FilePath
getArchive iuh = iuhUpdateDir iuh </> archive
getArchiveClone :: HackageUpdateInfo -> FilePath
getArchiveClone iuh = iuhUpdateDir iuh </> archiveClone
getTar :: HackageUpdateInfo -> FilePath
getTar iuh = iuhUpdateDir iuh </> tar
getTarClone :: HackageUpdateInfo -> FilePath
getTarClone iuh = iuhUpdateDir iuh </> tarClone
parseIntEnd :: (Num a, Read a) => String -> a
parseIntEnd val | not (null l) = read (DL.last l)
| otherwise = 0
where l = words val
parseValEnd :: String -> String
parseValEnd val | DL.length l > 1 = DL.last l
| otherwise = ""
where l = words val

View File

@ -35,7 +35,7 @@ type FileSnapshotData = SnapshotData
-- Calculates the file size
getFileSize :: FilePath -> IO Int64
getFileSize path = getFileStatus path >>= return.fileSize >>= \(COff v) -> return v
getFileSize path = fileSize <$> getFileStatus path >>= \(COff v) -> return v
-- Calculates the snapshot of the file of the archive
calcFileData :: FilePath -> IO FileSnapshotData
@ -51,7 +51,7 @@ calcFileData file = do
-- Calculates the MD5 hash of the file
calcMD5 :: FilePath -> IO MD5Digest
calcMD5 file = BL.readFile file >>= return.md5
calcMD5 file = md5 <$> BL.readFile file
-- Deletes the file it it exists.
removeIfExists :: FilePath -> IO ()
@ -92,10 +92,9 @@ getFileSubstring file from len = do
unzipFile :: FilePath -> FilePath -> IO()
unzipFile from to = do
removeIfExists to
fileBody <- (BL.readFile from)
fileBody <- BL.readFile from
BL.appendFile to (GZip.decompress fileBody)
loadTar :: FilePath -> IO (Tar.Entries Tar.FormatError)
loadTar file = do
content <- BL.readFile file

View File

@ -8,8 +8,8 @@ module HackageArchive (
HackageName,
HackageMap,
HackageUpdateMap,
HackageUpdate
) where
HackageUpdate,
parsePath) where
import qualified Codec.Archive.Tar as Tar
import qualified Data.List.Split as SPLT
@ -69,7 +69,7 @@ parseCabalFilePath = do
guard (name == package)
suff <- RP.string ".cabal"
RP.eof
pure $ (package, version)
pure (package, version)
where phi l = DC.isLetter l || l == '-'
updateMapCompare :: (Ord a) => String -> a -> M.Map String a -> M.Map String a
@ -87,7 +87,7 @@ buildDifferenceMap oldMap newMap = foldr M.union M.empty [deletedMap, addedMap,
addedMap = M.map ((,) Added) $ M.difference newMap oldMap
updatedMap' = M.intersection newMap oldMap
updatedMap = M.map ((,) Updated) $ M.differenceWith diff updatedMap' oldMap
diff newpack oldpack = if (newpack /= oldpack) then Just newpack else Nothing
diff newpack oldpack = if newpack /= oldpack then Just newpack else Nothing
createPackage :: DPD.PackageDescription -> HackagePackage
createPackage pd = HP { name = nm, version = ver, author = auth }
@ -104,7 +104,7 @@ parsePath path = case RP.readP_to_S parseCabalFilePath path of
parsePackageDescription :: Tar.EntryContent -> Maybe DPD.PackageDescription
parsePackageDescription (Tar.NormalFile content _) =
case (DPDP.parsePackageDescription (UTFC.toString content)) of
case DPDP.parsePackageDescription (UTFC.toString content) of
DPDP.ParseOk _ pd -> Just (DPD.packageDescription pd)
DPDP.ParseFailed _ -> Nothing
parsePackageDescription _ = Nothing
@ -137,7 +137,7 @@ buildHackageMap (Tar.Next entry entries) premap =
update path = do
(name, version) <- parsePath path
preversion <- M.lookup name premap
if (preversion == version) then parsePackage entry
if preversion == version then parsePackage entry
else Nothing
buildHackageMap Tar.Done _ = M.empty
buildHackageMap (Tar.Fail e) _ = X.throw e

View File

@ -4,6 +4,13 @@ module HackageUpdate (
performArchiveFileUpdate,
calcUpdateResultIO) where
import Data.Aeson.Types
import qualified Data.Aeson as A
import qualified Data.Aeson.Parser as AP
import qualified Data.Text as T
import qualified Control.Exception as X
import Network.HTTP.Client(parseUrlThrow)
import qualified Data.ByteString.Lazy as BL
import Data.Int(Int64)
@ -11,6 +18,22 @@ import HttpDownload
import FileUtils
import Common
type HackageSnapshotData = SnapshotData
-- This is the data that is extracted from the path to cabal file
-- Like, when program parses "safeio/0.0.2.0/safeio.cabal"
-- It gets the version 0.0.2.0 and safeio package name. Also checks, xxx and yyy match in
-- "xxx/version/yyy.cabal
instance FromJSON SnapshotData where
parseJSON = withObject "snapshot" $ \o -> do
signedO <- o .: "signed"
metaO <- signedO .: "meta"
tarO <- metaO .: "<repo>/01-index.tar.gz"
hashesO <- tarO .: "hashes"
md5str <- hashesO .: "md5"
len <- tarO .: "length"
return (SnapshotData md5str len)
-- The action, that is needed to perform to correctly update the downloaded
-- archive. ArchiveIsOk - everything is fine.
-- Update - need to add some information to the end of the file
@ -39,6 +62,18 @@ calcUpdateResultIO file json = do
return (calcUpdateResult snapshot fileData, snapshot, fileData)
-- The method, that raises an exception, if it was not able to parse the
-- snapshot from JSON
parseSnapshotJSONThrow :: BL.ByteString -> IO HackageSnapshotData
parseSnapshotJSONThrow body = case A.decode body of
(Just snapshot) -> return snapshot
Nothing -> X.throwIO $ UAE "Could not decode JSON"
-- Returns the snapshot of archive from the hackage
fetchSnapshot :: URL -> IO HackageSnapshotData
fetchSnapshot url = parseUrlThrow url >>= fetchResponseData >>= parseSnapshotJSONThrow
-- performs the update, returns True if the the archive was modified
performArchiveFileUpdate :: URL -> URL -> FilePath -> IO UpdateResult
performArchiveFileUpdate snapshotURL archiveURL archive = do
@ -46,13 +81,13 @@ performArchiveFileUpdate snapshotURL archiveURL archive = do
(status, snapshot, _) <- calcUpdateResultIO archive snapshotURL
case status of
ArchiveIsOk -> (putStrLn $ "Archive is up to date") >> return ArchiveIsOk
ArchiveIsOk -> putStrLn "Archive is up to date" >> return ArchiveIsOk
_ -> cutUpdate modifFunctions
where
performUpdate = updateArchive archive archiveURL
modifFunctions = [return (), cutting 50000, cutting 500000, cutting 5000000, removing]
cutting val = do
putStrLn $ "\tCutting " ++ (show val) ++ " from " ++ archive
putStrLn $ "\tCutting " ++ show val ++ " from " ++ archive
truncateIfExists archive val
removing = do
putStrLn $ "\tRemoving " ++ archive
@ -65,8 +100,8 @@ performArchiveFileUpdate snapshotURL archiveURL archive = do
ArchiveIsOk -> return ArchiveIsOk
Corrupted -> cutUpdate mfs
Update range -> do
putStrLn $ "\tSnapshot from " ++ snapshotURL ++ " " ++ (show snapshot)
putStrLn $ "\tUpdate range " ++ (show range)
putStrLn $ "\tSnapshot from " ++ snapshotURL ++ " " ++ show snapshot
putStrLn $ "\tUpdate range " ++ show range
result <- performUpdate snapshot range
if result then return status
else cutUpdate mfs
@ -82,9 +117,9 @@ updateArchive archive archiveURL snapshot range = do
write2File :: FilePath -> URL -> Range -> IO()
write2File archive url range = do
putStrLn $ "\tGetting range " ++ (show range) ++ " from " ++ url
putStrLn $ "\tGetting range " ++ show range ++ " from " ++ url
body <- fetchRangeData url range
putStrLn $ "\tGot range " ++ (show (BL.take 50 body))
putStrLn $ "\tGot range " ++ show (BL.take 50 body)
BL.appendFile archive body
putStrLn "Append ok"

View File

@ -1,43 +1,20 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
module HttpDownload(HackageSnapshotData,
UpdateArchiveException(..),
fetchSnapshot,
module HttpDownload(
fetchResponseData,
fetchRangeData,
Range,
cropRanges
) where
import Data.Aeson.Types
cropRanges) where
import Data.Int(Int64)
import qualified Data.Aeson as A
import qualified Data.Aeson.Parser as AP
import qualified Data.Text as T
import qualified Data.ByteString.Lazy as BL
import Network.HTTP.Client(Request(..), parseUrlThrow, newManager, responseBody, httpLbs)
import Network.HTTP.Client.TLS (tlsManagerSettings)
import Network.HTTP.Types.Header
import qualified Control.Exception as X
import Common
type HackageSnapshotData = SnapshotData
-- This is the data that is extracted from the path to cabal file
-- Like, when program parses "safeio/0.0.2.0/safeio.cabal"
-- It gets the version 0.0.2.0 and safeio package name. Also checks, xxx and yyy match in
-- "xxx/version/yyy.cabal
instance FromJSON SnapshotData where
parseJSON = withObject "snapshot" $ \o -> do
signedO <- o .: "signed"
metaO <- signedO .: "meta"
tarO <- metaO .: "<repo>/01-index.tar.gz"
hashesO <- tarO .: "hashes"
md5str <- hashesO .: "md5"
len <- tarO .: "length"
return (SnapshotData md5str len)
-- The range, from which to download
type Range = (Int64, Int64)
@ -62,21 +39,9 @@ makeRangeRequest (from, to) = makeRange
-- Returns the data from response, returned to the request
fetchResponseData :: Request -> IO BL.ByteString
fetchResponseData req = newManager tlsManagerSettings >>= httpLbs req >>= return.responseBody
fetchResponseData req = responseBody <$> (newManager tlsManagerSettings >>= httpLbs req)
-- Returns the bytes from the range request
fetchRangeData :: URL -> Range -> IO BL.ByteString
fetchRangeData url range = createRangeRequest url range >>= fetchResponseData
-- The method, that raises an exception, if it was not able to parse the
-- snapshot from JSON
parseSnapshotJSONThrow :: BL.ByteString -> IO HackageSnapshotData
parseSnapshotJSONThrow body = case A.decode body of
(Just snapshot) -> return snapshot
Nothing -> X.throwIO $ UAE "Could not decode JSON"
-- Returns the snapshot of archive from the hackage
fetchSnapshot :: URL -> IO HackageSnapshotData
fetchSnapshot url = parseUrlThrow url >>= fetchResponseData >>= parseSnapshotJSONThrow

View File

@ -10,16 +10,15 @@ module REPL ( processCycle,
import qualified Data.Map.Strict as M
import qualified Data.Char as DC
import qualified Data.List as DL
import qualified Control.Exception as X
import Control.Monad(forever)
import Control.Monad(forever, void)
import System.IO (stdout, hFlush)
import Data.Default
import Data.Int(Int64)
import System.Exit(exitSuccess)
import System.Directory(copyFile)
import System.FilePath((</>))
import Data.List(isPrefixOf)
import Common
import HackageArchive
@ -28,71 +27,18 @@ import FileUtils
import HttpDownload
import Storage
-- the constructor short name is really awkward in russian
data HackageUpdateInfo = IUH {
iuhUpdateDir :: FilePath,
iuhSnapshotURL :: URL,
iuhArchiveURL :: URL
} deriving (Eq, Show)
instance Default HackageUpdateInfo where
def = defaultIUH
defaultIUH :: HackageUpdateInfo
defaultIUH = IUH {
iuhUpdateDir = "hackagefiles",
iuhSnapshotURL = "https://hackage.haskell.org/snapshot.json",
iuhArchiveURL = "https://hackage.haskell.org/01-index.tar.gz"
}
getArchive :: HackageUpdateInfo -> FilePath
getArchive iuh = (iuhUpdateDir iuh) </> archive
getArchiveClone :: HackageUpdateInfo -> FilePath
getArchiveClone iuh = (iuhUpdateDir iuh) </> archiveClone
getTar :: HackageUpdateInfo -> FilePath
getTar iuh = (iuhUpdateDir iuh) </> tar
getTarClone :: HackageUpdateInfo -> FilePath
getTarClone iuh = (iuhUpdateDir iuh) </> tarClone
archive :: FilePath
archive = "01-index.tar.gz"
archiveClone :: FilePath
archiveClone = "01-index.tar.gz.orig"
tar :: FilePath
tar = "01-index.tar"
tarClone :: FilePath
tarClone = "01-index.orig.tar"
parseIntEnd :: (Num a, Read a) => String -> a
parseIntEnd val | DL.length l > 0 = read (DL.last l)
| otherwise = 0
where l = words val
parseValEnd :: String -> String
parseValEnd val | DL.length l > 1 = DL.last l
| otherwise = ""
where l = words val
processCycle :: HackageUpdateInfo -> IO ()
processCycle iuh = forever $ do
putStr "Input command: "
hFlush stdout
command <- getLine
hFlush stdout
(processCommand command) `X.catch` eh `X.catch` eh2 `X.catch` eh3
processCommand command `X.catch` eh `X.catch` eh2 `X.catch` eh3
where
processCommand = buildCommand iuh
eh (e :: X.IOException) = putStrLn $ "IO Error: " ++ (show e)
eh2 (e :: UpdateArchiveException) = putStrLn $ "Parsing error: " ++ (show e)
eh3 (e :: X.ErrorCall) = putStrLn $ "Error call: " ++ (show e)
eh (e :: X.IOException) = putStrLn $ "IO Error: " ++ show e
eh2 (e :: UpdateArchiveException) = putStrLn $ "Parsing error: " ++ show e
eh3 (e :: X.ErrorCall) = putStrLn $ "Error call: " ++ show e
buildCommand :: HackageUpdateInfo -> (String -> IO())
buildCommand iuh = processCommand
@ -124,11 +70,11 @@ buildCommand iuh = processCommand
| chk "tarparse" = showMap trFile 50 -- loads the tar information in the memory
| chk "compare" = showArchiveCompare arch archC
| chk "update" = performArchiveFileUpdate snapURL archURL arch >> return ()
| chk "update" = void $ performArchiveFileUpdate snapURL archURL arch
| chk "acidcompare" = printAcidCompare ud trFile
| chk "acidupdate" = acidUpdate ud trFile
| chk "acidquery" = printAcidQuery ud (parseValEnd command)
| chk "acidquery" = showAcidQuery ud (parseValEnd command)
| chk "tarcmp" = showDiffMap trFile trFileC
| chk "exit" = exitREPL
@ -137,7 +83,7 @@ buildCommand iuh = processCommand
| otherwise = showHelp iuh
where pc = map DC.toLower command
chk val = DL.isPrefixOf val pc
chk val = val `isPrefixOf` pc
arch = getArchive iuh
archC = getArchiveClone iuh
@ -147,22 +93,58 @@ buildCommand iuh = processCommand
trFileC = getTarClone iuh
ud = iuhUpdateDir iuh
showHelp :: HackageUpdateInfo -> IO()
showHelp iuh = do
putStrLn "Available commands: "
putStrLn $ "check - downloads the json length and md5 hash from " ++ snapURL ++
", and compares it with local " ++ arch
putStrLn $ "checkclone - same for " ++ archC
putStrLn $ "file - displays the current " ++ arch ++ " length and md5 hash"
putStrLn $ "fileclone - same for " ++ archC ++ " file"
putStrLn $ "copyorig - copy the " ++ arch ++ " to " ++ archC
putStrLn $ "cut size - cuts the size bytes from the end of the " ++ arch ++ " , for update command"
putStrLn "cutclone size - cuts the size bytes from the end of the 01-index.tar.gz, for update command"
putStrLn $ "unzip - unzips the " ++ arch ++ " in the " ++ trFile ++ " file"
putStrLn $ "unzipclone - unzips the " ++ archC ++ " in the " ++ trFileC ++ " file"
putStrLn $ "clean - deletes the " ++ arch ++ " and " ++ trFile
putStrLn $ "cleanclone - deletes the " ++ archC ++ " and " ++ trFileC
putStrLn $ "compare - compares the " ++ arch ++ " with " ++ archC
putStrLn $ "tarparse - loads the map of entries from " ++ trFile ++ " and displays it"
putStrLn $ "tarparseclone - same for " ++ trFileC
putStrLn $ "tarparsepre - loads the premap of entries from " ++ trFile ++ " and displays it"
putStrLn $ "tarparsepreclone - same for " ++ trFileC
putStrLn $ "tarcmp - compares the entries of " ++ trFile ++ " and " ++ trFileC
putStrLn $ "update - updates the current " ++ arch ++ " from " ++ archURL
putStrLn $ "acidcompare - compares the state of " ++ trFile ++ " with map from acid state"
putStrLn $ "acidupdate - updates the acid state with " ++ trFile
putStrLn "acidquery name - queries the acid with package"
putStrLn "exit - exits this repl"
where
arch = getArchive iuh
archC = getArchiveClone iuh
archURL = iuhArchiveURL iuh
snapURL = iuhSnapshotURL iuh
trFile = getTar iuh
trFileC = getTarClone iuh
-- Displays the snapshot of the file
showFileSnapshot :: FilePath -> IO()
showFileSnapshot file = do
filesnapshot <- calcFileData file
putStrLn $ "File result for " ++ file
putStrLn $ "\tFile snapshot: " ++ (show filesnapshot)
putStrLn $ "\tFile snapshot: " ++ show filesnapshot
-- Shows the update data for the archive on disk
showUpdateData :: FilePath -> URL -> IO()
showUpdateData file json = do
(range, snapshot, filesnapshot) <- calcUpdateResultIO file json
putStrLn $ "Update result for file " ++ file
putStrLn $ "\tHackage snapshot: " ++ (show snapshot)
putStrLn $ "\tFile snapshot: " ++ (show filesnapshot)
putStrLn $ "\tRange to update: " ++ (show range)
putStrLn $ "\tHackage snapshot: " ++ show snapshot
putStrLn $ "\tFile snapshot: " ++ show filesnapshot
putStrLn $ "\tRange to update: " ++ show range
-- shows the substring of specified length from file from offset
showFileSubstring :: FilePath -> Int64 -> Int64 -> IO ()
@ -178,15 +160,15 @@ copyArchive archive1 archive2 = do
copyFile archive1 archive2
putStrLn $ "Copied the " ++ archive1 ++ " to " ++ archive2
showMap :: FilePath -> Int -> IO()
showMap :: FilePath -> Int -> IO ()
showMap path count = do
putStrLn $ "Displaying " ++ (show count) ++ " entries for " ++ path
putStrLn $ "Displaying " ++ show count ++ " entries for " ++ path
tar <- loadTar path
mapM_ (print.snd) $ take count $ M.toList $ buildHackageMap tar (buildPreHackageMap tar)
showPreMap :: FilePath -> Int -> IO()
showPreMap :: FilePath -> Int -> IO ()
showPreMap path count = do
putStrLn $ "Pre displaying " ++ (show count) ++ " entries for " ++ path
putStrLn $ "Pre displaying " ++ show count ++ " entries for " ++ path
tar <- loadTar path
mapM_ print $ take count $ {-filter ((elem '-').fst) $-} M.toList $ buildPreHackageMap tar
@ -201,46 +183,21 @@ showDiffMap newTarFile oldTarFile = do
let diffMap = buildDifferenceMap oldMap newMap
mapM_ (print.snd) $ M.toList diffMap
showHelp :: HackageUpdateInfo -> IO()
showHelp iuh = do
putStrLn "Available commands: "
putStrLn $ "check - downloads the json length and md5 hash from " ++ snapURL ++
", and compares it with local " ++ arch
putStrLn $ "checkclone - same for " ++ archC
putStrLn $ "file - displays the current " ++ arch ++ " length and md5 hash"
putStrLn $ "fileclone - same for " ++ archC ++ " file"
putStrLn $ "copyorig - copy the " ++ arch ++ " to " ++ archC
putStrLn $ "cut size - cuts the size bytes from the end of the " ++ arch ++ " , for update command"
putStrLn $ "cutclone size - cuts the size bytes from the end of the 01-index.tar.gz, for update command"
putStrLn $ "unzip - unzips the " ++ arch ++ " in the " ++ trFile ++ " file"
putStrLn $ "unzipclone - unzips the " ++ archC ++ " in the " ++ trFileC ++ " file"
putStrLn $ "clean - deletes the " ++ arch ++ " and " ++ trFile
putStrLn $ "cleanclone - deletes the " ++ archC ++ " and " ++ trFileC
putStrLn $ "compare - compares the " ++ arch ++ " with " ++ archC
putStrLn $ "tarparse - loads the map of entries from " ++ trFile ++ " and displays it"
putStrLn $ "tarparseclone - same for " ++ trFileC
putStrLn $ "tarparsepre - loads the premap of entries from " ++ trFile ++ " and displays it"
putStrLn $ "tarparsepreclone - same for " ++ trFileC
putStrLn $ "tarcmp - compares the entries of " ++ trFile ++ " and " ++ trFileC
putStrLn $ "update - updates the current " ++ arch ++ " from " ++ archURL
putStrLn $ "acidcompare - compares the state of " ++ trFile ++ " with map from acid state"
putStrLn $ "acidupdate - updates the acid state with " ++ trFile
putStrLn $ "acidquery name - queries the acid with package"
putStrLn "exit - exits this repl"
where
arch = getArchive iuh
archC = getArchiveClone iuh
archURL = iuhArchiveURL iuh
snapURL = iuhSnapshotURL iuh
trFile = getTar iuh
trFileC = getTarClone iuh
showArchiveCompare :: FilePath -> FilePath -> IO()
showArchiveCompare archive1 archive2= do
val <- compareFiles archive1 archive2
putStrLn $ "Compare result " ++ archive1 ++ " " ++ archive2 ++ " " ++ (show val)
putStrLn $ "Compare result " ++ archive1 ++ " " ++ archive2 ++ " " ++ show val
showAcidQuery :: FilePath -> HackageName -> IO()
showAcidQuery updateDir name = do
putStrLn $ "Querying acid with " ++ name
value <- queryAcidMap updateDir name
case value of
Just package -> do
putStrLn "Found"
print package
Nothing -> putStrLn "Not found"
exitREPL :: IO()
exitREPL = putStrLn "Finished working with hackage REPL" >> exitSuccess
@ -250,7 +207,7 @@ exitREPL = putStrLn "Finished working with hackage REPL" >> exitSuccess
cutFile :: FilePath -> Int64 -> IO()
cutFile path size = do
truncateIfExists path size
putStrLn $ "Cut " ++ (show size) ++ " bytes from " ++ path
putStrLn $ "Cut " ++ show size ++ " bytes from " ++ path
unzipArchive :: FilePath -> FilePath -> IO()
unzipArchive archive tar = do
@ -263,7 +220,6 @@ removeArchiveFiles archive tar = do
removeIfExists archive
removeIfExists tar
printAcidCompare :: FilePath -> FilePath -> IO()
printAcidCompare updateDir tarFile = do
newTar <- loadTar tarFile
@ -276,18 +232,8 @@ acidUpdate updateDir tarFile = do
let newMap = buildHackageMap newTar (buildPreHackageMap newTar)
updateAcidMap updateDir newMap
printAcidQuery :: FilePath -> HackageName -> IO()
printAcidQuery updateDir name = do
putStrLn $ "Querying acid with " ++ name
value <- queryAcidMap updateDir name
case value of
Just package -> do
putStrLn "Found"
print package
Nothing -> putStrLn "Not found"
updateArchive :: HackageUpdateInfo -> IO()
updateArchive iuh = performArchiveFileUpdate snapURL archURL arch >> return ()
updateArchive iuh = void (performArchiveFileUpdate snapURL archURL arch)
where
arch = getArchive iuh
archURL = iuhArchiveURL iuh

View File

@ -1,20 +1,78 @@
module Stackage() where
{-# LANGUAGE OverloadedStrings #-}
module Stackage(
parseLTSLine,
parsePackageLine) where
import qualified Data.Map as M
import Text.Megaparsec
import Text.Megaparsec.Text
import qualified Text.Megaparsec.Lexer as L
import qualified Data.Version as DV
import qualified Data.Map as M
import qualified Text.ParserCombinators.ReadP as RP
import Control.Monad (void)
import Control.Applicative(empty)
import Common
type ConstraintMap = M.Map PackageName PackageData
type ShortSnapshotName = String
type LongSnapshotName = String
type StackageSnapshot = (ShortSnapshotName, LongSnapshotName)
-- the minor and major versions for stackage
type StackageLTS = (Int, Int)
shortName :: StackageSnapshot -> String
shortName = fst
data LTS = LTS {
name :: String,
url :: URL,
constraints :: ConstraintMap
}
longName :: StackageSnapshot -> String
longName = snd
type StackageLTS = (LongSnapshotName, [PackageData])
parseStackageLTS :: Parser StackageLTS
parseStackageLTS = do
many (try (manyTill anyChar eol >> notFollowedBy parseLTSLine))
ltsName <- parseLTSLine
--packages =
pure (ltsName, [])
parseLTSLine :: Parser LongSnapshotName
parseLTSLine = do
-- destroy everything
manyTill anyChar (string "http://www.stackage.org/snapshot/")
name <- some (letterChar <|> digitChar <|> char '.' <|> char '-')
space
void eol <|> eof
pure name
parsePackageLine :: Parser PackageData
parsePackageLine = do
packageData <- try parsePackageConst <|> parsePackageEmpty
space
manyTill anyChar (void eol <|> eof)
pure packageData
parsePackageConst :: Parser PackageData
parsePackageConst = do
manyTill anyChar (char ':') -- chop the 'constraints:' in the beginning
parsePackageEmpty
parsePackageEmpty :: Parser PackageData
parsePackageEmpty = do
space
parsePackageData
parsePackageData :: Parser PackageData
parsePackageData = do
name <- some (letterChar <|> digitChar <|> char '-')
space
string "=="
space
version <- parseVersion
many (char ',')
pure (name, version)
parseVersion :: Parser DV.Version
parseVersion = do
numbers <- sepBy1 L.integer (char '.')
pure $ DV.Version (map fromIntegral numbers) []

View File

@ -20,8 +20,7 @@ import qualified Control.Monad.State as State
import HackageArchive
import qualified Data.Version as DV
data KeyValue = KeyValue !HackageMap
deriving (Typeable)
newtype KeyValue = KeyValue HackageMap deriving (Typeable)
$(deriveSafeCopy 0 'base ''DV.Version)
$(deriveSafeCopy 0 'base ''HackagePackage)
@ -53,7 +52,7 @@ printAcidDiffMap path newMap = do
acid <- openLocalStateFrom path (KeyValue M.empty)
do
diffMap <- query acid (CompareMap newMap)
putStrLn $ "Printing difference map with acid-state"
putStrLn "Printing difference map with acid-state"
mapM_ (print.snd) $ M.toList diffMap
closeAcidState acid
@ -61,7 +60,7 @@ updateAcidMap :: FilePath -> HackageMap -> IO ()
updateAcidMap path newMap = do
acid <- openLocalStateFrom path (KeyValue M.empty)
do
putStrLn $ "Updating the acid map"
putStrLn "Updating the acid map"
update acid (UpdateMap newMap)
closeAcidState acid

View File

@ -1,39 +1,80 @@
{-# LANGUAGE OverloadedStrings #-}
import Test.Tasty
import Test.Tasty.HUnit
import Data.Ord
import Text.Megaparsec
import Text.Megaparsec.Text
import Data.Either (isRight, either)
import qualified Data.Text as T
import Data.Monoid ((<>))
import HackageArchive
import Stackage
import TarUtil as TU
import qualified Data.Text.IO as TIO
parseTests = testGroup "Different parsing tests"
testWorkingDir :: String
testWorkingDir = "testworkdir"
parseTests = testGroup "Hackage archive parsing tests"
[
-- testCase "'-' chat in package name parsing" $
-- (fst <$> parsePath "file-collection/0.1.1.9/file-collection.cabal")
-- `compare` (Just "file-collection") @?= EQ,
testPath "filecollection/0.1.1.9/filecollection.cabal" "filecollection" True
, testPath "filecollection/0.1.1.9/filecollection.cabal" "filecollectionz" False
, testPath "file-collection/0.1.1.9/file-collection.cabal" "file-collection" True
, testPath "file-collection/0.1.1.9/file-collection.cabal" "filecollection" False
]
testCase "Package name parsing" $
(fst <$> parsePath "filecollection/0.1.1.9/filecollection.cabal") == (Just "filecollection") @?= True
, testCase "Package name parsing" $
(fst <$> parsePath "filecollection/0.1.1.9/filecollection.cabal") == (Just "filecollectionz") @?= False
, testCase "Package name parsing" $
(fst <$> parsePath "file-collection/0.1.1.9/file-collection.cabal") == (Just "file-collection") @?= True
testPath :: T.Text -> T.Text -> Bool -> TestTree
testPath text val match = testCase (T.unpack ("Parsing " <> expect match <> " \'" <> text <> "\'")) $
assertBool "Failed" $ ((fst <$> parsePath (T.unpack text)) == Just (T.unpack val)) == match
, testCase "Package name parsing" $
(fst <$> parsePath "file-collection/0.1.1.9/file-collection.cabal") == (Just "filecollection") @?= False
parseStackageTests = testGroup "Stackage parsing tests"
[
testParse parsePackageLine "constraints: abstract-deque ==0.3," True
, testParse parsePackageLine "constraints: abstract-deque ==0.3" True
, testParse parsePackageLine "constraints: abstract-deque ==0." False
, testParse parsePackageLine "constraints: abstract-deque ==" False
, testParse parsePackageLine "constraints: abst3453#$%#ract-deque ==0.3" False
, testParse parsePackageLine "constraints: abstract-deque ==0.3," True
, testParse parsePackageLine " ztail ==1.2" True
, testParse parsePackageLine " adjunctions ==4.3," True
, testParse parsePackageLine "ztail ==1.2" True
, testParse parsePackageLine "adjunctions ==4.3," True
, testParse parseLTSLine "-- Stackage snapshot from: http://www.stackage.org/snapshot/lts-2.10" True
, testParse parseLTSLine "-- Stackage snapshot from: http://www.stackage.org/snapshot/lts-2.$10" False
, testParse parseLTSLine "-- Please place this file next to your .cabal file as cabal.config" False
, testParse parseLTSLine "-- To only use tested packages, uncomment the following line:" False
, testParse parseLTSLine "-- remote-repo: stackage-lts-2.10:http://www.stackage.org/lts-2.10" False
, testParse parseLTSLine "constraints: abstract-deque ==0.3," False
, testParse parseLTSLine "abstract-par ==0.3.3," False
, testParse parseLTSLine "zlib-lens ==0.1.2" False
, testParse parseLTSLine "-- Stackage snapshot from: http://www.stackage.org/snapshot/nightly-2017-06-10" True
]
{-
[ testCase "List comparison (different length)" $
[1, 2, 3] `compare` [1,2] @?= GT
-- the following test does not hold
, testCase "List comparison (same length)" $
[1, 2, 3] `compare` [1,2,2] @?= LT
parseCabalConfig = (testWorkDir </> "sometestfile.cnf") testGroup "Cabal config parser tests"
[
testFileParse parseStackageLTS
]
-}
expect :: Bool -> T.Text
expect True = "expect success"
expect False = "expect fail"
testParse :: Parser a -> T.Text -> Bool -> TestTree
testParse p text match = testCase (T.unpack ("Parsing " <> expect match <> " \'" <> text <> "\'")) $
assertBool "Failed" (isRight (runParser p "" text) == match)
testFileParse :: FilePath -> Parser a -> (T.Text -> b) -> (b -> a -> Bool) -> TestTree
testFileParse file p textFunc matchFunc =
testCase ("Testing file: " ++ file) $ do
fileText <- TIO.readFile file -- got the file
let textVal = textFunc fileText
let eVal = matchFunc textVal <$> runParser p "" fileText
assertBool "Failed" (either (const False) id eVal)
tests :: TestTree
tests = testGroup "REPL tests" [parseTests]
tests = testGroup "REPL tests" [parseStackageTests, parseTests]
main = defaultMain parseTests
main = defaultMain tests