Resolver.hs (4574B)
1 module ContentStore.Resolver 2 ( ObjectResolver(..) 3 , filesystemResolver 4 , cachedFilesystemResolver 5 , resolveObjectByHash 6 , resolveManifest 7 , resolveTree 8 ) where 9 10 import ContentStore.Alias 11 import ContentStore.Arboricx 12 import ContentStore.Filesystem 13 import ContentStore.Object 14 import Module.Manifest 15 import Runtime (Node(..), T, deserializeNode) 16 import qualified Runtime 17 18 import Data.ByteString (ByteString) 19 import Data.IORef (IORef, newIORef, readIORef, atomicModifyIORef') 20 import qualified Data.Map as Map 21 import qualified Data.Text as T 22 23 -- | Object and alias resolution capability. Module/import code should depend on 24 -- this boundary rather than on a concrete filesystem store. Future resolvers can 25 -- add trusted remotes, registries, or caches. Implementations must authenticate 26 -- their typed manifest/tree callbacks; callers cannot recover the original bytes 27 -- from a decoded result. Filesystem callbacks verify before decode/cache insertion. 28 -- Cached verified bytes are immutable snapshots, not live filesystem views. 29 data ObjectResolver = ObjectResolver 30 { resolverAlias :: AliasKind -> T.Text -> IO (Maybe ObjectRef) 31 , resolverObject :: ObjectRef -> IO (Maybe ByteString) 32 , resolverManifest :: ObjectHash -> IO (Maybe ModuleManifest) 33 , resolverTree :: ObjectHash -> IO (Maybe T) 34 } 35 36 filesystemResolver :: StorePath -> ObjectResolver 37 filesystemResolver store = resolver 38 where 39 resolver = ObjectResolver 40 { resolverAlias = readAlias store 41 , resolverObject = \ref -> getVerifiedObject store (Domain (objectRefKind ref)) (objectRefHash ref) 42 , resolverManifest = resolveManifestFromObjects resolver 43 , resolverTree = resolveTreeFromObjects resolver 44 } 45 46 cachedFilesystemResolver :: StorePath -> IO ObjectResolver 47 cachedFilesystemResolver store = do 48 objectCache <- newIORef Map.empty 49 manifestCache <- newIORef Map.empty 50 treeCache <- newIORef Map.empty 51 let resolver = ObjectResolver 52 { resolverAlias = readAlias store 53 , resolverObject = cachedLookup objectCache (\ref -> getVerifiedObject store (Domain (objectRefKind ref)) (objectRefHash ref)) 54 , resolverManifest = cachedLookup manifestCache (resolveManifestFromObjects resolver) 55 , resolverTree = cachedLookup treeCache (resolveTreeFromObjects resolver) 56 } 57 return resolver 58 where 59 cachedLookup :: Ord k => IORef (Map.Map k v) -> (k -> IO v) -> k -> IO v 60 cachedLookup ref load key = do 61 cache <- readIORef ref 62 case Map.lookup key cache of 63 Just value -> return value 64 Nothing -> do 65 value <- load key 66 atomicModifyIORef' ref (\m -> (Map.insert key value m, ())) 67 return value 68 69 resolveObjectByHash :: ObjectResolver -> T.Text -> ObjectHash -> IO (Maybe ByteString) 70 resolveObjectByHash resolver kind h = do 71 either fail pure (validateObjectHash h) 72 payload <- resolverObject resolver (ObjectRef kind h) 73 case payload of 74 Nothing -> pure Nothing 75 Just bytes -> do 76 either fail pure (verifyObjectBytes (Domain kind) h bytes) 77 pure (Just bytes) 78 79 resolveManifest :: ObjectResolver -> ObjectHash -> IO (Maybe ModuleManifest) 80 resolveManifest = resolverManifest 81 82 resolveManifestFromObjects :: ObjectResolver -> ObjectHash -> IO (Maybe ModuleManifest) 83 resolveManifestFromObjects resolver h = do 84 mBytes <- resolveObjectByHash resolver (unDomain manifestDomain) h 85 case mBytes of 86 Nothing -> return Nothing 87 Just bytes -> case decodeManifest bytes of 88 Left err -> fail $ "invalid module manifest " ++ T.unpack h ++ ": " ++ err 89 Right manifest -> return (Just manifest) 90 91 resolveTree :: ObjectResolver -> ObjectHash -> IO (Maybe T) 92 resolveTree = resolverTree 93 94 resolveTreeFromObjects :: ObjectResolver -> ObjectHash -> IO (Maybe T) 95 resolveTreeFromObjects resolver h = do 96 mNode <- resolveNode resolver h 97 case mNode of 98 Nothing -> return Nothing 99 Just node -> hydrate node 100 where 101 resolveNode r nodeHash = do 102 mBytes <- resolveObjectByHash r (unDomain merkleNodeDomain) nodeHash 103 case mBytes of 104 Nothing -> return Nothing 105 Just bytes -> return (Just (deserializeNode bytes)) 106 107 hydrate NLeaf = return (Just Runtime.Leaf) 108 hydrate (NStem child) = fmap Runtime.Stem <$> hydrateHash child 109 hydrate (NFork left right) = do 110 l <- hydrateHash left 111 r <- hydrateHash right 112 return $ Runtime.Fork <$> l <*> r 113 114 hydrateHash nodeHash = do 115 mChild <- resolveNode resolver nodeHash 116 case mChild of 117 Nothing -> return Nothing 118 Just child -> hydrate child