diff options
Diffstat (limited to 'app')
-rw-r--r-- | app/Server.hs | 132 |
1 files changed, 117 insertions, 15 deletions
diff --git a/app/Server.hs b/app/Server.hs index ced90d9..9160ba6 100644 --- a/app/Server.hs +++ b/app/Server.hs @@ -14,6 +14,7 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE NamedFieldPuns #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Main where @@ -283,8 +284,8 @@ newtype PackageName = PackageName instance Serialize.Serialize PackageName instance A.ToJSON PackageVersions -type GlobalReferenceMap = HM.HashMap HCE.ExternalId (S.Set GlobalReferences) - +type GlobalReferenceMap = HM.HashMap HCE.ExternalId (S.Set GlobalReferences) +type GlobalIdentifierMap = HCE.Trie Char HCE.ExternalIdentifierInfo data GlobalReferences = GlobalReferences { count :: Int @@ -444,6 +445,16 @@ instance Store.StoreItem GlobalReferenceMap where type KeyArgs GlobalReferenceMap = Proxy GlobalReferenceMap itemKey _ = "globalReferenceMap" +newtype GlobalIdentifierMapWrapper = GlobalIdentifierMapWrapper + { getGlobalIdentifierMap :: GlobalIdentifierMap + } + +instance Store.StoreItem GlobalIdentifierMapWrapper where + toByteString (GlobalIdentifierMapWrapper idMap) = Serialize.encode idMap + fromByteString bs = GlobalIdentifierMapWrapper <$> Serialize.decode bs + type KeyArgs GlobalIdentifierMapWrapper = Proxy GlobalIdentifierMapWrapper + itemKey _ = "globalIdentifierMap" + instance Store.StoreItem [PackageVersions] where toByteString = Serialize.encode fromByteString = Serialize.decode @@ -482,9 +493,9 @@ createStore :: FilePath -> ServerConfig -> IO () createStore storePath config = do packageDirectories <- findDirectories (configPackagesPath config) Store.createStore storePath $ \fileHandle -> do - (errors, packageMap', packagePathMap', packageVersions', globalReferenceMap', index'') <- + (errors, packageMap', packagePathMap', packageVersions', globalReferenceMap', globalIdentifiers', index'') <- foldM - (\(errors, packageMap, packagePathMap, packageVersions, globalReferenceMap, index) path -> do + (\(errors, packageMap, packagePathMap, packageVersions, globalReferenceMap, globalIdentifiers, index) path -> do eitherPackageInfo <- loadPackageInfo config path case eitherPackageInfo of Right (packageInfo, packagePath) -> do @@ -553,6 +564,10 @@ createStore storePath config = do HCE.externalIdOccMap $ packageInfo in HM.unionWith S.union references globalReferenceMap + , globalIdentifiers ++ + filter + isExportedId + (trieValues $ HCE.externalIdInfoMap packageInfo) , index') Left (errorMessage, path') -> return $ @@ -562,8 +577,9 @@ createStore storePath config = do , packagePathMap , packageVersions , globalReferenceMap + , globalIdentifiers , index)) - ([], HM.empty, HM.empty, [], HM.empty, M.empty) + ([], HM.empty, HM.empty, [], HM.empty, [], M.empty) packageDirectories let versions = L.sortOn (T.toLower . (name :: PackageVersions -> T.Text)) . @@ -575,6 +591,15 @@ createStore storePath config = do Store.add packagePathMap' (Proxy :: Proxy PackagePathMap) Store.add versions (Proxy :: Proxy [PackageVersions]) Store.add globalReferenceMap' (Proxy :: Proxy GlobalReferenceMap) + let globalIdentifierMap = + L.foldl' + (\trie exportedId@(HCE.ExternalIdentifierInfo HCE.IdentifierInfo {HCE.demangledOccName = name}) -> + HCE.insertToTrie S.insert (T.unpack name) exportedId trie) + HCE.emptyTrie + globalIdentifiers' + Store.add + (GlobalIdentifierMapWrapper globalIdentifierMap) + (Proxy :: Proxy GlobalIdentifierMapWrapper) Store.add packageMap' (Proxy :: Proxy (HM.HashMap PackageName (M.Map Version HCE.PackageId))) @@ -682,25 +707,30 @@ loadPackages :: -> IO (Maybe ( PackageMap , PackagePathMap , [PackageVersions] - , GlobalReferenceMap)) + , GlobalReferenceMap + , GlobalIdentifierMap)) loadPackages _config mbStore | (Just store) <- mbStore = do let eitherPackagePathMap = Store.lookup (Proxy :: Proxy PackagePathMap) store eitherGlobalReferenceMap = Store.lookup (Proxy :: Proxy GlobalReferenceMap) store + eitherGlobalIdentifierMap = + getGlobalIdentifierMap <$> + Store.lookup (Proxy :: Proxy GlobalIdentifierMapWrapper) store eitherPackageVersions = Store.lookup (Proxy :: Proxy [PackageVersions]) store eitherPackageMap = Store.lookup (Proxy :: Proxy (HM.HashMap PackageName (M.Map Version HCE.PackageId))) store - case (,,,) <$> (PackageMapStore store <$> eitherPackageMap) <*> + case (,,,,) <$> (PackageMapStore store <$> eitherPackageMap) <*> eitherPackagePathMap <*> eitherPackageVersions <*> - eitherGlobalReferenceMap of + eitherGlobalReferenceMap <*> + eitherGlobalIdentifierMap of Right res -> return $ Just res - Left _ -> do + Left e -> do putStrLn "Store lookup errors : " let ignoreRight :: Either a b -> Either a () ignoreRight = second (const ()) @@ -709,7 +739,7 @@ loadPackages _config mbStore [ ignoreRight eitherGlobalReferenceMap , ignoreRight eitherPackageMap , ignoreRight eitherPackageVersions - , ignoreRight eitherGlobalReferenceMap + , ignoreRight eitherGlobalIdentifierMap ] return Nothing loadPackages config _ = do @@ -774,15 +804,36 @@ loadPackages config _ = do in HM.unionWith S.union references hMap) HM.empty loadedPackages + globalIdentifierMap = + L.foldl' + (\trie (packageInfo, _path) -> + let exportedIds :: [HCE.ExternalIdentifierInfo] + exportedIds = + filter isExportedId $ + trieValues $ HCE.externalIdInfoMap packageInfo + in L.foldl + (\trie' exportedId@(HCE.ExternalIdentifierInfo (HCE.IdentifierInfo {HCE.demangledOccName = name})) -> + HCE.insertToTrie + S.insert + (T.unpack name) + exportedId + trie') + trie + exportedIds) + HCE.emptyTrie + loadedPackages packageMapCompacted <- ghcCompact packageMap packagePathMapCompacted <- ghcCompact packagePathMap packageVersionsCompacted <- ghcCompact packageVersions globalReferenceMapCompacted <- ghcCompact globalReferenceMap + globalIdentifierMapCompacted <- ghcCompact globalIdentifierMap return . Just $ ( packageMapCompacted , packagePathMapCompacted , packageVersionsCompacted - , globalReferenceMapCompacted) + , globalReferenceMapCompacted + , globalIdentifierMapCompacted + ) else return Nothing where packageName :: HCE.PackageInfo HCE.CompactModuleInfo -> PackageName @@ -794,6 +845,14 @@ loadPackages config _ = do packageVersion = HCE.version . (HCE.id :: HCE.PackageInfo modInfo -> HCE.PackageId) +trieValues :: HCE.Trie k v -> [v] +trieValues (HCE.Trie values children) = + S.toList values ++ concatMap trieValues (HM.elems children) + +isExportedId :: HCE.ExternalIdentifierInfo -> Bool +isExportedId (HCE.ExternalIdentifierInfo HCE.IdentifierInfo {isExported}) = + isExported + ghcCompact :: forall a. a -> IO a ghcCompact = #if MIN_VERSION_GLASGOW_HASKELL(8,4,3,0) @@ -854,6 +913,7 @@ type API = GetAllPackages :<|> GetReferences :<|> GetIdentifiers :<|> GetGlobalReferences + :<|> GetGlobalIdentifiers type GetAllPackages = "api" :> "packages" :> Get '[JSON] AllPackages @@ -894,6 +954,13 @@ type GetGlobalReferences = "api" :> "globalReferences" :> Capture "externalId" HCE.ExternalId :> Get '[JSON] [GlobalReferences] +type GetGlobalIdentifiers = "api" :> "globalIdentifiers" + :> Capture "query" T.Text + :> QueryParam "page" Int + :> QueryParam "per_page" Int + :> Get '[JSON] (Headers '[Header "Link" T.Text,Header "X-Total-Count" Int] + [HCE.ExternalIdentifierInfo]) + instance AllCTRender '[ JSON] AllPackages where handleAcceptH _ _ (AllPackages bytestring) = Just ("application/json", bytestring) @@ -941,6 +1008,7 @@ data Environment = Environment , envPackageMap :: !PackageMap , envPackageVersions :: !AllPackages , envGlobalReferenceMap :: !GlobalReferenceMap + , envGlobalIdentifierMap :: !GlobalIdentifierMap , envConfig :: !ServerConfig } @@ -1360,7 +1428,39 @@ findIdentifiers packageId query mbPage mbPerPage = respond $ S.toList $ HCE.match (T.unpack $ T.drop 4 query) trie - Left _ -> respond [] + Left _ -> respond [] + +findGlobalIdentifiers :: + T.Text + -> Maybe Int + -> Maybe Int + -> ReaderT Environment IO (Headers '[ Header "Link" T.Text, Header "X-Total-Count" Int] [HCE.ExternalIdentifierInfo]) +findGlobalIdentifiers query mbPage mbPerPage = do + globalIdentifierMap <- asks envGlobalIdentifierMap + let maxItems = 500 + let identifiers + | T.length query > 0 = + L.take maxItems $ + S.toList $ HCE.match (T.unpack query) globalIdentifierMap + | otherwise = [] + (paginatedIdentifiers, page, perPage, totalCount) <- + paginateItems mbPage mbPerPage identifiers + let url = + T.append "/" $ + toUrlPiece $ + safeLink + (Proxy :: Proxy API) + (Proxy :: Proxy GetGlobalIdentifiers) + query + Nothing + Nothing + linkHeader = buildLinkHeader url paginatedIdentifiers page perPage + addHeaders :: + forall a. + a + -> Headers '[ Header "Link" T.Text, Header "X-Total-Count" Int] a + addHeaders = addHeader linkHeader . addHeader totalCount + return . addHeaders . paginatedItems $ paginatedIdentifiers paginateItems :: Maybe Int @@ -1624,7 +1724,8 @@ server env = getExpressions :<|> getReferences :<|> findIdentifiers :<|> - getGlobalReferences) + getGlobalReferences :<|> + findGlobalIdentifiers) where toServantHandler :: ReaderT Environment IO a -> Handler a toServantHandler ma = Handler . ExceptT . try . runReaderT ma $ env @@ -1658,7 +1759,7 @@ main = do Nothing -> return Nothing packages <- loadPackages config mbStore case packages of - Just (packageMap, packagePathMap, packageVersions, globalReferenceMap) -> do + Just (packageMap, packagePathMap, packageVersions, globalReferenceMap, globalIdentifierMap) -> do loggerSet <- case configLog config of HCE.ToFile logfile -> newFileLoggerSet defaultBufSize logfile @@ -1666,7 +1767,7 @@ main = do loggerMiddleware <- liftIO $ mkRequestLogger - def {outputFormat = Detailed True, destination = Logger loggerSet} + def {outputFormat = Detailed True, destination = Logger loggerSet} let staticFilePrefix = configStaticFilesUrlPrefix config mbJsDistPath = configJsDistDirectory config environment = @@ -1675,6 +1776,7 @@ main = do packageMap (AllPackages . A.encode $ packageVersions) globalReferenceMap + globalIdentifierMap config static = if configServeStaticFiles config |