gdritter repos tansu / 134f153
Haddocks for everything! Getty Ritter 8 years ago
5 changed file(s) with 65 addition(s) and 6 deletion(s). Collapse all Expand all
11 module Database.Tansu.Backend.Ephemeral
2 (withEphemeralDb) where
2 ( EphemeralDb
3 , withEphemeralDb
4 , withNewEphemeralDb
5 , createEphemeralDb
6 ) where
37
48 import Control.Concurrent.MVar
59 import Data.ByteString (ByteString)
610 import Data.IORef
11 import Data.Map.Strict (Map)
712 import qualified Data.Map.Strict as M
13 import Data.Serialize (Serialize, encode)
14 import Database.Tansu (Tansu)
815 import Database.Tansu.Internal (TansuDb(..))
916
1017 type Table = M.Map ByteString ByteString
1825 ephemeralGet :: IORef Table -> ByteString -> IO (Maybe ByteString)
1926 ephemeralGet table key = M.lookup key `fmap` readIORef table
2027
21 withEphemeralDb :: (TansuDb -> IO a) -> IO a
22 withEphemeralDb comp = do
28 -- | An 'EphemeralDb' is just an in-memory map, with no way of saving it.
29 -- It is intended to be used for testing Tansu code.
30 newtype EphemeralDb = EDB { fromEDB :: Table }
31
32 -- | Create an in-memory table to use for testing.
33 createEphemeralDb :: (Serialize k, Serialize v) => [(k, v)] -> EphemeralDb
34 createEphemeralDb ls = EDB (M.fromList [ (encode k, encode v) | (k, v) <- ls ])
35
36 -- | Run a 'Tansu' operation with an empty in-memory table.
37 withNewEphemeralDb :: (TansuDb -> IO a) -> IO a
38 withNewEphemeralDb = withEphemeralDb $ EDB M.empty
39
40 -- | Run a 'Tansu' operation with an existing in-memory table.
41 withEphemeralDb :: EphemeralDb -> (TansuDb -> IO a) -> IO a
42 withEphemeralDb init comp = do
2343 lock <- newMVar ()
24 table <- newIORef M.empty
44 table <- newIORef (fromEDB init)
2545 comp $ TansuDb
2646 { dbRunTransaction = ephemeralRunTransaction lock
2747 , dbSet = ephemeralSet table
2525 filePathLock path comp = do
2626 withFileLock (path </> ".lock") Exclusive (const comp)
2727
28 -- | Use a local directory as a key-value store. Each key-value
29 -- pair is represented as a file whose filename is the
30 -- Base64-encoded serialized key, and whose content is the
31 -- raw serialized value. Locking is done using the
32 -- @.lock@ file in the specified directory, but note that
33 -- file locking is not a guaranteed way of ensuring exclusion,
34 -- and that the files themselves are not locked in any way.
2835 withFilesystemDb :: FilePath -> (TansuDb -> IO a) -> IO a
2936 withFilesystemDb path comp = do
3037 createDirectoryIfMissing True path
66
77 import Data.ByteString (ByteString)
88
9 -- | The 'TansuError' type enumerates the possible errors that
10 -- can arise in the process of running a sequence of database
11 -- accesses. Backends should attempt to use the most informative
12 -- error available, but the 'OtherError' constructor is provided
13 -- as a catch-all if none of the other constructors are
14 -- descriptive of the error in question.
915 data TansuError
1016 = KeyNotFound ByteString
1117 | DecodeError String
18 | AccessError String
19 | OtherError String
1220 deriving (Eq, Show)
1321
22 -- | The data representation of a Tansu backend. Users of the library
23 -- should treat this as an abstract type, but the full definition
24 -- is exposed by the "Database.Tansu.Internal" module so that
25 -- other libraries can implement new storage backends.
1426 data TansuDb = TansuDb
1527 { dbSet :: ByteString -> ByteString -> IO ()
1628 , dbGet :: ByteString -> IO (Maybe ByteString)
1 module Database.Tansu ( Tansu
1 module Database.Tansu ( -- * The 'Tansu' monad
2 Tansu
23 , TansuDb
34 , TansuError(..)
5 -- * 'Tansu' Operations
46 , get
57 , getMb
68 , set
2224
2325 type TansuM a = ReaderT TansuDb (ExceptionT TansuError IO) a
2426
27 -- | The 'Tansu' type is a monad which represents some sequence
28 -- of 'get' and 'set' operations. Ideally, backends should
29 -- make some guarantee about the atomicity of a given
30 -- 'Tansu' computation, but you should consult the documentation
31 -- about a given backend to make sure that holds.
2532 newtype Tansu a = Tansu { runTansu :: TansuM a }
2633
2734 instance Functor Tansu where
3441 instance Monad Tansu where
3542 Tansu x >>= f = Tansu (x >>= runTansu . f)
3643
44 -- | Sets the value for a key to a value.
3745 set :: (Serialize k, Serialize v) => k -> v -> Tansu ()
3846 set key val = do
3947 db <- Tansu ask
4048 Tansu $ inBase $ dbSet db (encode key) (encode val)
4149
50 -- | Sets the value for a key to a value. A convenience operator
51 -- that is identical to 'set'.
4252 (=:) :: (Serialize k, Serialize v) => k -> v -> Tansu ()
4353 (=:) = set
4454
55 -- | Gets a value for a given key. The resulting 'Tansu' computation
56 -- will fail if the key is not present in the storage backend.
4557 get :: (Serialize k, Serialize v) => k -> Tansu v
4658 get key = do
4759 result <- getMb key
4961 Just val -> return val
5062 Nothing -> Tansu (raise (KeyNotFound (encode key)))
5163
64 -- | Gets a value for a given key. If the key is not present, this
65 -- computation will return 'Nothing' instead. Other errors, such
66 -- as problems decoding the serialized value or difficulties
67 -- communicating with the storage backend, will still cause the
68 -- 'Tansu' computation to fail.
5269 getMb :: (Serialize k, Serialize v) => k -> Tansu (Maybe v)
5370 getMb key = do
5471 db <- Tansu ask
5976 Right val' -> return (Just val')
6077 Left err -> Tansu (raise (DecodeError err))
6178
79 -- | Given a storage backend and a 'Tansu' computation, execute the
80 -- sequence of 'get' and 'set' commands and produce either the value
81 -- or the error encountered while running the computation.
6282 run :: TansuDb -> Tansu a -> IO (Either TansuError a)
6383 run db (Tansu comp) =
6484 dbRunTransaction db (runExceptionT (runReaderT db comp))
1111 withFilesystemDb "sample.db" sample
1212
1313 putStrLn "Testing ephemeral db"
14 withEphemeralDb sample
14 withNewEphemeralDb sample
1515
1616 sample :: TansuDb -> IO ()
1717 sample db = do