module Network.NetLines
(
netLine,
netLines,
netLineEmpty,
netLinesEmpty,
enumHandleTimeout
)
where
import qualified Data.ByteString as B
import Control.ContStuff
import Data.ByteString (ByteString)
import Data.Enumerator as E
import Data.Enumerator.Binary as EB
import Data.Word
import System.IO
import System.IO.Error as IOErr
enumHandleTimeout :: forall b m. MonadIO m =>
Int -> Int -> Handle -> Enumerator ByteString m b
enumHandleTimeout bufSize timeout h = loop
where
loop :: Enumerator ByteString m b
loop (Continue k) = do
mHaveInput <- liftIO $ IOErr.try (hWaitForInput h timeout)
case mHaveInput of
Left err
| isEOFError err -> continue k
| otherwise -> throwError err
Right False -> throwError $ userError "Handle timed out"
Right True -> do
mStr <- liftIO $ IOErr.try (B.hGetNonBlocking h bufSize)
str <- either throwError return mStr
if B.null str
then continue k
else k (Chunks [str]) >>== loop
loop step = returnI step
isEol :: Word8 -> Bool
isEol c = c == 10 || c == 13
isNotEol :: Word8 -> Bool
isNotEol = not . isEol
netLine :: forall m r. Monad m => Int -> MaybeT r (Iteratee ByteString m) ByteString
netLine n =
lift (EB.dropWhile isEol) >> netLine' n
where
netLine' :: Int -> MaybeT r (Iteratee ByteString m) ByteString
netLine' 0 = B.empty <$ lift (EB.dropWhile isNotEol)
netLine' n = do
c <- liftF EB.head
if isNotEol c
then B.cons c <$> netLine' (n1)
else return B.empty
netLineEmpty :: forall m. Monad m => Int -> Iteratee ByteString m (Maybe ByteString)
netLineEmpty maxLine =
joinI $ E.map (B.filter (/= 13)) $$ evalMaybeT (netLineEmpty' maxLine)
where
netLineEmpty' :: forall r. Int -> MaybeT r (Iteratee ByteString m) ByteString
netLineEmpty' 0 = B.empty <$ lift (EB.dropWhile (/= 10) >> EB.drop 1)
netLineEmpty' n = do
c <- liftF EB.head
if c /= 10
then B.cons c <$> netLineEmpty' (n1)
else return B.empty
netLines :: forall b m. Monad m => Int -> Enumeratee ByteString ByteString m b
netLines maxLen = loop
where
loop :: Enumeratee ByteString ByteString m b
loop (Continue k) = do
mLine <- evalMaybeT $ netLine maxLen
case mLine of
Just line -> k (Chunks [line]) >>== loop
Nothing -> k EOF >>== loop
loop step = return step
netLinesEmpty :: forall b m. Monad m => Int -> Enumeratee ByteString ByteString m b
netLinesEmpty maxLen = loop
where
loop :: Enumeratee ByteString ByteString m b
loop (Continue k) = do
mLine <- netLineEmpty maxLen
case mLine of
Just line -> k (Chunks [line]) >>== loop
Nothing -> k EOF >>== loop
loop step = return step