module SlackBuilder.Package ( DownloadPlaceholder(..) , Download(..) , DownloadTemplate(..) , PackageInfo(..) , Maintainer(..) , Updater(..) , infoTemplate , renderDownloadWithVersion ) where import Data.List.NonEmpty (NonEmpty(..)) import Data.Text (Text) import qualified Data.Text as Text import Text.URI (URI(..)) import qualified Text.URI as URI import Crypto.Hash (Digest, MD5) import GHC.Records (HasField(..)) import System.FilePath (takeBaseName) import Data.List (partition) import SlackBuilder.Trans import Control.Monad.Catch (MonadThrow) -- | Download URI with the MD5 checksum of the target. data Download = Download { download :: URI , md5sum :: Digest MD5 , is64 :: Bool } deriving (Eq, Show) -- | Data used to generate an .info file. data PackageInfo = PackageInfo { path :: FilePath , version :: Text , homepage :: Text , requires :: [Text] , maintainer :: Maintainer } deriving (Eq, Show) -- | Package maintainer information. data Maintainer = Maintainer { name :: Text , email :: Text } deriving (Eq, Show) -- | Appears in the download URI template and specifies which part of the URI -- should be replaced with the package version. data DownloadPlaceholder = StaticPlaceholder Text | VersionPlaceholder deriving Eq instance Show DownloadPlaceholder where show (StaticPlaceholder staticPlaceholder) = Text.unpack staticPlaceholder show VersionPlaceholder = "{version}" -- | List of URI components, including version placeholders. newtype DownloadTemplate = DownloadTemplate (NonEmpty DownloadPlaceholder) deriving Eq instance Show DownloadTemplate where show (DownloadTemplate components) = concatMap show components -- | Replaces placeholders in the URL template with the given version. renderDownloadWithVersion :: MonadThrow m => DownloadTemplate -> Text -> m URI renderDownloadWithVersion (DownloadTemplate components) version = URI.mkURI $ foldr f "" components where f (StaticPlaceholder staticPlaceholder) accumulator = accumulator <> staticPlaceholder f VersionPlaceholder accumulator = accumulator <> version -- | Function used to get the latest version of a source. data Updater = Updater (SlackBuilderT (Maybe Text)) DownloadTemplate packageName :: PackageInfo -> Text packageName PackageInfo{ path } = Text.pack $ takeBaseName path infoTemplate :: PackageInfo -> [Download] -> Text infoTemplate package downloads = let (downloads64, downloads32) = partition (getField @"is64") downloads (download32, md5sum32, download64, md5sum64) = downloadEntries downloads64 downloads32 in Text.unlines [ "PRGNAM=\"" <> packageName package <> "\"" , "VERSION=\"" <> getField @"version" package <> "\"" , "HOMEPAGE=\"" <> getField @"homepage" package <> "\"" , "DOWNLOAD=\"" <> download32 <> "\"" , "MD5SUM=\"" <> md5sum32 <> "\"" , "DOWNLOAD_x86_64=\"" <> download64 <> "\"" , "MD5SUM_x86_64=\"" <> md5sum64 <> "\"" , "REQUIRES=\"" <> Text.unwords (getField @"requires" package) <> "\"" , "MAINTAINER=\"" <> getField @"name" (getField @"maintainer" package) <> "\"" , "EMAIL=\"" <> getField @"email" (getField @"maintainer" package) <> "\"" ] downloadEntries :: [Download] -> [Download] -> (Text, Text, Text, Text) downloadEntries downloads64 downloads32 = let download32 = if null downloads32 && not (null downloads64) then "UNSUPPORTED" else Text.intercalate " \\\n " $ URI.render . getField @"download" <$> downloads32 md5sum32 = Text.intercalate " \\\n " $ Text.pack . show . getField @"md5sum" <$> downloads32 download64 = Text.intercalate " \\\n " $ URI.render . getField @"download" <$> downloads64 md5sum64 = Text.intercalate " \\\n " $ Text.pack . show . getField @"md5sum" <$> downloads64 in (download32, md5sum32, download64, md5sum64)