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

-- |
-- Module      :  Distribution.Fedora.Branch
-- Copyright   :  (C) 2020-2022  Jens Petersen
--
-- Maintainer  :  Jens Petersen <petersen@fedoraproject.org>
--
-- Explanation: Fedora Branch type and functions

-- 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.

module Distribution.Fedora.Branch
  ( Branch(..)
  , readBranch
  , readBranch'
  , eitherBranch
  , eitherBranch'
  , readActiveBranch
  , readActiveBranch'
  , eitherActiveBranch
  , newerBranch
  , releaseBranch
  , getFedoraBranches
  , getFedoraBranched
  , branchDestTag
  , branchDist
  , branchTarget
  )
where

#if (defined(MIN_VERSION_base) && MIN_VERSION_base(4,8,0))
#else
import Control.Applicative ((<$>))
#endif

import Data.Char (isDigit)
import qualified Data.Text as T

import qualified Distribution.Fedora as Dist

-- | Branch datatype
--
-- Branch can be rawhide, or a fedora or epel branch
data Branch = EPEL Int | EPELNext Int | Fedora Int | Rawhide
  deriving (Branch -> Branch -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Branch -> Branch -> Bool
$c/= :: Branch -> Branch -> Bool
== :: Branch -> Branch -> Bool
$c== :: Branch -> Branch -> Bool
Eq, Eq Branch
Branch -> Branch -> Bool
Branch -> Branch -> Ordering
Branch -> Branch -> Branch
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: Branch -> Branch -> Branch
$cmin :: Branch -> Branch -> Branch
max :: Branch -> Branch -> Branch
$cmax :: Branch -> Branch -> Branch
>= :: Branch -> Branch -> Bool
$c>= :: Branch -> Branch -> Bool
> :: Branch -> Branch -> Bool
$c> :: Branch -> Branch -> Bool
<= :: Branch -> Branch -> Bool
$c<= :: Branch -> Branch -> Bool
< :: Branch -> Branch -> Bool
$c< :: Branch -> Branch -> Bool
compare :: Branch -> Branch -> Ordering
$ccompare :: Branch -> Branch -> Ordering
Ord)

-- | Read a Fedora Branch name, otherwise return branch string
eitherBranch :: String -> Either String Branch
eitherBranch :: [Char] -> Either [Char] Branch
eitherBranch [Char]
"rawhide" = forall a b. b -> Either a b
Right Branch
Rawhide
eitherBranch (Char
'f':[Char]
ns) | forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Char -> Bool
isDigit [Char]
ns = let br :: Branch
br = Int -> Branch
Fedora (forall a. Read a => [Char] -> a
read [Char]
ns) in forall a b. b -> Either a b
Right Branch
br
-- FIXME add proper parsing:
eitherBranch [Char]
"epel8-next" = forall a b. b -> Either a b
Right forall a b. (a -> b) -> a -> b
$ Int -> Branch
EPELNext Int
8
eitherBranch [Char]
"epel9-next" = forall a b. b -> Either a b
Right forall a b. (a -> b) -> a -> b
$ Int -> Branch
EPELNext Int
9
eitherBranch (Char
'e':Char
'p':Char
'e':Char
'l':[Char]
n) | forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Char -> Bool
isDigit [Char]
n = let br :: Branch
br = Int -> Branch
EPEL (forall a. Read a => [Char] -> a
read [Char]
n) in forall a b. b -> Either a b
Right Branch
br
eitherBranch (Char
'e':Char
'l':[Char]
n) | forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Char -> Bool
isDigit [Char]
n = let br :: Branch
br = Int -> Branch
EPEL (forall a. Read a => [Char] -> a
read [Char]
n) in forall a b. b -> Either a b
Right Branch
br
eitherBranch [Char]
cs = forall a b. a -> Either a b
Left [Char]
cs

-- | Read a Fedora Branch name, otherwise return an error message
eitherBranch' :: String -> Either String Branch
eitherBranch' :: [Char] -> Either [Char] Branch
eitherBranch' [Char]
cs = case [Char] -> Either [Char] Branch
eitherBranch [Char]
cs of
  Right Branch
br -> forall a b. b -> Either a b
Right Branch
br
  Left [Char]
xs -> forall a b. a -> Either a b
Left forall a b. (a -> b) -> a -> b
$ [Char]
xs forall a. [a] -> [a] -> [a]
++ [Char]
" is not a known Fedora/EPEL branch"

-- | Read a Fedora Branch name
readBranch :: String -> Maybe Branch
readBranch :: [Char] -> Maybe Branch
readBranch [Char]
bs =
  case [Char] -> Either [Char] Branch
eitherBranch [Char]
bs of
    Left [Char]
_ -> forall a. Maybe a
Nothing
    Right Branch
br -> forall a. a -> Maybe a
Just Branch
br

-- | Unsafely read a Fedora Branch name: errors for unknown branches
readBranch' :: String -> Branch
readBranch' :: [Char] -> Branch
readBranch' [Char]
bs =
  case [Char] -> Either [Char] Branch
eitherBranch [Char]
bs of
    Left [Char]
