Skip to content

Commit 9bb6d14

Browse files
Avoid duplicate package decodes for deprecation badges (#492)
1 parent ccd29f6 commit 9bb6d14

1 file changed

Lines changed: 72 additions & 13 deletions

File tree

src/Handler/Packages.hs

Lines changed: 72 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,13 @@ getPackageAvailableVersionsR (PathPackageName pkgName) =
8181
getPackageVersionR :: PathPackageName -> PathVersion -> Handler Html
8282
getPackageVersionR (PathPackageName pkgName) (PathVersion version) =
8383
cacheHtmlConditional $
84-
findPackageWithLatest pkgName version $ \pkg@D.Package{..} latestPkg -> do
84+
findPackageWithDeprecation pkgName version $ \pkg@D.Package{..} deprecated -> do
8585
moduleList <- renderModuleList pkg
8686
ereadme <- tryGetReadme pkg
8787
let cacheStatus = either (const NotOkToCache) (const OkToCache) ereadme
8888
content <- defaultLayout $ do
8989
setTitle (toHtml (runPackageName pkgName))
9090
let dependencies = bowerDependencies pkgMeta
91-
let deprecated = isDeprecated latestPkg
9291
$(widgetFile "packageVersion")
9392
return (cacheStatus, content)
9493

@@ -159,15 +158,14 @@ getPackageVersionDocsR (PathPackageName pkgName) (PathVersion version) =
159158

160159
getPackageVersionModuleDocsR :: PathPackageName -> PathVersion -> Text -> Handler Html
161160
getPackageVersionModuleDocsR (PathPackageName pkgName) (PathVersion version) mnString =
162-
cacheHtml $ findPackageWithLatest pkgName version $ \pkg@D.Package{..} latestPkg -> do
161+
cacheHtml $ findPackageWithDeprecation pkgName version $ \pkg@D.Package{..} deprecated -> do
163162
moduleList <- renderModuleList pkg
164163
mhtmlDocs <- renderHtmlDocs pkg mnString
165164
case mhtmlDocs of
166165
Nothing -> notFound
167166
Just htmlDocs ->
168167
defaultLayout $ do
169168
let mn = P.moduleNameFromString mnString
170-
let deprecated = isDeprecated latestPkg
171169
setTitle (toHtml (mnString <> " - " <> runPackageName pkgName))
172170
$(widgetFile "packageVersionModuleDocs")
173171

@@ -211,21 +209,82 @@ findPackage pkgName version cont = do
211209
Left NoSuchPackage -> packageNotFound pkgName
212210
Left NoSuchPackageVersion -> packageVersionNotFound pkgName version
213211

214-
findPackageWithLatest ::
212+
findPackageWithDeprecation ::
215213
PackageName ->
216214
Version ->
217-
(D.VerifiedPackage -> D.VerifiedPackage -> Handler r) ->
215+
(D.VerifiedPackage -> Bool -> Handler r) ->
218216
Handler r
219-
findPackageWithLatest pkgName version cont = do
217+
findPackageWithDeprecation pkgName version cont = do
220218
findPackage pkgName version $ \pkg -> do
221219
latestVersion <- fromMaybe version <$> getLatestVersionFor pkgName
222-
-- Avoid decoding the same package file twice when the requested version
223-
-- is the latest one; for large packages a decode is expensive.
224-
latestPkg <-
220+
deprecated <-
225221
if latestVersion == version
226-
then return pkg
227-
else fromMaybe pkg . hush <$> lookupPackage pkgName latestVersion
228-
cont pkg latestPkg
222+
then return (isDeprecated pkg)
223+
else getLatestDeprecation pkgName latestVersion (isDeprecated pkg)
224+
cont pkg deprecated
225+
226+
data DeprecationMarker = DeprecationMarker
227+
{ markerLatestVersion :: Text
228+
, markerDeprecated :: Bool
229+
}
230+
deriving (Show, Eq, Generic)
231+
232+
instance ToJSON DeprecationMarker where
233+
toJSON DeprecationMarker{..} =
234+
object
235+
[ "latest" .= markerLatestVersion
236+
, "deprecated" .= markerDeprecated
237+
]
238+
239+
instance FromJSON DeprecationMarker where
240+
parseJSON = Aeson.withObject "DeprecationMarker" $ \obj ->
241+
DeprecationMarker
242+
<$> obj .: "latest"
243+
<*> obj .: "deprecated"
244+
245+
getLatestDeprecation :: PackageName -> Version -> Bool -> Handler Bool
246+
getLatestDeprecation pkgName latestVersion fallbackDeprecated = do
247+
path <- deprecationMarkerFileFor pkgName
248+
emarker <- tryAny (liftIO (readFileMay path))
249+
case emarker of
250+
Right mmarker ->
251+
case mmarker >>= Aeson.decodeStrict of
252+
Just DeprecationMarker{..}
253+
| markerLatestVersion == latestVersionText ->
254+
return markerDeprecated
255+
_ ->
256+
recompute path
257+
_ -> recompute path
258+
where
259+
latestVersionText = pack (showVersion latestVersion)
260+
261+
recompute path = do
262+
latestPkg <- hush <$> lookupPackage pkgName latestVersion
263+
let deprecated = maybe fallbackDeprecated isDeprecated latestPkg
264+
writeDeprecationMarker path deprecated
265+
return deprecated
266+
267+
writeDeprecationMarker path deprecated = do
268+
let marker =
269+
DeprecationMarker
270+
{ markerLatestVersion = latestVersionText
271+
, markerDeprecated = deprecated
272+
}
273+
result <- tryAny (writeFileWithParents path (toStrict (Aeson.encode marker)))
274+
case result of
275+
Right () -> return ()
276+
Left err ->
277+
$logError
278+
( "Failed to write deprecation marker for "
279+
<> runPackageName pkgName <> ": " <> tshow err
280+
)
281+
282+
deprecationMarkerFileFor :: PackageName -> Handler FilePath
283+
deprecationMarkerFileFor pkgName = do
284+
dir <- getDataDir
285+
return $
286+
dir </> "cache" </> "packages" </>
287+
unpack (runPackageName pkgName) </> "deprecation-marker.json"
229288

230289
packageNotFound :: PackageName -> Handler a
231290
packageNotFound pkgName = do

0 commit comments

Comments
 (0)