{-# language MonadComprehensions #-}

module Hix.Version where

import Data.List.Extra (groupOnKey)
import Distribution.Version (
  Bound (ExclusiveBound),
  LowerBound (LowerBound),
  UpperBound (UpperBound),
  Version,
  VersionInterval (VersionInterval),
  VersionRange,
  alterVersion,
  asVersionIntervals,
  version0,
  versionNumbers,
  )

import qualified Hix.Data.Version
import Hix.Data.Version (Major (Major))

lowerVersion :: VersionRange -> Maybe Version
lowerVersion :: VersionRange -> Maybe Version
lowerVersion VersionRange
range =
  [Version
v | VersionInterval (LowerBound Version
v Bound
_) UpperBound
_ <- [VersionInterval] -> Maybe VersionInterval
forall a. [a] -> Maybe a
head (VersionRange -> [VersionInterval]
asVersionIntervals VersionRange
range), Version
v Version -> Version -> Bool
forall a. Eq a => a -> a -> Bool
/= Version
version0]

exclusiveUpperVersion :: VersionRange -> Maybe Version
exclusiveUpperVersion :: VersionRange -> Maybe Version
exclusiveUpperVersion VersionRange
range =
  [Version
v | VersionInterval LowerBound
_ (UpperBound Version
v Bound
ExclusiveBound) <- [VersionInterval] -> Maybe VersionInterval
forall a. [a] -> Maybe a
last (VersionRange -> [VersionInterval]
asVersionIntervals VersionRange
range)]

upperVersion :: VersionRange -> Maybe Version
upperVersion :: VersionRange -> Maybe Version
upperVersion VersionRange
range =
  [Version
v | VersionInterval LowerBound
_ (UpperBound Version
v Bound
_) <- [VersionInterval] -> Maybe VersionInterval
forall a. [a] -> Maybe a
last (VersionRange -> [VersionInterval]
asVersionIntervals VersionRange
range)]

majorParts :: Version -> Maybe (Int, Int)
majorParts :: Version -> Maybe (Int, Int)
majorParts =
  Version -> [Int]
versionNumbers (Version -> [Int])
-> ([Int] -> Maybe (Int, Int)) -> Version -> Maybe (Int, Int)
forall {k} (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
>>> \case
    [] -> Maybe (Int, Int)
forall a. Maybe a
Nothing
    [Item [Int]
0] -> Maybe (Int, Int)
forall a. Maybe a
Nothing
    [Item [Int]
s] -> (Int, Int) -> Maybe (Int, Int)
forall a. a -> Maybe a
Just (Int
Item [Int]
s, Int
0)
    Int
s : Int
m : [Int]
_ -> (Int, Int) -> Maybe (Int, Int)
forall a. a -> Maybe a
Just (Int
s, Int
m)

majorPrefix :: Version -> Maybe [Int]
majorPrefix :: Version -> Maybe [Int]
majorPrefix =
  Version -> Maybe (Int, Int)
majorParts (Version -> Maybe (Int, Int))
-> (Maybe (Int, Int) -> Maybe [Int]) -> Version -> Maybe [Int]
forall {k} (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
>>> ((Int, Int) -> [Int]) -> Maybe (Int, Int) -> Maybe [Int]
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap \case
    (Int
s, Int
m) -> [Int
Item [Int]
s, Int
Item [Int]
m]

hasMajor :: Int -> Int -> Version -> Bool
hasMajor :: Int -> Int -> Version -> Bool
hasMajor Int
s Int
m Version
candidate =
  case Int -> [Int] -> [Int]
forall a. Int -> [a] -> [a]
take Int
2 (Version -> [Int]
versionNumbers Version
candidate) of
    [Item [Int]
s'] -> Int
Item [Int]
s' Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
s Bool -> Bool -> Bool
&& Int
m Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0
    Int
s' : Int
m' : [Int]
_ -> Int
s' Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
s Bool -> Bool -> Bool
&& Int
m' Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
m
    [Int]
_ -> Bool
False

minMajor :: Int -> Int -> Version -> Bool
minMajor :: Int -> Int -> Version -> Bool
minMajor Int
s Int
m Version
candidate =
  case Int -> [Int] -> [Int]
forall a. Int -> [a] -> [a]
take Int
2 (Version -> [Int]
versionNumbers Version
candidate) of
    [Item [Int]
s'] -> Int
Item [Int]
s' Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
s Bool -> Bool -> Bool
|| (Int
Item [Int]
s' Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
s Bool -> Bool -> Bool
&& Int
m Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0)
    Int
s' : Int
m' : [Int]
_ -> Int
s' Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
s Bool -> Bool -> Bool
|| (Int
s' Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
s Bool -> Bool -> Bool
&& Int
m' Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
m)
    [Int]
_ -> Bool
False

beforeMajor :: Int -> Int -> Version -> Bool
beforeMajor :: Int -> Int -> Version -> Bool
beforeMajor Int
s Int
m Version
candidate =
  case Int -> [Int] -> [Int]
forall a. Int -> [a] -> [a]
take Int
2 (Version -> [Int]
versionNumbers Version
candidate) of
    [Item [Int]
s'] -> Int
Item [Int]
s' Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
s Bool -> Bool -> Bool
|| (Int
Item [Int]
s' Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
s Bool -> Bool -> Bool
&& Int
m Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
0)
    Int
s' : Int
m' : [Int]
_ -> Int
s' Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
s Bool -> Bool -> Bool
|| (Int
s' Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
s Bool -> Bool -> Bool
&& Int
m' Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
m)
    [Int]
_ -> Bool
False

lastMajor :: [Version] -> [Version]
lastMajor :: [Version] -> [Version]
lastMajor =
  [Version] -> [Version]
forall a. [a] -> [a]
reverse ([Version] -> [Version])
-> ([Version] -> [Version]) -> [Version] -> [Version]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([Version], (Int, Int)) -> [Version]
forall a b. (a, b) -> a
fst (([Version], (Int, Int)) -> [Version])
-> ([Version] -> ([Version], (Int, Int))) -> [Version] -> [Version]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (([Version], (Int, Int)) -> Version -> ([Version], (Int, Int)))
-> ([Version], (Int, Int)) -> [Version] -> ([Version], (Int, Int))
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl ([Version], (Int, Int)) -> Version -> ([Version], (Int, Int))
step ([], (Int
0, Int
0))
  where
    step :: ([Version], (Int, Int)) -> Version -> ([Version], (Int, Int))
step ([Version]
z, (Int, Int)
cur) Version
a
      | Just (Int, Int)
pre <- Version -> Maybe (Int, Int)
majorParts Version
a
      , (Int, Int)
pre (Int, Int) -> (Int, Int) -> Bool
forall a. Eq a => a -> a -> Bool
/= (Int, Int)
cur
      = ([Version
Item [Version]
a], (Int, Int)
pre)
      | Bool
otherwise
      = (Version
a Version -> [Version] -> [Version]
forall a. a -> [a] -> [a]
: [Version]
z, (Int, Int)
cur)

lastMajorBefore :: Int -> Int -> [Version] -> [Version]
lastMajorBefore :: Int -> Int -> [Version] -> [Version]
lastMajorBefore Int
s Int
m =
  [Version] -> [Version]
forall a. [a] -> [a]
reverse ([Version] -> [Version])
-> ([Version] -> [Version]) -> [Version] -> [Version]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([Version], (Int, Int)) -> [Version]
forall a b. (a, b) -> a
fst (([Version], (Int, Int)) -> [Version])
-> ([Version] -> ([Version], (Int, Int))) -> [Version] -> [Version]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (([Version], (Int, Int)) -> Version -> ([Version], (Int, Int)))
-> ([Version], (Int, Int)) -> [Version] -> ([Version], (Int, Int))
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl ([Version], (Int, Int)) -> Version -> ([Version], (Int, Int))
step ([], (Int
0, Int
0))
  where
    step :: ([Version], (Int, Int)) -> Version -> ([Version], (Int, Int))
step ([Version]
z, (Int, Int)
cur) Version
a
      | Just (Int
s', Int
m') <- Maybe (Int, Int)
mp
      , Int
s' Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
s Bool -> Bool -> Bool
|| (Int
s' Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
s Bool -> Bool -> Bool
&& Int
m' Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
m)
      = ([Version]
z, (Int, Int)
cur)
      | Just (Int, Int)
pre <- Maybe (Int, Int)
mp
      , (Int, Int)
pre (Int, Int) -> (Int, Int) -> Bool
forall a. Eq a => a -> a -> Bool
/= (Int, Int)
cur
      = ([Version
Item [Version]
a], (Int, Int)
pre)
      | Bool
otherwise
      = (Version
a Version -> [Version] -> [Version]
forall a. a -> [a] -> [a]
: [Version]
z, (Int, Int)
cur)
      where
        mp :: Maybe (Int, Int)
mp = Version -> Maybe (Int, Int)
majorParts Version
a

nextMajor :: Version -> Version
nextMajor :: Version -> Version
nextMajor =
  ([Int] -> [Int]) -> Version -> Version
alterVersion \case
    [] -> [Int
Item [Int]
0, Int
Item [Int]
1]
    [Item [Int]
s] -> [Item [Int]
s, Int
Item [Int]
1]
    Int
s : Int
m : [Int]
_ -> [Int
Item [Int]
s, Int
m Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1]

prevMajor :: Version -> Version
prevMajor :: Version -> Version
prevMajor =
  ([Int] -> [Int]) -> Version -> Version
alterVersion \case
    [Item [Int]
s] | Int
Item [Int]
s Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
0 -> [Int
Item [Int]
s Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1, Int
Item [Int]
99]
    Int
s : Int
0 : [Int]
_ | Int
s Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
0 -> [Int
s Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1, Int
Item [Int]
99]
    Int
s : Int
m : [Int]
_ | Int
s Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
0, Int
m Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
0 -> [Int
Item [Int]
s, Int
m Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1]
    [Int]
_ -> [Int
Item [Int]
0]

currentMajor :: Version -> Version
currentMajor :: Version -> Version
currentMajor = ([Int] -> [Int]) -> Version -> Version
alterVersion (Int -> [Int] -> [Int]
forall a. Int -> [a] -> [a]
take Int
2)

isMajor :: Int -> Int -> Major -> Bool
isMajor :: Int -> Int -> Major -> Bool
isMajor Int
s Int
m Major {Version
prefix :: Version
prefix :: Major -> Version
prefix} =
  Version
prefix Version -> Version -> Bool
forall a. Eq a => a -> a -> Bool
== [Int
Item Version
s, Int
Item Version
m]

majorOlder :: Int -> Int -> Major -> Bool
majorOlder :: Int -> Int -> Major -> Bool
majorOlder Int
s Int
m Major {Version
prefix :: Major -> Version
prefix :: Version
prefix} =
  Version
prefix Version -> Version -> Bool
forall a. Ord a => a -> a -> Bool
< [Int
Item Version
s, Int
Item Version
m]

majorNewer :: Int -> Int -> Major -> Bool
majorNewer :: Int -> Int -> Major -> Bool
majorNewer Int
s Int
m Major {Version
prefix :: Major -> Version
prefix :: Version
prefix} =
  Version
prefix Version -> Version -> Bool
forall a. Ord a => a -> a -> Bool
> [Int
Item Version
s, Int
Item Version
m]

allMajors :: [Version] -> [Major]
allMajors :: [Version] -> [Major]
allMajors =
  ((Version, [Version]) -> Maybe Major)
-> [(Version, [Version])] -> [Major]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe ((Version -> [Version] -> Maybe Major)
-> (Version, [Version]) -> Maybe Major
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry Version -> [Version] -> Maybe Major
cons) ([(Version, [Version])] -> [Major])
-> ([Version] -> [(Version, [Version])]) -> [Version] -> [Major]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Version -> Version) -> [Version] -> [(Version, [Version])]
forall k a. Eq k => (a -> k) -> [a] -> [(k, [a])]
groupOnKey Version -> Version
currentMajor
  where
    cons :: Version -> [Version] -> Maybe Major
cons Version
prefix = \case
      (Version
h : [Version]
t) -> Major -> Maybe Major
forall a. a -> Maybe a
Just (Major {Version
prefix :: Version
prefix :: Version
prefix, versions :: NonEmpty Version
versions = Version
h Version -> [Version] -> NonEmpty Version
forall a. a -> [a] -> NonEmpty a
:| [Version]
t})
      [] -> Maybe Major
forall a. Maybe a
Nothing

majorsBefore :: Int -> Int -> [Version] -> [Major]
majorsBefore :: Int -> Int -> [Version] -> [Major]
majorsBefore Int
s Int
m =
  (Major -> Bool) -> [Major] -> [Major]
forall a. (a -> Bool) -> [a] -> [a]
takeWhile (Int -> Int -> Major -> Bool
majorOlder Int
s Int
m) ([Major] -> [Major])
-> ([Version] -> [Major]) -> [Version] -> [Major]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Version] -> [Major]
allMajors

majorsFrom :: Int -> Int -> [Version] -> [Major]
majorsFrom :: Int -> Int -> [Version] -> [Major]
majorsFrom Int
s Int
m =
  (Major -> Bool) -> [Major] -> [Major]
forall a. (a -> Bool) -> [a] -> [a]
dropWhile (Int -> Int -> Major -> Bool
majorOlder Int
s Int
m) ([Major] -> [Major])
-> ([Version] -> [Major]) -> [Version] -> [Major]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Version] -> [Major]
allMajors

onlyMajor :: Int -> Int -> [Version] -> Maybe Major
onlyMajor :: Int -> Int -> [Version] -> Maybe Major
onlyMajor Int
s Int
m =
  (Major -> Bool) -> [Major] -> Maybe Major
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find (Int -> Int -> Major -> Bool
isMajor Int
s Int
m) ([Major] -> Maybe Major)
-> ([Version] -> [Major]) -> [Version] -> Maybe Major
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Version] -> [Major]
allMajors

versionsFrom :: Version -> [Version] -> [Major]
versionsFrom :: Version -> [Version] -> [Major]
versionsFrom Version
start =
  [Version] -> [Major]
allMajors ([Version] -> [Major])
-> ([Version] -> [Version]) -> [Version] -> [Major]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Version -> Bool) -> [Version] -> [Version]
forall a. (a -> Bool) -> [a] -> [a]
dropWhile (Version -> Version -> Bool
forall a. Ord a => a -> a -> Bool
< Version
start)

versionsBetween :: Version -> Version -> [Version] -> [Major]
versionsBetween :: Version -> Version -> [Version] -> [Major]
versionsBetween Version
l Version
u =
  [Version] -> [Major]
allMajors ([Version] -> [Major])
-> ([Version] -> [Version]) -> [Version] -> [Major]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Version -> Bool) -> [Version] -> [Version]
forall a. (a -> Bool) -> [a] -> [a]
takeWhile (Version -> Version -> Bool
forall a. Ord a => a -> a -> Bool
< Version
u) ([Version] -> [Version])
-> ([Version] -> [Version]) -> [Version] -> [Version]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Version -> Bool) -> [Version] -> [Version]
forall a. (a -> Bool) -> [a] -> [a]
dropWhile (Version -> Version -> Bool
forall a. Ord a => a -> a -> Bool
< Version
l)