e -> forall a. [Char] -> a
error' forall a b. (a -> b) -> a -> b
$! [Char]
"unknown Fedora branch: " forall a. [a] -> [a] -> [a]
++ [Char]
e
    Right Branch
br -> Branch
br

-- | Read a Branch name (one of the list of active branches)
--
-- Provides error strings for inactive or unknown branches.
eitherActiveBranch :: [Branch] -> String -> Either String Branch
eitherActiveBranch :: [Branch] -> [Char] -> Either [Char] Branch
eitherActiveBranch [Branch]
active [Char]
bs =
  case [Char] -> Either [Char] Branch
eitherBranch [Char]
bs of
    Left [Char]
e -> forall a b. a -> Either a b
Left [Char]
e
    Right Branch
br -> if Branch
br forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Branch]
active
                then forall a b. b -> Either a b
Right Branch
br
                else forall a b. a -> Either a b
Left [Char]
bs

-- | Read a Branch name (one of the list of active branches)
--
-- Similar to eitherActiveBranch but ignores any error string
readActiveBranch :: [Branch] -> String -> Maybe Branch
readActiveBranch :: [Branch] -> [Char] -> Maybe Branch
readActiveBranch [Branch]
active [Char]
cs =
  case [Branch] -> [Char] -> Either [Char] Branch
eitherActiveBranch [Branch]
active [Char]
cs of
    Left [Char]
_ -> forall a. Maybe a
Nothing
    Right Branch
br -> forall a. a -> Maybe a
Just Branch
br

-- | Read a Branch name (one of the list of active branches)
--
-- Like readActiveBranch, but errors for inactive or unknown branches.
readActiveBranch' :: [Branch] -> String -> Branch
readActiveBranch' :: [Branch] -> [Char] -> Branch
readActiveBranch' [Branch]
active [Char]
cs =
  case [Branch] -> [Char] -> Either [Char] Branch
eitherActiveBranch [Branch]
active [Char]
cs of
    Left [Char]
e -> forall a. [Char] -> a
error' forall a b. (a -> b) -> a -> b
$ [Char]
"inactive Fedora branch: " forall a. [a] -> [a] -> [a]
++ [Char]
e
    Right Branch
br -> Branch
br

instance Show Branch where
  show :: Branch -> [Char]
show Branch
Rawhide = [Char]
"rawhide"
  show (Fedora Int
n) = [Char]
"f" forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> [Char]
show Int
n
  show (EPEL Int
n) = (if Int
n forall a. Ord a => a -> a -> Bool
<= Int
6 then [Char]
"el" else [Char]
"epel") forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> [Char]
show Int
n
  show (EPELNext Int
n) = [Char]
"epel" forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> [Char]
show Int
n forall a. [a] -> [a] -> [a]
++ [Char]
"-next"

-- | Map Branch to Koji destination tag
branchDestTag :: Branch -> String
branchDestTag :: Branch -> [Char]
branchDestTag Branch
Rawhide = [Char]
"rawhide"
branchDestTag (Fedora Int
n) = forall a. Show a => a -> [Char]
show (Int -> Branch
Fedora Int
n) forall a. [a] -> [a] -> [a]
++ [Char]
"-updates-candidate"
branchDestTag (EPEL Int
n) = forall a. Show a => a -> [Char]
show (Int -> Branch
EPEL Int
n) forall a. [a] -> [a] -> [a]
++ [Char]
"-testing-candidate"
branchDestTag (EPELNext Int
n) = forall a. Show a => a -> [Char]
show (Int -> Branch
EPELNext Int
n) forall a. [a] -> [a] -> [a]
++ [Char]
"-testing-candidate"

-- | Default build target associated with a branch
branchTarget :: Branch -> String
branchTarget :: Branch -> [Char]
branchTarget (Fedora Int
n) = forall a. Show a => a -> [Char]
show (Int -> Branch
Fedora Int
n)
branchTarget (EPEL Int
n) = forall a. Show a => a -> [Char]
show (Int -> Branch
EPEL Int
n)
branchTarget (EPELNext Int
n) = forall a. Show a => a -> [Char]
show (Int -> Branch
EPELNext Int
n)
branchTarget Branch
Rawhide = [Char]
"rawhide"

--getLatestBranch :: IO Branch

-- | Returns newer branch than given one from supplied active branches.
--
-- Branches should be in descending order, eg from getFedoraBranches
newerBranch :: Branch -> [Branch] -> Branch
newerBranch :: Branch -> [Branch] -> Branch
newerBranch Branch
Rawhide [Branch]
_ = Branch
Rawhide
newerBranch (Fedora Int
n) [Branch]
branches =
  if Int -> Branch
Fedora Int
n forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Branch]
branches
  then if Int -> Branch
Fedora (Int
nforall a. Num a => a -> a -> a
+Int
1) forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Branch]
branches
       then Int -> Branch
Fedora (Int
nforall a. Num a => a -> a -> a
+Int
1)
       else Branch
Rawhide
  else forall a. [Char] -> a
error' forall a b. (a -> b) -> a -> b
$ [Char]
"Unsupported branch: " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> [Char]
show (Int -> Branch
Fedora Int
n)
newerBranch (EPEL Int
n) [Branch]
branches =
  if Int -> Branch
