{-# LANGUAGE UndecidableInstances #-}
module Hackage.Security.TUF.Snapshot (
Snapshot(..)
) where
import Control.Monad.Except
import Control.Monad.Reader
import Hackage.Security.JSON
import Hackage.Security.TUF.Header
import Hackage.Security.TUF.FileInfo
import Hackage.Security.TUF.FileMap
import Hackage.Security.TUF.Layout.Repo
import Hackage.Security.TUF.Signed
import qualified Hackage.Security.TUF.FileMap as FileMap
import Hackage.Security.Util.Pretty (pretty)
data Snapshot = Snapshot {
snapshotVersion :: FileVersion
, snapshotExpires :: FileExpires
, snapshotInfoRoot :: FileInfo
, snapshotInfoMirrors :: FileInfo
, snapshotInfoTarGz :: FileInfo
, snapshotInfoTar :: Maybe FileInfo
}
instance HasHeader Snapshot where
fileVersion f x = (\y -> x { snapshotVersion = y }) <$> f (snapshotVersion x)
fileExpires f x = (\y -> x { snapshotExpires = y }) <$> f (snapshotExpires x)
instance MonadReader RepoLayout m => ToJSON m Snapshot where
toJSON Snapshot{..} = do
repoLayout <- ask
mkObject [
("_type" , return $ JSString "Snapshot")
, ("version" , toJSON snapshotVersion)
, ("expires" , toJSON snapshotExpires)
, ("meta" , toJSON (snapshotMeta repoLayout))
]
where
snapshotMeta repoLayout = FileMap.fromList $ [
(pathRoot repoLayout , snapshotInfoRoot)
, (pathMirrors repoLayout , snapshotInfoMirrors)
, (pathIndexTarGz repoLayout , snapshotInfoTarGz)
] ++
[ (pathIndexTar repoLayout , infoTar) | Just infoTar <- [snapshotInfoTar] ]
instance ( MonadReader RepoLayout m
, MonadError DeserializationError m
, ReportSchemaErrors m
) => FromJSON m Snapshot where
fromJSON enc = do
verifyType enc "Snapshot"
repoLayout <- ask
snapshotVersion <- fromJSField enc "version"
snapshotExpires <- fromJSField enc "expires"
snapshotMeta <- fromJSField enc "meta"
let lookupMeta k = case FileMap.lookup k snapshotMeta of
Nothing -> expected ("\"" ++ pretty k ++ "\" entry in .meta object") Nothing
Just v -> pure v
snapshotInfoRoot <- lookupMeta (pathRoot repoLayout)
snapshotInfoMirrors <- lookupMeta (pathMirrors repoLayout)
snapshotInfoTarGz <- lookupMeta (pathIndexTarGz repoLayout)
let snapshotInfoTar = FileMap.lookup (pathIndexTar repoLayout) snapshotMeta
return Snapshot{..}
instance (MonadKeys m, MonadReader RepoLayout m) => FromJSON m (Signed Snapshot) where
fromJSON = signedFromJSON
pathRoot, pathMirrors, pathIndexTarGz, pathIndexTar :: RepoLayout -> TargetPath
pathRoot = TargetPathRepo . repoLayoutRoot
pathMirrors = TargetPathRepo . repoLayoutMirrors
pathIndexTarGz = TargetPathRepo . repoLayoutIndexTarGz
pathIndexTar = TargetPathRepo . repoLayoutIndexTar