darcswatch-0.4: src/Darcs/Watch/Storage.hs
{-
Copyright (C) 2008 Joachim Breitner
This program 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 2, or (at your option)
any later version.
This program 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 COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
-}
module Darcs.Watch.Storage where
import Darcs
import Darcs.Watch.Data
import Data.Time
import qualified Data.ByteString.Char8 as B
import qualified Data.Map as M
import Data.ByteString.Char8 (ByteString)
import System.FilePath
import System.Directory
import Data.List
import Control.Applicative
import System.Directory
import Control.Monad
import Data.Char
-- | Adds a new patch bundle to the stogare
addBundle :: StorageConf -> PatchBundle -> IO BundleHash
addBundle path bundle = do
let hash = hash_bundle bundle
let dataFile = bundleDir path </> hash <.> "data"
ex <- doesFileExist dataFile
unless ex $ B.writeFile dataFile (B.pack "[]")
B.writeFile (bundleFileName path hash)
(make_bundle bundle)
return hash
-- | Retrieves a new patch bundle from the storage
getBundle :: StorageConf -> BundleHash -> IO PatchBundle
getBundle path hash =
either error id . scan_bundle <$> B.readFile (bundleFileName path hash)
-- | Retrieves the meta information for the handle
getBundleHistory :: StorageConf -> BundleHash -> IO [BundleHistory]
getBundleHistory path hash =
read <$> B.unpack <$> B.readFile (bundleDir path </> hash <.> "data")
-- | Adds a new entry to the bundle history (stamped with the current time)
changeBundleState :: StorageConf -> BundleHash -> Source -> BundleState -> IO ()
changeBundleState path hash source state = do
history <- getBundleHistory path hash
now <- getZonedTime
B.writeFile (bundleDir path </> hash <.> "data") $ B.pack $ show $
(now, source, state) : history
listBundles :: StorageConf -> IO [BundleHash]
listBundles path = do
items <- getDirectoryContents (bundleDir path)
return $ map dropExtension
$ filter ( (".dpatch" == ) . takeExtension )
$ items
updateRepository :: StorageConf -> (String -> IO ()) -> RepositoryURL -> IO Bool
updateRepository path write repo = do
let infoFile = repoDir path </> safeName repo <.> "data"
let repoFile = repoDir path </> safeName repo <.> "inventory"
ex <- doesFileExist infoFile
info <- if ex then read . B.unpack <$> B.readFile infoFile
else return (RepositoryInfo Nothing Nothing)
now <- getCurrentTime
(ps,changed) <- getInventory write (cacheDir path) repo
when changed $ B.writeFile repoFile (make_context ps)
B.writeFile infoFile $ B.pack $ show $ RepositoryInfo
{ lastCheck = Just now
, lastUpdate = if changed then Just now else lastUpdate info
}
return changed
readRepository :: StorageConf -> RepositoryURL -> IO [PatchInfo]
readRepository path repo = do
let repoFile = repoDir path </> safeName repo <.> "inventory"
ex <- doesFileExist repoFile
if ex then scan_context `fmap` B.readFile repoFile
else return []
getRepositoryInfo :: StorageConf -> RepositoryURL -> IO RepositoryInfo
getRepositoryInfo path repo = do
let infoFile = repoDir path </> safeName repo <.> "data"
ex <- doesFileExist infoFile
if ex then read . B.unpack <$> B.readFile infoFile
else return (RepositoryInfo Nothing Nothing)
setBundleListList :: StorageConf -> [BundleList] -> IO ()
setBundleListList path bll = do
writeFile (bundleListDir path </> "index") (show bll)
getBundleListList :: StorageConf -> IO [BundleList]
getBundleListList path = do
read <$> B.unpack <$> B.readFile (bundleListDir path </> "index")
writeBundleList :: StorageConf -> BundleList -> [BundleHash] -> IO ()
writeBundleList path bl blist = do
let filename = bundleListFilename path bl
sorted = sort blist
(oldList,_) <- readBundleList path bl
when (oldList /= blist) $ do
now <- getCurrentTime
writeFile filename (unlines (show now : blist))
readBundleList :: StorageConf -> BundleList -> IO ([BundleHash], UTCTime)
readBundleList path bl = do
let filename = bundleListFilename path bl
ex <- doesFileExist filename
if not ex then return ([],UTCTime (ModifiedJulianDay 0) 0) else do
(stamp:bhashes) <- lines <$> B.unpack <$> B.readFile filename
return (bhashes, read stamp)
writeNameMapping :: StorageConf -> M.Map Author String -> IO ()
writeNameMapping path map = do
B.writeFile (nameMappingFilename path) $ B.pack $ show map
readNameMapping :: StorageConf -> IO (M.Map Author String)
readNameMapping path = do
let filename = nameMappingFilename path
ex <- doesFileExist filename
if not ex then return M.empty else
read <$> B.unpack <$> B.readFile filename
bundleListFilename path (RepositoryBundleList repo) =
bundleListDir path </> "repo_" ++ safeName repo
bundleListFilename path (AuthorBundleList author) =
bundleListDir path </> "user_" ++ safeName author
bundleListFilename path UnmatchedBundleList =
bundleListDir path </> "unmatched"
nameMappingFilename path = path </> "nameMapping"
bundleDir :: StorageConf -> FilePath
bundleDir path = path </> "bundles"
bundleBaseName :: BundleHash -> FilePath
bundleBaseName hash = hash <.> "dpatch"
bundleFileName :: StorageConf -> BundleHash -> FilePath
bundleFileName path hash = bundleDir path </> bundleBaseName hash
repoDir :: StorageConf -> FilePath
repoDir path = path </> "repos"
cacheDir :: StorageConf -> FilePath
cacheDir path = path </> "cache"
bundleListDir :: StorageConf -> FilePath
bundleListDir path = path </> "bundleLists"
safeName n = map s n
where s c = if isSafeFileChar c then c else '_'
isSafeFileChar c = isAlpha c || isDigit c || c `elem` "-_.@:"