content: {-# LANGUAGE ExistentialQuantification, FlexibleInstances #-}
  
  -- | A class describing IO operations with instances for IO and strings.
  -- This is primarily for the purpose of unit testing.
  module IO.IOStream (IOStream(..),IOStream'(..)) where
  
  import Data.IORef
  import System.IO
  
  -- | The abstract base class describing methods for IOStreams.
  -- The purpose of each function is identical to the ones in
  -- System.IO.Handle starting with an 'h' instead of a 'v'.
  class IOStream' h where
      vGetContents :: h -> IO String
      vSetContents :: h -> String -> IO ()
      vPutStr :: h -> String -> IO ()
      vFlush :: h -> IO ()
      vGetLine :: h -> IO String
      vIsEOF :: h -> IO Bool
  
  -- | An instance of IOStream' for IO Handles.
  instance IOStream' Handle where
      vGetContents = hGetContents
      vSetContents h s = hSetFileSize h 0 >> hPutStr h s
      vPutStr  = hPutStr
      vFlush   = hFlush
      vGetLine = hGetLine
      vIsEOF   = hIsEOF
  
  -- | An instance for IOStream' for references to Strings.
  instance IOStream' (IORef String) where
      vGetContents h = readIORef h
      vSetContents h s = writeIORef h s
      vPutStr h s = modifyIORef h (\text -> text ++ s)
      vFlush _ = return ()
      vGetLine h = do
          text <- readIORef h
          let (s, text') = span (/= '\n') text
          if not (null text') && head text' == '\n'
              then do
                  writeIORef h (tail text')
                  return s
              else do
                  writeIORef h ""
                  return text'
      vIsEOF h = do
          text <- readIORef h
          return $ null text
  
  -- | A datatype wrapper for IOStream'.
  data IOStream = forall h. IOStream' h => IOStream h
  
  -- | IOStream is itself an instance of IOStream', which reduces the
  -- overhead of unwrapping them from the IOStream constructor.
  instance IOStream' IOStream where
      vGetContents (IOStream h) = vGetContents h
      vSetContents (IOStream h) = vSetContents h
      vPutStr (IOStream h) = vPutStr h
      vFlush (IOStream h) = vFlush h
      vGetLine (IOStream h) = vGetLine h
      vIsEOF (IOStream h) = vIsEOF h
  