-- ---------------------------------------------------------------------------- {- | Module : Holumbus.FileSystem.Storage.FileStorage Copyright : Copyright (C) 2008 Stefan Schmidt License : MIT Maintainer : Stefan Schmidt (stefanschmidt@web.de) Stability : experimental Portability: portable Version : 0.1 Implementation of the storage class. The FileStorage saves each file in a single physical file. The metadata of the files is hold in an extra directory an saved on disk. -} -- ---------------------------------------------------------------------------- module Holumbus.FileSystem.Storage.FileStorage ( -- * datatypes FileStorage -- * storage initialisation , newFileStorage ) where import Data.Binary import System.Directory import System.Log.Logger import qualified Data.Map as Map import Holumbus.Common.Utils ( handleAll ) import Holumbus.Common.FileHandling import qualified Holumbus.FileSystem.Storage as S localLogger :: String localLogger = "Holumbus.FileSystem.Storage.FileStorage" -- | The directory of the files in the storage. type StorageFileDirectory = Map.Map S.FileId S.FileData -- | The filestorage datatype. -- Every file is physically saved in one file on the harddisk data FileStorage = MkFileStorage { fs_Path :: FilePath -- ^ path to the storage directory , fs_DirfilePath :: FilePath -- ^ path to the directory file , fs_Directory :: StorageFileDirectory -- ^ the directory with the metadata } deriving (Show) -- ----------------------------------------------------------------------------- -- internal operations - adding and deleting files to the directory -- ----------------------------------------------------------------------------- -- | Add or update a new file to the directory. addFileData :: StorageFileDirectory -> S.FileData -> StorageFileDirectory addFileData dir new = Map.insert fn dat dir where fn = (S.fd_FileId new) dat = maybe new (S.updateFileData new) (Map.lookup fn dir) -- | Delete a new file to the directory. deleteFileData :: StorageFileDirectory -> S.FileId -> StorageFileDirectory deleteFileData dir i = Map.delete i dir -- | Lookup the metadata from the directory. lookupFileData :: StorageFileDirectory -> S.FileId -> (Maybe S.FileData) lookupFileData dir i = Map.lookup i dir -- | Get all fileIds from the directory getIds :: StorageFileDirectory -> [S.FileId] getIds dir = Map.keys dir -- | Check, if a fileId is a member of the storage isMember :: StorageFileDirectory -> S.FileId -> Bool isMember dir i = Map.member i dir -- ----------------------------------------------------------------------------- -- file storage operations and typeclass -- ----------------------------------------------------------------------------- -- | Create a new filestorage, which is empty an contains no files newFileStorage :: FilePath -- ^ the path of the filestorage on disk -> FilePath -- ^ the name of the directory file -> FileStorage newFileStorage path name = MkFileStorage path (path ++ name) Map.empty -- | Loads the filestorage from disk readDirectory :: FileStorage -> IO (FileStorage) readDirectory stor = do handleAll (\_ -> writeDirectory stor) $ do infoM localLogger ("opening filestorage directory: " ++ (fs_DirfilePath stor)) file <- decodeFile (fs_DirfilePath stor) file `seq` return (stor {fs_Directory = file}) {- bracket (do infoM localLogger ("opening filestorage directory: " ++ (fs_DirfilePath stor)) openFile (fs_DirfilePath stor) ReadMode ) (hClose) (\hdl -> do pkg <- liftM words $ hGetLine hdl raw <- B.hGet hdl (read $ head pkg) dir <- return (decode raw) return (stor {fs_Directory = dir}) )-} -- | Saves the filestorage on disk writeDirectory :: FileStorage -> IO (FileStorage) writeDirectory stor = do infoM localLogger ("writing filestorage directory: " ++ (fs_DirfilePath stor)) createDirectoryIfMissing True (fs_Path stor) encodeFile (fs_DirfilePath stor) (fs_Directory stor) return stor {- bracket (do infoM localLogger ("writing filestorage directory: " ++ (fs_DirfilePath stor)) createDirectoryIfMissing True (fs_Path stor) debugM localLogger "directory created .. " openFile (fs_DirfilePath stor) WriteMode) (hClose) (\hdl -> do debugM localLogger "encode store.. " enc <- return (encode $ fs_Directory stor) debugM localLogger "calc length .. " hPutStrLn hdl ((show $ B.length enc) ++ " ") debugM localLogger "put store .. " B.hPut hdl enc debugM localLogger "return .. " return stor )-} writeSingleFile :: FileStorage -> S.FileId -> S.FileContent -> IO FileStorage writeSingleFile stor fn c = do debugM localLogger "write to bin file .. " writeToBinFile path c debugM localLogger "create file metadata" dat <- S.createFileData fn c return $ stor {fs_Directory = newdir dat} where path = (fs_Path stor) ++ fn newdir d = addFileData (fs_Directory stor) d -- | deriving FileStorage from the class Storage instance S.Storage FileStorage where openStorage = readDirectory closeStorage = writeDirectory createFile stor fn c = do stor' <- writeSingleFile stor fn c debugM localLogger "write directory" writeDirectory stor' createFiles stor l = do debugM localLogger "write to bin file .. " stor' <- writeFiles stor l debugM localLogger "write directory" writeDirectory stor' where writeFiles :: FileStorage -> [(S.FileId,S.FileContent)] -> IO FileStorage writeFiles stor'' [] = return stor'' writeFiles stor'' ((fn,c):xs) = do stor''' <- writeSingleFile stor'' fn c writeFiles stor''' xs deleteFile stor i = do stor' <- if isMember (fs_Directory stor) i then do removeFile path return (stor {fs_Directory = newdir}) else return stor writeDirectory $ stor' where path = (fs_Path stor) ++ i newdir = deleteFileData (fs_Directory stor) i appendFile stor i c = do if isMember (fs_Directory stor) i then do appendToBinFile path c -- case c of -- (S.TextFile t) -> appendToTextFile path t -- (S.ListFile l) -> appendToListFile path l -- (S.BinFile b) -> appendToBinFile path b dat <- S.createFileData i c writeDirectory $ stor { fs_Directory = newdir dat } else do S.createFile stor i c where path = (fs_Path stor) ++ i newdir d = addFileData (fs_Directory stor) d containsFile stor i = do return (isMember (fs_Directory stor) i) getFileContent stor i = do infoM localLogger $ "getFileContent: reading " ++ show i if (isMember (fs_Directory stor) i) then do handleAll (\e -> do errorM localLogger $ "getFileContent: " ++ show e return Nothing ) $ do -- metaData <- S.getFileData stor i c <- readFromBinFile path {- c <- case (S.fd_Type $ fromJust metaData) of (S.FTText) -> do t <- readFromTextFile path return $ S.TextFile t (S.FTList) -> do l <- readFromListFile path return $ S.ListFile l (S.FTBin) -> do b <- readFromBinFile path return $ S.BinFile b -} debugM localLogger $ "getFileContent: content: " ++ show c infoM localLogger $ "getFileContent: finished " return (Just c) else do return Nothing where path = (fs_Path stor) ++ i getFileData stor i = do return (lookupFileData (fs_Directory stor) i) getFileIds stor = do return (getIds (fs_Directory stor))