EPEL Int
n forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Branch]
branches
  then if Int -> Branch
EPEL (Int
nforall a. Num a => a -> a -> a
+Int
1) forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Branch]
branches
       then Int -> Branch
EPEL (Int
nforall a. Num a => a -> a -> a
+Int
1)
       else Int -> Branch
EPEL Int
n
  else forall a. [Char] -> a
error' forall a b. (a -> b) -> a -> b
$ [Char]
"Unsupported branch: " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> [Char]
show (Int -> Branch
EPEL Int
n)
newerBranch (EPELNext Int
n) [Branch]
branches =
  if Int -> Branch
EPELNext Int
n forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Branch]
branches
  then if Int -> Branch
EPELNext (Int
nforall a. Num a => a -> a -> a
+Int
1) forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Branch]
branches
       then Int -> Branch
EPELNext (Int
nforall a. Num a => a -> a -> a
+Int
1)
       else Int -> Branch
EPELNext Int
n
  else forall a. [Char] -> a
error' forall a b. (a -> b) -> a -> b
$ [Char]
"Unsupported branch: " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> [Char]
show (Int -> Branch
EPELNext Int
n)

--olderBranch :: Branch -> Branch
--olderBranch Rawhide = latestBranch
--olderBranch (Fedora n) = Fedora (n-1)

-- | Returns list of active Fedora branches, including rawhide and EPEL
getFedoraBranches :: IO [Branch]
getFedoraBranches :: IO [Branch]
getFedoraBranches = do
  -- FIXME get these from Bodhi Releases?
  let epelnext :: [Branch]
epelnext = [Int -> Branch
EPELNext Int
8, Int -> Branch
EPELNext Int
9]
  [Branch]
brs <- forall a b. (a -> b) -> [a] -> [b]
map Text -> Branch
releaseBranch forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO [Text]
Dist.getReleaseIds
  forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ [Branch]
brs forall a. [a] -> [a] -> [a]
++ [Branch]
epelnext

-- | Maps release-id to Branch
releaseBranch :: T.Text -> Branch
releaseBranch :: Text -> Branch
releaseBranch Text
"fedora-rawhide" = Branch
Rawhide
releaseBranch Text
rel | Text
"fedora-" Text -> Text -> Bool
`T.isPrefixOf` Text
rel =
                      let (Text
_,[Char]
ver) = Text -> [Char]
T.unpack forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text -> Text -> (Text, Text)
T.breakOnEnd Text
"-" Text
rel in
                        if forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Char -> Bool
isDigit [Char]
ver
                        then Int -> Branch
Fedora forall a b. (a -> b) -> a -> b
$ forall a. Read a => [Char] -> a
read [Char]
ver
                        else forall a. [Char] -> a
error' forall a b. (a -> b) -> a -> b
$ [Char]
"Unsupport release: " forall a. [a] -> [a] -> [a]
++ Text -> [Char]
T.unpack Text
rel
                  | Text
"epel-" Text -> Text -> Bool
`T.isPrefixOf` Text
rel =
                      let (Text
_,[Char]
ver) = Text -> [Char]
T.unpack forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text -> Text -> (Text, Text)
T.breakOnEnd Text
"-" Text
rel in
                        if forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Char -> Bool
isDigit [Char]
ver
                        then Int -> Branch
EPEL forall a b. (a -> b) -> a -> b
$ forall a. Read a => [Char] -> a
read [Char]
ver
                        else forall a. [Char] -> a
error' forall a b. (a -> b) -> a -> b
$ [Char]
"Unsupport release: " forall a. [a] -> [a] -> [a]
++ Text -> [Char]
T.unpack Text
rel
                  | Bool
otherwise = forall a. [Char] -> a
error' forall a b. (a -> b) -> a -> b
$ [Char]
"Unsupport release: " forall a. [a] -> [a] -> [a]
++ Text -> [Char]
T.unpack Text
rel

-- | Returns list of active Fedora branches, excluding rawhide
getFedoraBranched :: IO [Branch]
getFedoraBranched :: IO [Branch]
getFedoraBranched = forall a. (a -> Bool) -> [a] -> [a]
filter (forall a. Eq a => a -> a -> Bool
/= Branch
Rawhide) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO [Branch]
getFedoraBranches

-- from simple-cmd
error' :: String -> a
#if (defined(MIN_VERSION_base) && MIN_VERSION_base(4,9,0))
error' :: forall a. [Char] -> a
error' = forall a. [Char] -> a
errorWithoutStackTrace
#else
error' = error
#endif

-- | Convert a Branch to a Dist
branchDist :: Branch -> IO Dist.Dist
branchDist :: Branch -> IO Dist
branchDist (Fedora Int
n) = forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ Int -> Dist
Dist.Fedora Int
n
branchDist (EPEL Int
n) = forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ Int -> Dist
Dist.EPEL Int
n
branchDist (EPELNext Int
n) = forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ Int -> Dist
Dist.EPELNext Int
n
branchDist Branch
Rawhide = IO Dist
Dist.getRawhideDist