{-# LANGUAGE QuasiQuotes #-} module Handler.Hoogle where import Control.DeepSeq (NFData(..)) import Control.DeepSeq.Generics (genericRnf) import Data.Data (Data) import Data.Text.Read (decimal) import qualified Hoogle import Import import Text.Blaze.Html (preEscapedToHtml) import Stackage.Database import qualified Data.Text as T getHoogleDB :: SnapName -> Handler (Maybe FilePath) getHoogleDB name = track "Handler.Hoogle.getHoogleDB" $ do app <- getYesod liftIO $ appGetHoogleDB app name getHoogleR :: SnapName -> Handler Html getHoogleR name = track "Handler.Hoogle.getHoogleR" $ do Entity _ snapshot <- lookupSnapshot name >>= maybe notFound return mquery <- lookupGetParam "q" mPackageName <- lookupGetParam "package" mpage <- lookupGetParam "page" exact <- isJust <$> lookupGetParam "exact" mresults' <- lookupGetParam "results" let count' = case decimal <$> mresults' of Just (Right (i, "")) -> min perPage i _ -> perPage page = case decimal <$> mpage of Just (Right (i, "")) -> i _ -> 1 offset = (page - 1) * perPage mdatabasePath <- getHoogleDB name dbPath <- maybe (hoogleDatabaseNotAvailableFor name) return mdatabasePath -- Avoid concurrent Hoogle queries, see -- https://github.com/fpco/stackage-server/issues/172 lock <- appHoogleLock <$> getYesod urlRender <- getUrlRender HoogleQueryOutput results mtotalCount <- case mquery of Just query -> do let input = HoogleQueryInput { hqiQueryInput = case mPackageName of Nothing -> query Just pn -> concat ["+", pn, " ", query] , hqiLimitTo = count' , hqiOffsetBy = offset , hqiExact = exact } liftIO $ withMVar lock $ const $ Hoogle.withDatabase dbPath -- NB! I got a segfault when I didn't force with $! $ \db -> return $! runHoogleQuery urlRender name db input Nothing -> return $ HoogleQueryOutput [] Nothing let queryText = fromMaybe "" mquery pageLink p = (SnapshotR name HoogleR , (if exact then (("exact", "true"):) else id) $ maybe id (\q' -> (("q", q'):)) mquery [("page", tshow p)]) snapshotLink = SnapshotR name StackageHomeR hoogleForm = $(widgetFile "hoogle-form") defaultLayout $ do setTitle "Hoogle Search" $(widgetFile "hoogle") getHoogleDatabaseR :: SnapName -> Handler Html getHoogleDatabaseR name = track "Handler.Hoogle.getHoogleDatabaseR" $ do mdatabasePath <- getHoogleDB name case mdatabasePath of Nothing -> hoogleDatabaseNotAvailableFor name Just path -> sendFile "application/octet-stream" path hoogleDatabaseNotAvailableFor :: SnapName -> Handler a hoogleDatabaseNotAvailableFor name = track "Handler.Hoogle.hoogleDatabaseNotAvailableFor" $ do (>>= sendResponse) $ defaultLayout $ do setTitle "Hoogle database not available" [whamlet|