svndump 0.2.0 → 0.3.0
raw patch · 5 files changed
+128/−116 lines, 5 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Subversion.Dump: Entry :: FieldMap -> FieldMap -> ByteString -> Entry
- Subversion.Dump: data Entry
- Subversion.Dump: entryBody :: Entry -> ByteString
- Subversion.Dump: entryProps :: Entry -> FieldMap
- Subversion.Dump: entryTags :: Entry -> FieldMap
- Subversion.Dump: instance Show Entry
- Subversion.Dump: parseEntry :: Parser Entry
- Subversion.Dump: parseHeader :: Parser ByteString
- Subversion.Dump: readSvnDumpRaw :: ByteString -> Result [Entry]
- Subversion.Dump: type FieldMap = [(ByteString, ByteString)]
+ Subversion.Dump.Raw: Entry :: FieldMap -> FieldMap -> ByteString -> Entry
+ Subversion.Dump.Raw: data Entry
+ Subversion.Dump.Raw: entryBody :: Entry -> ByteString
+ Subversion.Dump.Raw: entryProps :: Entry -> FieldMap
+ Subversion.Dump.Raw: entryTags :: Entry -> FieldMap
+ Subversion.Dump.Raw: instance Show Entry
+ Subversion.Dump.Raw: readInt :: ByteString -> Int
+ Subversion.Dump.Raw: readSvnDumpRaw :: ByteString -> [Entry]
+ Subversion.Dump.Raw: type FieldMap = [(ByteString, ByteString)]
- Subversion.Dump: readSvnDump :: ByteString -> Either String [Revision]
+ Subversion.Dump: readSvnDump :: ByteString -> [Revision]
Files
- src/Subversion/Dump.hs +5/−97
- src/Subversion/Dump/Raw.hs +110/−0
- svndump.cabal +2/−1
- test/test-cooked.hs +5/−8
- test/test-raw.hs +6/−10
src/Subversion/Dump.hs view
@@ -8,21 +8,10 @@ , OpAction(..) , Operation(..) - , FieldMap- , Entry(..)- , readSvnDump- , readSvnDumpRaw-- , parseHeader- , parseEntry ) where import Control.Applicative hiding (many)-import Control.Monad-import qualified Data.Attoparsec.Char8 as AC-import Data.Attoparsec.Combinator-import Data.Attoparsec.Lazy as AL import Data.ByteString as B hiding (map) import qualified Data.ByteString.Char8 as BC hiding (map) import qualified Data.ByteString.Lazy as BL hiding (map)@@ -30,9 +19,9 @@ import Data.Text (Text) import qualified Data.Text.Encoding as E import Data.Maybe-import Data.Word (Word8)-import System.FilePath +import Subversion.Dump.Raw+ import Prelude hiding (getContents) default (ByteString)@@ -99,12 +88,8 @@ -- | Reads a dump file from a ByteString in the IO monad into a list of -- Revision values. This is the "cooked" parallel of 'readSvnDumpRaw'. -readSvnDump :: BL.ByteString -> Either String [Revision]-readSvnDump io = do- case readSvnDumpRaw io of- Fail _ _ y -> Left y- Done _ result -> Right $ map processRevs (L.groupBy sameRev result)-+readSvnDump :: BL.ByteString -> [Revision]+readSvnDump dump = map processRevs $ L.groupBy sameRev $ readSvnDumpRaw dump where sameRev _ y = isNothing $ L.lookup "Revision-number" (entryTags y) getField f n x = L.lookup n (f x)@@ -147,85 +132,8 @@ "change" -> Change "replace" -> Replace _ -> error "Unexpected"--type FieldMap = [(ByteString, ByteString)] -data Entry = Entry { entryTags :: FieldMap- , entryProps :: FieldMap- , entryBody :: ByteString }- deriving Show--readSvnDumpRaw :: BL.ByteString -> Result [Entry]-readSvnDumpRaw dump = parse parseSvnDump dump---- These are the Parsec parsers for the various parts of the input file.--space :: Parser Word8-space = satisfy (== 32)--newline :: Parser Word8-newline = satisfy (== 10)--parseTag :: Parser (ByteString, ByteString)-parseTag =- (,) <$> takeWhile1 fieldChar <* string ": " -- :- <*> takeWhile1 (/= 10) <* newline- where fieldChar w = (w >= 65 && w <= 90) -- A-Z- || (w >= 97 && w <= 121) -- a-z- || (w >= 48 && w <= 57) -- 0-9- || w == 45 -- -- || w == 95 -- _--parseIndicator :: Parser (Word8, Int)-parseIndicator = (,) <$> satisfy (oneOf 75 86) <* space -- K or V- <*> AC.decimal <* newline- where oneOf x y w = w == x || w == y--parseSpecValue :: Parser ByteString-parseSpecValue = do- (_, len) <- parseIndicator- AL.take len <* newline--parseProperty :: Parser (ByteString, ByteString)-parseProperty = (,) <$> parseSpecValue -- K- <*> parseSpecValue -- V--readInt :: ByteString -> Int-readInt bs = B.foldl' addup 0 bs- where addup acc x = acc * 10 + (fromIntegral x - 48) -- '0'--parseEntry :: Parser Entry-parseEntry = do- fields <- many1 parseTag <* newline-- props <- case L.lookup "Prop-content-length" fields of- Nothing -> return []- Just _ -> manyTill parseProperty (try (string "PROPS-END\n"))-- body <- case L.lookup "Text-content-length" fields of- Nothing -> return B.empty- Just len -> AL.take (readInt len)-- _ <- AL.takeWhile (== 10)-- return Entry { entryTags = fields- , entryProps = props- , entryBody = body }--parseHeader :: Parser ByteString-parseHeader = do- _ <- string "SVN-fs-dump-format-version: 2\n\n"- <?> "Dump file starts without a recognizable tag"- string "UUID: " *> takeWhile1 uuidMember- <* newline <* newline- where- -- Accept any hexadecimal character, or '-'- uuidMember w = w == 45 || (w >= 48 && w <= 57) || (w >= 97 && w <= 102)--parseSvnDump :: Parser [Entry]-parseSvnDump = parseHeader >> many1 parseEntry- parseDate :: ByteString -> RevDate parseDate = E.decodeUtf8 --- SvnDump.hs ends here+-- Dump.hs ends here
+ src/Subversion/Dump/Raw.hs view
@@ -0,0 +1,110 @@+{-# LANGUAGE OverloadedStrings #-}++module Subversion.Dump.Raw+ ( FieldMap+ , Entry(..)++ , readInt+ , readSvnDumpRaw+ ) where++import Control.Applicative hiding (many)+import Control.Monad+import qualified Data.Attoparsec.Char8 as AC+import Data.Attoparsec.Combinator+import Data.Attoparsec.Lazy as AL+import Data.ByteString as B hiding (map)+import qualified Data.ByteString.Lazy as BL hiding (map)+import qualified Data.List as L+import Data.Maybe+import Data.Word (Word8)++import Prelude hiding (getContents)++default (ByteString)++type FieldMap = [(ByteString, ByteString)]++data Entry = Entry { entryTags :: FieldMap+ , entryProps :: FieldMap+ , entryBody :: ByteString }+ deriving Show++readSvnDumpRaw :: BL.ByteString -> [Entry]+readSvnDumpRaw dump =+ case parse parseHeader dump of+ Fail _ _ _ -> error "Stream is not a Subversion dump file"+ Done contents _ -> parseDumpFile contents++parseDumpFile :: BL.ByteString -> [Entry]+parseDumpFile contents = do+ case parse parseEntry contents of+ Fail _ _ _ -> []+ Done contents' entry -> do+ entry : parseDumpFile contents'++-- These are the Parsec parsers for the various parts of the input file.++space :: Parser Word8+space = satisfy (== 32)++newline :: Parser Word8+newline = satisfy (== 10)++parseTag :: Parser (ByteString, ByteString)+parseTag =+ (,) <$> takeWhile1 fieldChar <* string ": " -- :+ <*> takeWhile1 (/= 10) <* newline+ where fieldChar w = (w >= 65 && w <= 90) -- A-Z+ || (w >= 97 && w <= 121) -- a-z+ || (w >= 48 && w <= 57) -- 0-9+ || w == 45 -- -+ || w == 95 -- _++parseIndicator :: Parser (Word8, Int)+parseIndicator = (,) <$> satisfy (oneOf 75 86) <* space -- K or V+ <*> AC.decimal <* newline+ where oneOf x y w = w == x || w == y++parseSpecValue :: Parser ByteString+parseSpecValue = do+ (_, len) <- parseIndicator+ AL.take len <* newline++parseProperty :: Parser (ByteString, ByteString)+parseProperty = (,) <$> parseSpecValue -- K+ <*> parseSpecValue -- V++readInt :: ByteString -> Int+readInt bs = B.foldl' addup 0 bs+ where addup acc x = acc * 10 + (fromIntegral x - 48) -- '0'++parseEntry :: Parser Entry+parseEntry = do+ fields <- many1 parseTag <* newline++ props <- case L.lookup "Prop-content-length" fields of+ Nothing -> return []+ Just _ -> manyTill parseProperty (try (string "PROPS-END\n"))++ body <- case L.lookup "Text-content-length" fields of+ Nothing -> return B.empty+ Just len -> AL.take (readInt len)++ _ <- AL.takeWhile (== 10)++ return Entry { entryTags = fields+ , entryProps = props+ , entryBody = body }++parseHeader :: Parser ByteString+parseHeader = do+ _ <- string "SVN-fs-dump-format-version: 2\n\n"+ <?> "Dump file starts without a recognizable tag"+ string "UUID: " *> takeWhile1 uuidMember+ <* newline <* newline+ where+ -- Accept any hexadecimal character, or '-'+ uuidMember w = w == 45 || (w >= 48 && w <= 57) || (w >= 97 && w <= 102)++-- SvnDump.hs ends here
svndump.cabal view
@@ -1,6 +1,6 @@ name: svndump category: Subversion-version: 0.2.0+version: 0.3.0 license: BSD3 cabal-version: >= 1.8 license-file: LICENSE@@ -40,6 +40,7 @@ exposed-modules: Subversion Subversion.Dump+ Subversion.Dump.Raw ghc-options: -Wall -fwarn-tabs -O2 hs-source-dirs: src
test/test-cooked.hs view
@@ -8,13 +8,10 @@ main :: IO () main = do file <- B.readFile "data/cunit.dump.gz"- case readSvnDump (GZip.decompress file) of- Left _ -> exitFailure- Right xs -> do- let len = length xs- putStrLn $ show len ++ " cooked entries found, expecting 157"- if len == 157- then exitSuccess- else exitFailure+ let len = length $ readSvnDump (GZip.decompress file)+ putStrLn $ show len ++ " cooked entries found, expecting 157"+ if len == 157+ then exitSuccess+ else exitFailure -- test-cooked.hs ends here
test/test-raw.hs view
@@ -1,21 +1,17 @@ module Main where import qualified Codec.Compression.GZip as GZip-import Data.Attoparsec.Lazy import qualified Data.ByteString.Lazy as B-import Subversion.Dump+import Subversion.Dump.Raw import System.Exit main :: IO () main = do file <- B.readFile "data/cunit.dump.gz"- case readSvnDumpRaw (GZip.decompress file) of- Fail _ _ _ -> exitFailure- Done _ xs -> do- let len = length xs- putStrLn $ show len ++ " raw entries found, expecting 1950"- if len == 1950- then exitSuccess- else exitFailure+ let len = length $ readSvnDumpRaw (GZip.decompress file)+ putStrLn $ show len ++ " raw entries found, expecting 1950"+ if len == 1950+ then exitSuccess+ else exitFailure -- test-raw.hs ends here