]> gitweb @ CieloNegro.org - Rakka.git/blobdiff - Rakka/Storage.hs
Record to save changes
[Rakka.git] / Rakka / Storage.hs
index 1abace0ac17453e210c87b284cb56eda4be73282..023806134a52a2854b5da15263133aea984b950a 100644 (file)
@@ -12,17 +12,56 @@ module Rakka.Storage
     where
 
 import           Control.Arrow.ArrowIO
+import           Control.Concurrent.STM
+import           Control.Exception
+import           Control.Monad
 import           Control.Monad.Trans
+import           Data.Set (Set)
 import           Rakka.Page
 import           Rakka.Storage.DefaultPage
 import           Subversion.Types
-
-
-data Storage = Storage -- FIXME
-
-
-mkStorage :: Storage -- FIXME
-mkStorage = Storage
+import           System.Directory
+import           System.FilePath
+import           System.IO
+import           System.Log.Logger
+import           System.Posix.Files
+import           System.Posix.Types
+import           System.Posix.IO
+import           Subversion.FileSystem
+import           Subversion.Repository
+import           Text.HyperEstraier hiding (WriteLock)
+
+logger = "Rakka.Storage"
+
+
+data Storage
+    = Storage {
+        stoIndexRevLocked :: !(TVar Bool)
+      , stoIndexRevFile   :: !FilePath
+      , stoIndexDB        :: !Database
+      , stoRepository     :: !Repository
+      , stoMakeDraft      :: !(Page -> IO Document)
+      }
+
+
+mkStorage :: FilePath -> Repository -> (Page -> IO Document) -> IO Storage
+mkStorage lsdir repos mkDraft
+    = do let indexDir = lsdir `combine` "index"
+             revFile  = lsdir `combine` "indexRev"
+             
+         revLocked <- newTVarIO False
+         indexDB   <- openIndex indexDir revFile
+
+         let sto = Storage {
+                     stoIndexRevLocked = revLocked
+                   , stoIndexRevFile   = revFile
+                   , stoIndexDB        = indexDB
+                   , stoRepository     = repos
+                   , stoMakeDraft      = mkDraft
+                   }
+
+         syncIndex sto
+         return sto
 
 
 getPage :: MonadIO m => Storage -> PageName -> m (Maybe Page)
@@ -40,4 +79,109 @@ getPageA = arrIO . getPage
 
 
 putPageA :: ArrowIO a => Storage -> a (Maybe RevNum, Page) ()
-putPageA = arrIO2 . putPage
\ No newline at end of file
+putPageA = arrIO2 . putPage
+
+
+findAllPages :: Storage -> RevNum -> IO (Set PageName)
+findAllPages sto revNum
+    = findAllDefaultPages -- FIXME
+
+
+findChangedPages :: Storage -> RevNum -> RevNum -> IO (Set PageName)
+findChangedPages sto 0      newRev = findAllPages sto newRev
+findChangedPages sto oldRev newRev
+    = fail "fixme: not impl"
+
+
+getCurrentRevNum :: Storage -> IO RevNum
+getCurrentRevNum sto
+    = getRepositoryFS (stoRepository sto) >>= getYoungestRev
+
+
+-- casket を R/W モードで開く。成功したらそのまま返し、失敗したら
+-- indexDir と revFile を削除してから casket を R/W モードで開く。
+openIndex :: FilePath -> FilePath -> IO Database
+openIndex indexDir revFile
+    = do ret <- openDatabase indexDir (Writer [])
+         case ret of
+           Right db
+               -> do debugM logger ("Opened an H.E. database on " ++ indexDir)
+                     return db
+
+           Left err
+               -> do warningM logger ("Failed to open an H.E. database on "
+                                      ++ indexDir ++ ": " ++ show err)
+
+                     indexExists <- doesDirectoryExist indexDir
+                     when indexExists
+                              $ removeDirectoryRecursive indexDir
+
+                     revFileExists <- doesFileExist revFile
+                     when revFileExists
+                              $ removeFile revFile
+
+                     Right db <- openDatabase indexDir (Writer [Create []])
+                     noticeM logger ("Created an H.E. database on " ++ indexDir)
+
+                     return db
+
+
+syncIndex :: Storage -> IO ()
+syncIndex sto
+    = updateIndexRev sto $ \ oldRev ->
+      do debugM logger ("The index revision is currently " ++ show oldRev)
+         
+         newRev <- getCurrentRevNum sto
+         debugM logger ("The repository revision is currently " ++ show newRev)
+
+         when (newRev /= oldRev) (syncIndex' oldRev newRev)
+
+         return oldRev -- FIXME
+    where
+      syncIndex' :: RevNum -> RevNum -> IO ()
+      syncIndex' oldRev newRev
+          = do pages <- findChangedPages sto oldRev newRev
+               print pages
+
+
+updateIndexRev :: Storage -> (RevNum -> IO RevNum) -> IO ()
+updateIndexRev sto f = bracket acquireLock releaseLock update
+    where
+      acquireLock :: IO Fd
+      acquireLock
+          = do atomically $ do revLocked <- readTVar (stoIndexRevLocked sto)
+                               if revLocked then
+                                   retry
+                                 else
+                                   writeTVar (stoIndexRevLocked sto) True
+               fd <- openFd (stoIndexRevFile sto) ReadWrite (Just stdFileMode) defaultFileFlags
+               waitToSetLock fd (WriteLock, AbsoluteSeek, 0, 0)
+               return fd
+
+      releaseLock :: Fd -> IO ()
+      releaseLock fd
+          = do setLock fd (Unlock, AbsoluteSeek, 0, 0)
+               atomically $ writeTVar (stoIndexRevLocked sto) False
+
+      update :: Fd -> IO ()
+      update fd
+          = do fdSeek fd AbsoluteSeek 0
+               size <- return . fromIntegral . fileSize =<< getFdStatus fd
+               (revStr, gotSize) <- fdRead fd size
+               when (size /= gotSize) $ fail ("read " ++ show gotSize ++
+                                              " bytes but expected " ++ show size ++ " bytes")
+               
+               let rev = case revStr of
+                           "" -> 0
+                           _  -> read revStr
+
+               rev' <- f rev
+
+               let revStr' = show rev' ++ "\n"
+                   size'   = fromIntegral $ length revStr'
+
+               fdSeek fd AbsoluteSeek 0
+               setFdSize fd 0
+               wroteSize <- fdWrite fd revStr'
+               when (size' /= wroteSize) $ fail ("wrote " ++ show wroteSize ++
+                                                 " bytes but expected " ++ show size' ++ " bytes")
\ No newline at end of file