Browse Source

Streamline: Sane treatment of end of input

Marcos Dumay de Medeiros 7 years ago
parent
commit
05670c893c
1 changed files with 28 additions and 15 deletions
  1. 28 15
      src/System/IO/Uniform/Streamline.hs

+ 28 - 15
src/System/IO/Uniform/Streamline.hs

@@ -33,9 +33,10 @@ module System.IO.Uniform.Streamline (
   scan',
   recieveTill,
   recieveTill',
-  -- * Behavior settings
+  -- * Behavior changing and status
   startTls,
   isSecure,
+  isEOF,
   transformTarget,
   limitInput,
   echoTo,
@@ -65,11 +66,13 @@ import Data.IP (IP)
 
 import qualified Data.Attoparsec.ByteString as A
 
+import Debug.Trace
+
 -- | Internal state for a Streamline monad
-data StreamlineState = StreamlineState {str :: SomeIO, buff :: ByteString, isEOF :: Bool, echo :: Maybe Handle, inLimit :: Int, sentEmpty :: Bool}
+data StreamlineState = StreamlineState {str :: SomeIO, buff :: ByteString, targetEOF :: Bool, echo :: Maybe Handle, inLimit :: Int}
 instance Default StreamlineState where
   -- | Will open StdIO
-  def = StreamlineState (SomeIO Std.StdIO) BS.empty False Nothing (-1) False
+  def = StreamlineState (SomeIO Std.StdIO) BS.empty False Nothing (-1)
 
 -- | Monad that emulates character stream IO over block IO.
 newtype Streamline m a = Streamline {withTarget' :: StreamlineState -> m (a, StreamlineState)}
@@ -86,7 +89,10 @@ readF = -- Must try just not to read more than the limit, actual limiting is don
            else if lim <= blockSize then lim
                 else blockSize
   l <- liftIO $ S.uRead (str cl) sz
-  let cl' = cl{buff= l}
+  let cl' = cl{
+        buff = l,
+        targetEOF = BS.null l
+        }
   case echo cl of
     Just h -> do
       liftIO $ BS.hPutStr h "< "
@@ -100,18 +106,14 @@ takeBuff = do
   readF
   Streamline $ \cl -> 
     let lim = inLimit cl
-        eof = isEOF cl
+        eof = targetEOF cl
         b = buff cl
-    in if eof then eofError "System.IO.Uniform.Streamline"
-       else if lim < 0 then return (b, cl{buff="", isEOF=BS.null b})
-            else let (r, b') = BS.splitAt lim b
-                 in return (r, cl{
-                               -- EOF is at the real end of file, not on limited input
-                               isEOF = lim /= 0 && (BS.null b || sentEmpty cl),
-                               sentEmpty = BS.null r,
-                               buff = b',
-                               inLimit = lim - BS.length r
-                               })
+    in if lim < 0 then return (b, cl{buff=""})
+       else let (r, b') = BS.splitAt lim b
+            in return (r, cl{
+                          buff = b',
+                          inLimit = lim - BS.length r
+                          })
 
 -- | Pushes remaining data back into the buffer
 pushBuff :: Monad m => ByteString -> Streamline m ()
@@ -332,6 +334,17 @@ runAttoparsec p = snd <$> runAttoparsecAndReturn p
 isSecure :: Monad m => Streamline m Bool
 isSecure = Streamline $ \cl -> return (S.isSecure $ str cl, cl)
 
+{- |
+True if the input is at the end of stream.
+
+If the input is limited, will be true when either the limit is reached,
+or the underlining target was at actual end of stream. In case of EOF
+due to reaching the limit, changing the limit will immediately make this
+return False again.
+-}
+isEOF :: Monad m => Streamline m Bool
+isEOF = Streamline $ \cl -> return (targetEOF cl || inLimit cl == 0, cl)
+
 -- | Sets echo of the streamlines IO target.
 --   If echo is set, all the data read an written to the target
 --   will be echoed in stdout, with ">" and "<" markers indicating