diff --git a/src/Subversion/Dump.hs b/src/Subversion/Dump.hs
--- a/src/Subversion/Dump.hs
+++ b/src/Subversion/Dump.hs
@@ -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
diff --git a/src/Subversion/Dump/Raw.hs b/src/Subversion/Dump/Raw.hs
new file mode 100644
--- /dev/null
+++ b/src/Subversion/Dump/Raw.hs
@@ -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
diff --git a/svndump.cabal b/svndump.cabal
--- a/svndump.cabal
+++ b/svndump.cabal
@@ -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
diff --git a/test/test-cooked.hs b/test/test-cooked.hs
--- a/test/test-cooked.hs
+++ b/test/test-cooked.hs
@@ -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
diff --git a/test/test-raw.hs b/test/test-raw.hs
--- a/test/test-raw.hs
+++ b/test/test-raw.hs
@@ -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
