packages feed

svndump 0.1.1 → 0.2.0

raw patch · 4 files changed

+90/−81 lines, 4 filesdep +attoparsecdep +containersdep −parsec

Dependencies added: attoparsec, containers

Dependencies removed: parsec

Files

src/Subversion/Dump.hs view
@@ -13,23 +13,29 @@         , readSvnDump        , readSvnDumpRaw++       , parseHeader+       , parseEntry        ) where -import           Control.Applicative hiding (many, (<|>))+import           Control.Applicative hiding (many) import           Control.Monad-import qualified Data.ByteString.Lazy as B---import qualified Data.ByteString.Lazy.Char8 as BC+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) import qualified Data.List as L+import           Data.Text (Text)+import qualified Data.Text.Encoding as E import           Data.Maybe-import           Data.Text.Lazy hiding (map, count)-import           Data.Text.Lazy.Encoding as E+import           Data.Word (Word8) import           System.FilePath-import           Text.Parsec-import           Text.Parsec.ByteString.Lazy as PB  import           Prelude hiding (getContents) -default (Data.Text.Lazy.Text)+default (ByteString)  -- | A parser for Subversion dump files.  The objective is to convert a dump --   file into a series of data structures representing that same information.@@ -69,10 +75,10 @@ data Operation = Operation { opKind          :: OpKind                            , opAction        :: OpAction                            , opPathname      :: FilePath-                           , opContents      :: B.ByteString+                           , opContents      :: ByteString                            , opContentLength :: Int-                           , opChecksumMD5   :: Maybe String-                           , opChecksumSHA1  :: Maybe String+                           , opChecksumMD5   :: Maybe Text+                           , opChecksumSHA1  :: Maybe Text                            , opCopyFromRev   :: Maybe Int                            , opCopyFromPath  :: Maybe FilePath }                deriving Show@@ -93,10 +99,11 @@ -- | 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 :: B.ByteString -> IO (Either ParseError [Revision])+readSvnDump :: BL.ByteString -> Either String [Revision] readSvnDump io = do-  result <- readSvnDumpRaw io-  return $ map processRevs <$> (L.groupBy sameRev <$> result)+  case readSvnDumpRaw io of+    Fail _ _ y    -> Left y+    Done _ result -> Right $ map processRevs (L.groupBy sameRev result)    where sameRev _ y     = isNothing $                           L.lookup "Revision-number" (entryTags y)@@ -110,24 +117,24 @@         processRevs [] = error "Unexpected"         processRevs (rev:ops) =           Revision {-              revNumber     = read $ tag "Revision-number" rev+              revNumber     = readInt $ tag "Revision-number" rev             , revDate       = parseDate $ prop "svn:date" rev-            , revAuthor     = propM "svn:author" rev-            , revComment    = propM "svn:log" rev+            , revAuthor     = E.decodeUtf8 <$> propM "svn:author" rev+            , revComment    = E.decodeUtf8 <$> propM "svn:log" rev             , revOperations = map processOp ops }          processOp op =           Operation {               opKind          = getOpKind $ tag "Node-kind" op             , opAction        = getOpAction $ tag "Node-action" op-            , opPathname      = tag "Node-path" op+            , opPathname      = BC.unpack $ tag "Node-path" op             , opContents      = entryBody op-            , opContentLength = read $ tag "Text-content-length" op-            , opCopyFromRev   = read <$>+            , opContentLength = readInt $ tag "Text-content-length" op+            , opCopyFromRev   = readInt <$>                                 tagM "Node-copyfrom-rev" op-            , opCopyFromPath  = tagM "Node-copyfrom-path" op-            , opChecksumMD5   = tagM "Text-content-md5" op-            , opChecksumSHA1  = tagM "Text-content-sha1" op }+            , opCopyFromPath  = BC.unpack <$> tagM "Node-copyfrom-path" op+            , opChecksumMD5   = E.decodeUtf8 <$> tagM "Text-content-md5" op+            , opChecksumSHA1  = E.decodeUtf8 <$> tagM "Text-content-sha1" op }          getOpKind kind = case kind of           "file" -> File@@ -141,83 +148,84 @@           "replace" -> Replace           _      -> error "Unexpected" -type FieldMap a = [(String, a)]+type FieldMap = [(ByteString, ByteString)] -data Entry = Entry { entryTags  :: FieldMap String-                   , entryProps :: FieldMap Text-                   , entryBody  :: B.ByteString }-             deriving Show+data Entry = Entry { entryTags  :: FieldMap+                   , entryProps :: FieldMap+                   , entryBody  :: ByteString }+           deriving Show -readSvnDumpRaw :: B.ByteString -> IO (Either ParseError [Entry])-readSvnDumpRaw dump = return $ parse parseSvnDump "" dump+readSvnDumpRaw :: BL.ByteString -> Result [Entry]+readSvnDumpRaw dump = parse parseSvnDump dump  -- These are the Parsec parsers for the various parts of the input file. -parseTag :: PB.Parser (String, String)-parseTag = (,) <$> fieldKey   <* char ':' <* space-               <*> fieldValue <* newline-  where-    fieldKey   = (:) <$> letter <*> many fieldChar-    fieldChar  = letter <|> digit <|> oneOf "-_"-    fieldValue = many1 (noneOf "\n")+space :: Parser Word8+space = satisfy (== 32) -parseIndicator :: PB.Parser (Char, Integer)-parseIndicator = (,) <$> oneOf "KV" <* space-                     <*> (read <$> many1 digit <* newline)+newline :: Parser Word8+newline = satisfy (== 10) -readTextRange :: Integer -> PB.Parser B.ByteString-readTextRange len = do-  input <- getInput-  let value = B.take (fromIntegral len) input-  setInput $ B.drop (fromIntegral len) input-  return value+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            -- _ ---readTextRange' :: Integer -> PB.Parser B.ByteString---readTextRange' len = BC.pack <$> count (fromIntegral len) anyChar+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 :: Char -> PB.Parser Text-parseSpecValue expected = do-  (kind, len) <- parseIndicator-  when (kind /= expected) $ unexpected "Unexpected spec value char"-  value <- readTextRange len-  --trace ("Value: " ++ (show value)) $ return ()-  _ <- newline-  return $ E.decodeUtf8 value+parseSpecValue :: Parser ByteString+parseSpecValue = do+  (_, len) <- parseIndicator+  AL.take len <* newline -parseProperty :: PB.Parser (String, Text)-parseProperty = (,) <$> (unpack <$> parseSpecValue 'K')-                    <*> parseSpecValue 'V'+parseProperty :: Parser (ByteString, ByteString)+parseProperty = (,) <$> parseSpecValue -- K+                    <*> parseSpecValue -- V -parseEntry :: PB.Parser Entry+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 _  -> many parseProperty <* string "PROPS-END\n"+              Just _  -> manyTill parseProperty (try (string "PROPS-END\n"))    body   <- case L.lookup "Text-content-length" fields of               Nothing  -> return B.empty-              Just len -> readTextRange (read len)+              Just len -> AL.take (readInt len) -  _ <- many newline <?> "entry-terminating newline"+  _ <- AL.takeWhile (== 10)    return Entry { entryTags  = fields                , entryProps = props                , entryBody  = body } -parseHeader :: PB.Parser ()+parseHeader :: Parser ByteString parseHeader = do   _ <- string "SVN-fs-dump-format-version: 2\n\n"        <?> "Dump file starts without a recognizable tag"-  _ <- string "UUID: " <* many1 (hexDigit <|> char '-')-       <* newline <* newline-  return ()+  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 :: PB.Parser [Entry]-parseSvnDump = parseHeader >> many parseEntry+parseSvnDump :: Parser [Entry]+parseSvnDump = parseHeader >> many1 parseEntry -parseDate :: Text -> RevDate-parseDate = id+parseDate :: ByteString -> RevDate+parseDate = E.decodeUtf8  -- SvnDump.hs ends here
svndump.cabal view
@@ -1,6 +1,6 @@ name:          svndump category:      Subversion-version:       0.1.1+version:       0.2.0 license:       BSD3 cabal-version: >= 1.8 license-file:  LICENSE@@ -31,9 +31,10 @@ library   build-depends:     base                 >= 4.3      && < 5,-    parsec               >= 3.1.3,+    attoparsec           >= 0.10.2,     filepath             >= 1.3,     bytestring           >= 0.9      && < 0.10,+    containers           >= 0.4.2,     text                 >= 0.11     && < 0.12    exposed-modules:@@ -50,6 +51,7 @@    build-depends:     base                 >= 4.3      && < 5,+    attoparsec           >= 0.10.2,     bytestring           >= 0.9      && < 0.10,     zlib                 >= 0.5      && < 0.6,     svndump
test/test-cooked.hs view
@@ -8,8 +8,7 @@ main :: IO () main = do   file <- B.readFile "data/cunit.dump.gz"-  dump <- readSvnDump $ GZip.decompress file-  case dump of+  case readSvnDump (GZip.decompress file) of     Left _   -> exitFailure     Right xs -> do       let len = length xs@@ -18,4 +17,4 @@         then exitSuccess         else exitFailure --- raw-parser.hs ends here+-- test-cooked.hs ends here
test/test-raw.hs view
@@ -1,6 +1,7 @@ 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           System.Exit@@ -8,14 +9,13 @@ main :: IO () main = do   file <- B.readFile "data/cunit.dump.gz"-  dump <- readSvnDumpRaw $ GZip.decompress file-  case dump of-    Left _   -> exitFailure-    Right xs -> do+  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 --- raw-parser.hs ends here+-- test-raw.hs ends here