From bb33cb9cd3fa7f93ce895cc25ed00f5c5e9373f7 Mon Sep 17 00:00:00 2001 From: Bodigrim Date: Sat, 13 Dec 2025 23:46:05 +0000 Subject: [PATCH] Write database to a temporary file, then rename So that hitting Ctrl+C does not leave you with a corrupted database. This is using `withTempFile` from `temporary` and not from `extra`, because it comes with more handy bells and whistles. --- hoogle.cabal | 1 + src/General/Store.hs | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/hoogle.cabal b/hoogle.cabal index cf70b6a7..1f97e25a 100644 --- a/hoogle.cabal +++ b/hoogle.cabal @@ -80,6 +80,7 @@ library storable-tuple, tar, template-haskell, + temporary, text >= 2, time >= 1.5, transformers, diff --git a/src/General/Store.hs b/src/General/Store.hs index d37082ed..e145340b 100644 --- a/src/General/Store.hs +++ b/src/General/Store.hs @@ -33,8 +33,11 @@ import General.Util import Numeric.Extra import Paths_hoogle import Prelude -import System.IO.Extra +import System.Directory (renameFile) +import System.FilePath (takeDirectory, takeFileName) +import System.IO.Extra (Handle, hTell, hClose, hPutBuf) import System.IO.MMap +import System.IO.Temp (withTempFile) import System.IO.Unsafe -- Ensure the string is always 25 chars long, so version numbers don't change its size @@ -108,7 +111,7 @@ storeWriteFile :: FilePath -> (StoreWrite -> IO a) -> IO ([String], a) storeWriteFile file act = do atoms <- newIORef Map.empty parts <- newIORef Nothing - withBinaryFile file WriteMode $ \h -> do + withTempFile (takeDirectory file) (takeFileName file) $ \tmpFile h -> do -- put the version string at the start and end, so we can tell truncation vs wrong version BS.hPut h verString ref <- newIORef $ SW h (BS.length verString) [] @@ -130,6 +133,8 @@ storeWriteFile file act = do let stats = prettyTable 0 "Bytes" $ ("Overheads", intToDouble $ fromIntegral final - sum (map atomSize $ Map.elems atoms)) : [(name ++ " :: " ++ atomType, intToDouble atomSize) | (name, Atom{..}) <- Map.toList atoms] + hClose h + renameFile tmpFile file pure (stats, res) storeWrite :: (Typeable (t a), Typeable a, Stored a) => StoreWrite -> t a -> a -> IO ()