mirror of
https://github.com/input-output-hk/foliage.git
synced 2024-12-02 07:54:45 +03:00
186 lines
5.9 KiB
Haskell
186 lines
5.9 KiB
Haskell
{-# LANGUAGE DeriveAnyClass #-}
|
|
{-# LANGUAGE DeriveGeneric #-}
|
|
{-# LANGUAGE DerivingVia #-}
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
{-# LANGUAGE PatternSynonyms #-}
|
|
|
|
module Foliage.Meta
|
|
( PackageMeta (PackageMeta),
|
|
readPackageMeta,
|
|
writePackageMeta,
|
|
PackageVersionMeta (PackageVersionMeta),
|
|
packageVersionTimestamp,
|
|
packageVersionSource,
|
|
packageVersionRevisions,
|
|
packageVersionForce,
|
|
readPackageVersionMeta,
|
|
writePackageVersionMeta,
|
|
RevisionMeta (RevisionMeta),
|
|
revisionTimestamp,
|
|
revisionNumber,
|
|
PackageVersionSource,
|
|
pattern TarballSource,
|
|
UTCTime,
|
|
latestRevisionNumber,
|
|
consolidateRanges,
|
|
)
|
|
where
|
|
|
|
import Control.Monad (void)
|
|
import Data.Maybe (fromMaybe)
|
|
import Data.Text qualified as T
|
|
import Data.Time.LocalTime (utc, utcToZonedTime, zonedTimeToUTC)
|
|
import Development.Shake.Classes
|
|
( Binary,
|
|
Hashable,
|
|
NFData,
|
|
)
|
|
import Distribution.Parsec (simpleParsec)
|
|
import Distribution.Pretty
|
|
import Distribution.Types.Orphans ()
|
|
import Distribution.Types.Version (Version)
|
|
import Distribution.Types.VersionRange
|
|
( VersionRange,
|
|
anyVersion,
|
|
intersectVersionRanges,
|
|
notThisVersion,
|
|
)
|
|
import Distribution.Version
|
|
( isAnyVersion,
|
|
isNoVersion,
|
|
simplifyVersionRange,
|
|
)
|
|
import Foliage.Time (UTCTime)
|
|
import GHC.Generics (Generic)
|
|
import Toml (TomlCodec, (.=))
|
|
import Toml qualified
|
|
|
|
newtype PackageMeta = PackageMeta
|
|
{ packageMetaEntries :: [PackageMetaEntry]
|
|
}
|
|
deriving (Show, Eq, Generic)
|
|
deriving anyclass (Binary, Hashable, NFData)
|
|
|
|
data PackageMetaEntry = PackageMetaEntry
|
|
{ packageMetaEntryTimestamp :: UTCTime,
|
|
packageMetaEntryPreferred :: [VersionRange],
|
|
packageMetaEntryDeprecated :: [Version]
|
|
}
|
|
deriving (Show, Eq, Generic)
|
|
deriving anyclass (Binary, Hashable, NFData)
|
|
|
|
readPackageMeta :: FilePath -> IO PackageMeta
|
|
readPackageMeta = Toml.decodeFile packageMetaCodec
|
|
|
|
writePackageMeta :: FilePath -> PackageMeta -> IO ()
|
|
writePackageMeta fp a = void $ Toml.encodeToFile packageMetaCodec fp a
|
|
|
|
packageMetaCodec :: TomlCodec PackageMeta
|
|
packageMetaCodec =
|
|
PackageMeta
|
|
<$> Toml.list packageMetaEntryCodec "entries" .= packageMetaEntries
|
|
|
|
packageMetaEntryCodec :: TomlCodec PackageMetaEntry
|
|
packageMetaEntryCodec =
|
|
PackageMetaEntry
|
|
<$> timeCodec "timestamp" .= packageMetaEntryTimestamp
|
|
<*> Toml.arrayOf _VersionRange "preferred-versions" .= packageMetaEntryPreferred
|
|
<*> Toml.arrayOf _Version "deprecated-versions" .= packageMetaEntryDeprecated
|
|
|
|
_Version :: Toml.TomlBiMap Version Toml.AnyValue
|
|
_Version = Toml._TextBy showVersion parseVersion
|
|
where
|
|
showVersion = T.pack . prettyShow
|
|
parseVersion t = case simpleParsec (T.unpack t) of
|
|
Nothing -> Left $ T.pack $ "unable to parse version" ++ T.unpack t
|
|
Just v -> Right v
|
|
|
|
_VersionRange :: Toml.TomlBiMap VersionRange Toml.AnyValue
|
|
_VersionRange = Toml._TextBy showVersion parseVersion
|
|
where
|
|
showVersion = T.pack . prettyShow
|
|
parseVersion t = case simpleParsec (T.unpack t) of
|
|
Nothing -> Left $ T.pack $ "unable to parse version" ++ T.unpack t
|
|
Just v -> Right v
|
|
|
|
data PackageVersionSource
|
|
= TarballSource String (Maybe String)
|
|
deriving (Show, Eq, Generic)
|
|
deriving anyclass (Binary, Hashable, NFData)
|
|
|
|
packageSourceCodec :: TomlCodec PackageVersionSource
|
|
packageSourceCodec =
|
|
Toml.dimatch matchTarballSource (uncurry TarballSource) tarballSourceCodec
|
|
|
|
tarballSourceCodec :: TomlCodec (String, Maybe String)
|
|
tarballSourceCodec =
|
|
Toml.pair
|
|
(Toml.string "url")
|
|
(Toml.dioptional $ Toml.string "subdir")
|
|
|
|
matchTarballSource :: PackageVersionSource -> Maybe (String, Maybe String)
|
|
matchTarballSource (TarballSource url mSubdir) = Just (url, mSubdir)
|
|
|
|
data PackageVersionMeta = PackageVersionMeta
|
|
{ -- | timestamp
|
|
packageVersionTimestamp :: Maybe UTCTime,
|
|
-- | source parameters
|
|
packageVersionSource :: PackageVersionSource,
|
|
-- | revisions
|
|
packageVersionRevisions :: [RevisionMeta],
|
|
-- | force version
|
|
packageVersionForce :: Bool
|
|
}
|
|
deriving (Show, Eq, Generic)
|
|
deriving anyclass (Binary, Hashable, NFData)
|
|
|
|
sourceMetaCodec :: TomlCodec PackageVersionMeta
|
|
sourceMetaCodec =
|
|
PackageVersionMeta
|
|
<$> Toml.dioptional (timeCodec "timestamp") .= packageVersionTimestamp
|
|
<*> packageSourceCodec .= packageVersionSource
|
|
<*> Toml.list revisionMetaCodec "revisions" .= packageVersionRevisions
|
|
<*> withDefault False (Toml.bool "force-version") .= packageVersionForce
|
|
|
|
readPackageVersionMeta :: FilePath -> IO PackageVersionMeta
|
|
readPackageVersionMeta = Toml.decodeFile sourceMetaCodec
|
|
|
|
writePackageVersionMeta :: FilePath -> PackageVersionMeta -> IO ()
|
|
writePackageVersionMeta fp a = void $ Toml.encodeToFile sourceMetaCodec fp a
|
|
|
|
data RevisionMeta = RevisionMeta
|
|
{ revisionTimestamp :: UTCTime,
|
|
revisionNumber :: Int
|
|
}
|
|
deriving (Show, Eq, Generic)
|
|
deriving anyclass (Binary, Hashable, NFData)
|
|
|
|
revisionMetaCodec :: TomlCodec RevisionMeta
|
|
revisionMetaCodec =
|
|
RevisionMeta
|
|
<$> timeCodec "timestamp" .= revisionTimestamp
|
|
<*> Toml.int "number" .= revisionNumber
|
|
|
|
timeCodec :: Toml.Key -> TomlCodec UTCTime
|
|
timeCodec key = Toml.dimap (utcToZonedTime utc) zonedTimeToUTC $ Toml.zonedTime key
|
|
|
|
latestRevisionNumber :: PackageVersionMeta -> Maybe Int
|
|
latestRevisionNumber sm =
|
|
if null (packageVersionRevisions sm)
|
|
then Nothing
|
|
else Just $ maximum $ map revisionNumber (packageVersionRevisions sm)
|
|
|
|
withDefault :: Eq a => a -> TomlCodec a -> TomlCodec a
|
|
withDefault d c = (fromMaybe d <$> Toml.dioptional c) .= f
|
|
where
|
|
f a = if a == d then Nothing else Just a
|
|
|
|
-- | copied from hackage-server
|
|
consolidateRanges :: PackageMetaEntry -> Maybe VersionRange
|
|
consolidateRanges PackageMetaEntry {packageMetaEntryPreferred, packageMetaEntryDeprecated} =
|
|
if isAnyVersion range || isNoVersion range then Nothing else Just range
|
|
where
|
|
range =
|
|
simplifyVersionRange $
|
|
foldr intersectVersionRanges anyVersion (map notThisVersion packageMetaEntryDeprecated ++ packageMetaEntryPreferred)
|