{-# 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