dao-0.1.0.1: src/Dao/Lib/File.hs
-- "src/Dao/Lib/File.hs" built-in plain file object
--
-- Copyright (C) 2008-2014 Ramin Honary.
-- This file is part of the Dao System.
--
-- The Dao System is free software: you can redistribute it and/or
-- modify it under the terms of the GNU General Public License as
-- published by the Free Software Foundation, either version 3 of the
-- License, or (at your option) any later version.
--
-- The Dao System is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program (see the file called "LICENSE"). If not, see
-- <http://www.gnu.org/licenses/agpl.html>.
module Dao.Lib.File where
import Dao.String
import Dao.Predicate
import Dao.PPrint
import qualified Dao.Binary as B
import Dao.Interpreter
import qualified Data.ByteString.Lazy as B
import qualified Data.Map.Lazy as M
import Data.Typeable
import Control.Applicative
import Control.Monad
import Control.Monad.IO.Class
import Control.Monad.State
import System.IO
data File = File { filePath :: UStr, fileHandle :: Maybe Handle } deriving Typeable
instance Eq File where { a==b = filePath a == filePath b }
instance Ord File where { compare a b = compare (filePath a) (filePath b) }
instance Show File where { show a = "File("++show (filePath a)++")" }
instance HasNullValue File where
nullValue = File{ filePath=nil, fileHandle=Nothing }
testNull (File{ filePath=name, fileHandle=Nothing }) = nil==name
testNull (File{}) = False
instance PPrintable File where { pPrint = pShow }
instance B.Binary File MethodTable where
get = return File <*> B.get <*> pure Nothing
put f = B.put (filePath f)
errFilePath :: Name
errFilePath = ustr "filePath"
_openParamFail :: String -> [Object] -> Exec ig
_openParamFail func ox =
throwArityError "expecting a file path as the only parameter" 1 ox [(errInFunc, obj (ustr func :: Name))]
_paramPath :: String -> [Object] -> Exec UStr
_paramPath func ox = case ox of
[path] -> xmaybe (fromObj path <|> (filePath <$> fromObj path)) <|> _openParamFail func ox
ox -> _openParamFail func ox
_catchIOException :: String -> File -> IO a -> Exec a
_catchIOException func file f = execCatchIO (liftIO f) $
[ newExecIOHandler $ \e -> execThrow "" (ExecIOException e) $
[(errInFunc, obj (ustr func :: Name)), (errFilePath, obj $ filePath file)]
]
_openFile :: String -> File -> IOMode -> Exec File
_openFile func file mode = _catchIOException func file $
(\o -> file{ fileHandle=Just o}) <$> openFile (uchars $ filePath file) mode
_getHandle :: String -> File -> Exec Handle
_getHandle func file = case fileHandle file of
Just h -> return h
Nothing ->
execThrow "function evaluated on a file handle which has not been opened" ExecErrorUntyped
[(errInFunc, obj (ustr func :: Name)), (errFilePath, obj $ filePath file)]
_withClosedHandle :: String -> File -> Exec ()
_withClosedHandle func file = case fileHandle file of
Nothing -> return ()
Just _ -> execThrow "function cannot operate on open file handle" ExecErrorUntyped
[(errInFunc, obj (ustr func :: Name)), (errFilePath, obj $ filePath file)]
_withContents :: (String -> IO Object) -> DaoFunc File
_withContents f =
daoFunc
{ daoForeignFunc = \file ox -> case ox of
[] -> do
_withClosedHandle "read" file
_catchIOException "read" file $ fmap (flip (,) file . Just) $ readFile (uchars $ filePath file) >>= f
ox -> throwArityError "" 0 ox [(errInFunc, obj (ustr "read" :: Name))]
}
gGetErrToExecError :: B.GGetErr -> ExecControl
gGetErrToExecError (B.GetErr{ B.gGetErrOffset=offset, B.gGetErrMsg=msg }) =
newError
{ execErrorMessage = msg
, execErrorInfo = M.fromList [(ustr "byteOffset", OLong (toInteger offset))]
}
loadLibrary_File :: DaoSetup
loadLibrary_File = do
let fileOpener func mode = daoFunction func $
daoFunc
{ daoForeignFunc = \ () ->
_paramPath func >=> fmap (flip (,) () . Just . obj) . flip (_openFile func . flip File Nothing) mode
}
fileOpener "readFile" ReadMode
fileOpener "writeFile" WriteMode
fileOpener "appendFile" AppendMode
daoClass (haskellType::File)
daoFunction "File" $
daoFunc
{ daoForeignFunc = \ () -> fmap (flip (,) () . Just . obj . flip File Nothing) . _paramPath "File"
}
instance ObjectClass File where { obj=new; fromObj=objFromHata; }
instance HataClass File where
haskellDataInterface = interface "File" $ do
autoDefEquality >> autoDefOrdering >> autoDefNullTest
autoDefPPrinter >> autoDefBinaryFmt
let fileOpener func mode = defMethod func $
daoFunc
{ funcAutoDerefParams = False
, daoForeignFunc = \file ox ->
case ox of
[] -> do
_withClosedHandle func file
f <- _openFile func file mode
return (Just $ obj f, f)
ox -> throwArityError "" 0 ox [(errInFunc, obj (ustr "File" :: Name))]
}
fileOpener "openRead" ReadMode
fileOpener "openWrite" WriteMode
fileOpener "openAppend" AppendMode
defMethod "close" $
daoFunc
{ funcAutoDerefParams = False
, daoForeignFunc = \file ox -> fmap (flip (,) (file{ fileHandle=Nothing })) $ do
case ox of
[] -> _getHandle "close" file >>=
_catchIOException "close" file . liftIO . hClose >> return Nothing
ox -> throwArityError "" 0 ox [(errInFunc, obj (ustr "close" :: Name))]
}
defMethod "writeBinary" $
daoFunc
{ daoForeignFunc = \file ox -> do
mtab <- gets globalMethodTable
handl <- _getHandle "writeBinary" file
forM_ ox $ \o -> do
bin <- execCatchIO (liftIO $ return (B.encode mtab o)) $
[ newExecIOHandler $ \e -> execThrow "while encoding object" (ExecHaskellError e) $
[ (errInFunc, obj (ustr "writeBinary" :: Name))
, (errFilePath, obj $ filePath file)
]
]
_catchIOException "writeBinary" file (B.hPutStr handl bin)
return (Nothing, file)
}
defMethod "readBinary" $
daoFunc
{ daoForeignFunc = \file ox -> case ox of
[] -> do
_withClosedHandle "readBinary" file
mtab <- gets globalMethodTable
bin <- _catchIOException "readBinary" file (liftIO $ B.readFile $ uchars $ filePath file)
result <- execCatchIO (return $ fmapPFail gGetErrToExecError $ B.decode mtab bin) $
[ newExecIOHandler $ \e -> execThrow "while decoding object" (ExecHaskellError e) $
[(errInFunc, obj (ustr "readBinary" :: Name)), (errFilePath, obj $ filePath file)]
]
predicate result
ox -> throwArityError "" 0 ox [(errInFunc, obj (ustr "readBinary" :: Name))]
}
let writeFunc func putstr = defMethod func $
daoFunc
{ daoForeignFunc = \file ox -> do
ox <- requireAllStringArgs func ox
handl <- _getHandle func file
forM_ ox $ _catchIOException "write" file . putstr handl . uchars
return (Nothing, file)
}
writeFunc "write" hPutStr
defMethod "read" $ _withContents (return . obj)
defMethod "readAllLines" $ _withContents (return . obj . fmap obj . lines)
writeFunc "writeLine" hPutStrLn
defMethod "readLine" $
daoFunc
{ daoForeignFunc = \file ox -> case ox of
[] -> do
handl <- _getHandle "readLine" file
_catchIOException "readLine" file (flip (,) file . Just . obj <$> hGetLine handl)
ox -> throwArityError "" 0 ox [(errInFunc, obj (ustr "readLine" :: Name))]
}
let defPrinter func print = defMethod func $
makePrintFunc $ \file str -> _getHandle func file >>= \h -> liftIO (print h str)
defPrinter "print" hPutStr
defPrinter "println" hPutStrLn