-- | This module exports some useful 'Proxy's that act upon -- 'ParserStatus' values received from downstream. module Control.Proxy.Attoparsec.Control ( skipMalformedChunks , retryLeftovers , throwParsingErrors , limitInputLength ) where import Control.Proxy import Control.Proxy.Attoparsec.Types import qualified Control.Proxy.Trans.Either as PE import Prelude hiding (drop, null) -- | If a downstream parsing 'Proxy' reports a parser failure, skip the -- whole input /chunk/ being processed, including any left-overs, and -- start processing new input as soon as it's available. -- -- Useful when the input found in a single @'ParserSupply' a@ is -- supposed to be well-formed, so it can be safely skipped if it is not. skipMalformedChunks :: (Monad m, Proxy p, AttoparsecInput a) => ParserStatus a -> p (ParserStatus a) (ParserSupply a) (ParserStatus a) (ParserSupply a) m r skipMalformedChunks = runIdentityK . foreverK $ go where go x@(Failed _ _) = request x >>= \(_,a) -> respond (Start, a) go x = request x >>= respond -- | If a downstream parsing 'Proxy' reports a parsing failure, then -- keep retrying with any left-over input, skipping individual bits each -- time. If there are no left-overs, then more input is requestsd form -- upstream. retryLeftovers :: (Monad m, Proxy p, AttoparsecInput a) => ParserStatus a -> p (ParserStatus a) (ParserSupply a) (ParserStatus a) (ParserSupply a) m r retryLeftovers = runIdentityK . foreverK $ go where go s@(Failed _ _) = retry s >>= moreSupply >>= respond go s = request s >>= respond retry s@(Failed rest _) = do s' <- respond (Start, rest) if s' == s then return s else retry s' retry s = return s moreSupply s@(Failed rest _) = do let rest' = drop 1 rest if null rest' then request s else return (Start, rest') moreSupply s = request s -- | If a downstream parsing 'Proxy' reports a parser failure, then -- throw a 'MalformedInput' error in the 'EitherP' proxy transformer. throwParsingErrors :: (Monad m, Proxy p, AttoparsecInput a) => ParserStatus a -> PE.EitherP BadInput p (ParserStatus a) (ParserSupply a) (ParserStatus a) (ParserSupply a) m r throwParsingErrors = foreverK go where go (Failed _ e) = PE.throw $ MalformedInput e go x = request x >>= respond -- | If a downstream parsing 'Proxy' doesn't produce a value after -- having consumed input of at least the given length, then throw an -- 'InputTooLong' error in the 'EitherP' proxy transformer. limitInputLength :: (Monad m, Proxy p, AttoparsecInput a) => Int -> ParserStatus a -> PE.EitherP BadInput p (ParserStatus a) (ParserSupply a) (ParserStatus a) (ParserSupply a) m r limitInputLength n = foreverK go where go (Parsing m) | m >= n = PE.throw $ InputTooLong m go x = request x >>= respond