module Database.VCache.Path
    ( vcacheSubdir
    , vcacheSubdirM
    ) where

import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import Database.VCache.Types

maxPathLen :: Int
maxPathLen = 511  -- key size limit from LMDB 0.9.10

-- | VCache implements a simplified filesystem metaphor. Developers 
-- can assign a distinct prefix for all PVars created by the VCache,
-- thus modeling namespaces or subdirectories. Assuming the normal 
-- advice of opening the VCache only once in the main module, these
-- prefixes enable transparent, modular decomposition of a VCache 
-- application without risk of name collisions.
--
-- VCache is simplistic about this: a prefix is appended directly.
-- If developers use subdir "foo" followed by "bar", the result is 
-- the same as "foobar". Separators are left to local conventions.
-- Consider "foo/" and "bar/" to model filesystem subdirectories. 
--                 
-- Paths have a limited maximum size of ~500 bytes, including the
-- final PVar name. A runtime error may be generated for oversized
-- paths. In practice, this should not be an issue. 
--
-- Usage Note: Subdirectories allow developers to control risk of
-- namespace collisions between modules or plugins. But they are not
-- intended for domain data! Avoid dynamically creating directories
-- or named PVars based on runtime data. It's better to push most
-- domain logic and schema into the PVar layer, which is subject to
-- rich type safety, GC, potential versioning, and other benefits.
--
vcacheSubdir :: ByteString -> VCache -> VCache
vcacheSubdir p (VCache vs d) = 
    let d' = subdir d p in
    if (BS.length d' > maxPathLen) 
        then error ("VCache path too long: " ++ show d')
        else (VCache vs d')

-- | as vcacheSubdir, but returns Nothing if the path is too large.
vcacheSubdirM :: ByteString -> VCache -> Maybe VCache
vcacheSubdirM p (VCache vs d) =
    let d' = subdir d p in
    if (BS.length d' > maxPathLen)
        then Nothing
        else Just (VCache vs d')

subdir :: ByteString -> ByteString -> ByteString
subdir = BS.append
{-# INLINE subdir #-}