{- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/. -} module Main ( main ) where import Control.Monad.Catch (MonadThrow(..), handle) import Control.Monad.IO.Class (MonadIO(..)) import qualified Data.Map as Map import Options.Applicative (execParser) import SlackBuilder.CommandLine import SlackBuilder.Config import SlackBuilder.Trans import SlackBuilder.LatestVersionCheck import SlackBuilder.Update import qualified Toml import Data.Text (Text) import qualified Data.Text.IO as Text import Control.Monad.Trans.Reader (ReaderT(..), asks) import SlackBuilder.Package (PackageDescription(..)) import qualified SlackBuilder.Package as Package import Text.URI.QQ (uri) import Data.Foldable (find, traverse_) import GHC.Records (HasField(..)) import System.Console.ANSI ( setSGR , SGR(..) , ColorIntensity(..) , Color(..) , ConsoleLayer(..) ) import Data.Maybe (fromJust, mapMaybe) import qualified Text.URI as URI findUpdatable :: Text -> [PackageSettings] -> DownloaderSettings findUpdatable packageName = getField @"downloader" . fromJust . find ((== packageName) . getField @"name" . getField @"downloader") githubAutoUpdatables :: [PackageSettings] -> [PackageDescription] githubAutoUpdatables = mapMaybe go where go PackageSettings{ downloader = setting, downloaders } = do latest' <- packageUpdaterFromSettings setting pure $ PackageDescription { latest = latest' , name = getField @"name" setting , downloaders = Map.fromList $ mapMaybe forDownloader downloaders } forDownloader downloaderSettings@DownloaderSettings{ name } = (name,) <$> packageUpdaterFromSettings downloaderSettings packageUpdaterFromSettings :: DownloaderSettings -> Maybe Package.Updater packageUpdaterFromSettings DownloaderSettings{..} = do getVersion' <- getVersionSettings detectLatest' <- detectLatestSettings Just Package.Updater { detectLatest = detectLatest' , getVersion = getVersion' , is64 = is64 } where detectLatestSettings | Just githubSettings <- github = let ghArguments = uncurry PackageOwner githubSettings in Just $ latestGitHub ghArguments version | Just packagistSettings <- packagist = let packagistArguments = uncurry PackageOwner packagistSettings in Just $ latestPackagist packagistArguments | otherwise = Nothing getVersionSettings | Just template' <- template = Just $ repackageWithTemplate repackage $ Package.DownloadTemplate template' | Just CloneSettings{..} <- clone = flip cloneFromGit tagTemplate <$> URI.mkURI remote | otherwise = Nothing autoUpdatable :: [PackageSettings] -> [PackageDescription] autoUpdatable packageSettings = githubAutoUpdatables packageSettings ++ [ PackageDescription { latest = let setting = findUpdatable "webex" packageSettings textArguments = uncurry TextArguments $ fromJust $ getField @"text" setting template = Package.DownloadTemplate $ fromJust $ getField @"template" setting in Package.Updater { detectLatest = latestText textArguments (getField @"version" setting) , getVersion = repackageWithTemplate (getField @"repackage" setting) template , is64 = getField @"is64" setting } , name = "webex" , downloaders = mempty } , PackageDescription { latest = let setting = findUpdatable "dmd" packageSettings textArguments = uncurry TextArguments $ fromJust $ getField @"text" setting template = Package.DownloadTemplate $ fromJust $ getField @"template" setting in Package.Updater { detectLatest = latestText textArguments (getField @"version" setting) , getVersion = repackageWithTemplate (getField @"repackage" setting) template , is64 = getField @"is64" setting } , name = "dmd" , downloaders = mempty } , PackageDescription { latest = let setting = findUpdatable "d-tools" packageSettings textArguments = uncurry TextArguments $ fromJust $ getField @"text" setting template = Package.DownloadTemplate $ fromJust $ getField @"template" setting in Package.Updater { detectLatest = latestText textArguments (getField @"version" setting) , getVersion = repackageWithTemplate (getField @"repackage" setting) template , is64 = getField @"is64" setting } , name = "d-tools" , downloaders = let dubArguments = PackageOwner{ owner = "dlang", name = "dub" } dscannerArguments = PackageOwner{ owner = "dlang-community", name = "D-Scanner" } dcdArguments = PackageOwner{ owner = "dlang-community", name = "DCD" } latestDub = Package.Updater { detectLatest = latestGitHub dubArguments "(v)\\." , getVersion = reuploadWithTemplate dubTemplate , is64 = False } latestDscanner = Package.Updater { detectLatest = latestGitHub dscannerArguments "(v)\\." , getVersion = cloneFromGit dscannerURI "v" , is64 = False } dcdURI = [uri|https://github.com/dlang-community/DCD.git|] latestDcd = Package.Updater { detectLatest = latestGitHub dcdArguments "(v)\\." , getVersion = cloneFromGit dcdURI "v" , is64 = False } dubTemplate = Package.DownloadTemplate "https://codeload.github.com/dlang/dub/tar.gz/v{version}" dscannerURI = [uri|https://github.com/dlang-community/D-Scanner.git|] in Map.fromList [ ("DUB", latestDub) , ("DSCANNER", latestDscanner) , ("DCD", latestDcd) ] } ] up2Date :: Maybe Text -> SlackBuilderT () up2Date selectedPackage = do packages' <- SlackBuilderT $ asks (getField @"packages") case selectedPackage of Nothing -> traverse_ (handle handleException . go) $ autoUpdatable packages' Just packageName | Just foundPackage <- find ((packageName ==) . getField @"name") (autoUpdatable packages') -> go foundPackage | otherwise -> throwM $ UpdaterNotFound packageName where go package = getAndLogLatest package >>= mapM_ updatePackageIfRequired >> liftIO (putStrLn "") check :: SlackBuilderT () check = SlackBuilderT (asks (getField @"packages")) >>= traverse_ (handle handleException . go) . autoUpdatable where go package = getAndLogLatest package >>= mapM_ checkUpdateAvailability >> liftIO (putStrLn "") main :: IO () main = execParser slackBuilderParser >>= handle handleException . withCommandLine where withCommandLine programCommand = do settingsResult <- Toml.decodeFileEither settingsCodec configurationFile case settingsResult of Right settings -> flip runReaderT settings $ runSlackBuilderT $ executeCommand programCommand Left settingsErrors -> setSGR [SetColor Foreground Dull Red] >> putStrLn (configurationFile <> " parsing failed.") >> setSGR [Reset] >> Text.putStr (Toml.prettyTomlDecodeErrors settingsErrors) configurationFile = "config/config.toml" executeCommand = \case CheckCommand -> check Up2DateCommand packageName -> up2Date packageName