{-# LANGUAGE DeriveDataTypeable #-}
-- | A function to convert attoparsec 'Parser's into 'Iteratee's.
module Data.Attoparsec.Iteratee
  ( ParseError(..), parserToIteratee ) where


------------------------------------------------------------------------------
import           Control.Exception
import qualified Data.Attoparsec as Atto
import           Data.Attoparsec hiding (many, Result(..))
import           Data.ByteString (ByteString)
import qualified Data.ByteString as B
import           Data.Iteratee
import           Data.Typeable
------------------------------------------------------------------------------

data ParseError
    = ParseError {errorContexts :: [String], errorMessage :: String}
    deriving (Show, Typeable)

instance Exception ParseError

-- | A function to convert attoparsec 'Parser's into 'Iteratee's.
parserToIteratee :: (Monad m) =>
                    Parser a
                 -> Iteratee ByteString m a
parserToIteratee p =
    icont (f (parse p)) Nothing
  where
    f k (EOF Nothing) =
        case feed (k B.empty) B.empty of
          Atto.Fail _ err dsc -> throwErr (toException $ ParseError err dsc)
          Atto.Partial _ -> throwErr (toException EofException)
          Atto.Done rest v
              | B.null rest -> idone v (EOF Nothing)
              | otherwise -> idone v (Chunk rest)
    f _ (EOF (Just e)) = throwErr e
    f k (Chunk s)
        | B.null s = icont (f k) Nothing
        | otherwise =
            case k s of
              Atto.Fail _ err dsc -> throwErr (toException $
                                               ParseError err dsc)
              Atto.Partial k' -> icont (f k') Nothing
              Atto.Done rest v -> idone v (Chunk rest)