{-# LANGUAGE OverloadedStrings   #-}
{-# LANGUAGE CPP                 #-}

-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.

{-|
The module provides a higher level API over BodhiReleases
with a Release record type.
-}

module Distribution.Fedora.Release
  ( Release(..)
  , getActiveReleases
  , getFedoraReleases
  , getEPELReleases
  , getBranchRelease
  , getRawhideVersion
  , getCurrentFedoraVersion
  )
where

import Control.Monad (guard)
import Data.Aeson(Object)
import Data.Maybe (mapMaybe)
import Distribution.Fedora.BodhiReleases
import Numeric.Natural (Natural)
import Safe (headMay)

-- | Fedora Release data
--
-- See <https://bodhi.fedoraproject.org/releases/>
data Release = Release {
    Release -> String
releaseName :: String,
    Release -> String
releaseVersion :: String,
    Release -> String
releaseIdPrefix :: String,
    Release -> String
releaseBranch :: String,
    Release -> Bool
releaseComposed :: Bool,
    Release -> String
releaseCandidateTag :: String,
    Release -> String
releaseDistTag :: String,
    Release -> Maybe String
releaseSettingStatus :: Maybe String,
    Release -> String
releaseState :: String,
    Release -> Bool
releaseAutomaticUpdates :: Bool,
    Release -> Maybe String
releaseTestingRepo :: Maybe String,
    Release -> String
releaseTestingTag :: String,
    Release -> Maybe String
releaseEOL :: Maybe String
  } deriving (Int -> Release -> ShowS
[Release] -> ShowS
Release -> String
(Int -> Release -> ShowS)
-> (Release -> String) -> ([Release] -> ShowS) -> Show Release
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Release -> ShowS
showsPrec :: Int -> Release -> ShowS
$cshow :: Release -> String
show :: Release -> String
$cshowList :: [Release] -> ShowS
showList :: [Release] -> ShowS
Show,Release -> Release -> Bool
(Release -> Release -> Bool)
-> (Release -> Release -> Bool) -> Eq Release
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Release -> Release -> Bool
== :: Release -> Release -> Bool
$c/= :: Release -> Release -> Bool
/= :: Release -> Release -> Bool
Eq)

readRelease :: Object -> Maybe Release
readRelease :: Object -> Maybe Release
readRelease Object
obj = do
  String
name <- Text -> Object -> Maybe String
forall a. FromJSON a => Text -> Object -> Maybe a
lookupKey Text
"name" Object
obj
  String
ver <- Text -> Object -> Maybe String
forall a. FromJSON a => Text -> Object -> Maybe a
lookupKey Text
"version" Object
obj
  String
idPref <- Text -> Object -> Maybe String
forall a. FromJSON a => Text -> Object -> Maybe a
lookupKey Text
"id_prefix" Object
obj
  Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (String
idPref String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` [String
"FEDORA-CONTAINER",String
"FEDORA-FLATPAK"])
  String
br <- Text -> Object -> Maybe String
forall a. FromJSON a => Text -> Object -> Maybe a
lookupKey Text
"branch" Object
obj
  Bool
composed <- Text -> Object -> Maybe Bool
forall a. FromJSON a => Text -> Object -> Maybe a
lookupKey Text
"composed_by_bodhi" Object
obj
  String
candidate <- Text -> Object -> Maybe String
forall a. FromJSON a => Text -> Object -> Maybe a
lookupKey Text
"candidate_tag" Object
obj
  String
disttag <- Text -> Object -> Maybe String
forall a. FromJSON a => Text -> Object -> Maybe a
lookupKey Text
"dist_tag" Object
obj
  let setting :: Maybe String
setting = Text -> Object -> Maybe String
forall a. FromJSON a => Text -> Object -> Maybe a
lookupKey Text
"setting_status" Object
obj
  String
state <- Text -> Object -> Maybe String
forall a. FromJSON a => Text -> Object -> Maybe a
lookupKey Text
"state" Object
obj
  Bool
automatic <- Text -> Object -> Maybe Bool
forall a. FromJSON a => Text -> Object -> Maybe a
lookupKey Text
"create_automatic_updates" Object
obj
  let testrepo :: Maybe String
testrepo = Text -> Object -> Maybe String
forall a. FromJSON a => Text -> Object -> Maybe a
lookupKey Text
"testing_repository" Object
obj
  String
testtag <- Text -> Object -> Maybe String
forall a. FromJSON a => Text -> Object -> Maybe a
lookupKey Text
"testing_tag" Object
obj
  let eol :: Maybe String
eol = Text -> Object -> Maybe String
forall a. FromJSON a => Text -> Object -> Maybe a
lookupKey Text
"eol" Object
obj
  Release -> Maybe Release
forall a. a -> Maybe a
forall (m :: * -> *) a. Monad m => a -> m a
return (Release -> Maybe Release) -> Release -> Maybe Release
forall a b. (a -> b) -> a -> b
$ String
-> String
-> String
-> String
-> Bool
-> String
-> String
-> Maybe String
-> String
-> Bool
-> Maybe String
-> String
-> Maybe String
-> Release
Release String
name String
ver String
idPref String
br Bool
composed String
candidate String
disttag Maybe String
setting String
state Bool
automatic Maybe String
testrepo String
testtag Maybe String
eol

-- | ordered by releaseName
instance Ord Release where
  compare :: Release -> Release -> Ordering
compare Release
r1 Release
r2 =
    String -> String -> Ordering
forall a. Ord a => a -> a -> Ordering
compare (Release -> String
releaseName Release
r1) (Release -> String
releaseName Release
r2)

-- containers and flatpaks are filtered out by readRelease
-- | Get list of all current Fedora Project releases (from Bodhi)
getActiveReleases :: IO [Release]
getActiveReleases :: IO [Release]
getActiveReleases =
  (Object -> Maybe Release) -> [Object] -> [Release]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe Object -> Maybe Release
readRelease ([Object] -> [Release]) -> IO [Object] -> IO [Release]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO [Object]
getBodhiReleases

-- getProductReleases :: String -> IO [Release]
-- getProductReleases name =
--   mapMaybe readRelease <$> getBodhiProductReleases name

-- | Get list of current Fedora Linux releases
getFedoraReleases :: IO [Release]
getFedoraReleases :: IO [Release]
getFedoraReleases =
  (Object -> Maybe Release) -> [Object] -> [Release]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe Object -> Maybe Release
readRelease ([Object] -> [Release]) -> IO [Object] -> IO [Release]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO [Object]
getBodhiFedoraReleases

-- | Get list of current Fedora EPEL releases
getEPELReleases :: IO [Release]
getEPELReleases :: IO [Release]
getEPELReleases =
  (Object -> Maybe Release) -> [Object] -> [Release]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe Object -> Maybe Release
readRelease ([Object] -> [Release]) -> IO [Object] -> IO [Release]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO [Object]
getBodhiEPELReleases

-- releaseFilter :: (Release -> a) -> (a -> Bool) -> [Release] -> [Release]
-- releaseFilter f p = filter (p . f)

-- | Get the Release for an active Branch
--
-- Errors for an inactive Branch
getBranchRelease :: String -> IO Release
getBranchRelease :: String -> IO Release
getBranchRelease String
br = do
  [Release]
rels <- (Object -> Maybe Release) -> [Object] -> [Release]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe Object -> Maybe Release
readRelease ([Object] -> [Release]) -> IO [Object] -> IO [Release]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> IO [Object]
getBodhiBranchReleases String
br
  case [Release]
rels of
    [] -> String -> IO Release
forall a. HasCallStack => String -> a
error (String -> IO Release) -> String -> IO Release
forall a b. (a -> b) -> a -> b
$ String
"release not found for branch " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
br
    [Release
rel] -> Release -> IO Release
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Release
rel
    [Release]
_ -> String -> IO Release
forall a. HasCallStack => String -> a
error (String -> IO Release) -> String -> IO Release
forall a b. (a -> b) -> a -> b
$ String
"multiple releases for " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
br String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
":\n" String -> ShowS
forall a. [a] -> [a] -> [a]
++ [String] -> String
unwords ((Release -> String) -> [Release] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map Release -> String
releaseName [Release]
rels)

-- | Get the Fedora release version of Rawhide
getRawhideVersion :: IO Natural
getRawhideVersion :: IO Natural
getRawhideVersion =
  String -> Natural
forall a. Read a => String -> a
read (String -> Natural) -> (Release -> String) -> Release -> Natural
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Release -> String
releaseVersion (Release -> Natural) -> IO Release -> IO Natural
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> IO Release
getBranchRelease String
"rawhide"

-- | Get the latest current stable Fedora release version
getCurrentFedoraVersion :: IO Natural
getCurrentFedoraVersion :: IO Natural
getCurrentFedoraVersion = do
  [Release]
rels <- (Release -> Bool) -> [Release] -> [Release]
forall a. (a -> Bool) -> [a] -> [a]
filter ((String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
"current") (String -> Bool) -> (Release -> String) -> Release -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Release -> String
releaseState) ([Release] -> [Release]) -> IO [Release] -> IO [Release]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO [Release]
getFedoraReleases
  case [Release] -> Maybe Release
forall a. [a] -> Maybe a
headMay [Release]
rels of
    Just Release
rel -> Natural -> IO Natural
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Natural -> IO Natural) -> Natural -> IO Natural
forall a b. (a -> b) -> a -> b
$ String -> Natural
forall a. Read a => String -> a
read (String -> Natural) -> String -> Natural
forall a b. (a -> b) -> a -> b
$ Release -> String
releaseVersion Release
rel
    Maybe Release
Nothing -> String -> IO Natural
forall a. HasCallStack => String -> a
error String
"current fedora release not found"