{-# LANGUAGE CPP, MultiWayIf #-}
module Data.RPM.VerCmp (rpmVerCompare)
where
import Data.Char (isAsciiLower, isAsciiUpper, isDigit)
import Data.List (isPrefixOf)
#if !MIN_VERSION_base(4,11,0)
import Data.Monoid ((<>))
#endif
rpmVerCompare :: String -> String -> Ordering
rpmVerCompare :: String -> String -> Ordering
rpmVerCompare String
a String
b =
if String
a String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
b then Ordering
EQ
else
let a' :: String
a' = String -> String
dropSeparators String
a
b' :: String
b' = String -> String
dropSeparators String
b
fn :: Char -> Bool
fn = if Char -> Bool
isDigit (String -> Char
forall a. [a] -> a
head String
a') then Char -> Bool
isDigit else Char -> Bool
isAsciiAlpha
(String
prefixA, String
suffixA) = (Char -> Bool) -> String -> (String, String)
forall a. (a -> Bool) -> [a] -> ([a], [a])
span Char -> Bool
fn String
a'
(String
prefixB, String
suffixB) = (Char -> Bool) -> String -> (String, String)
forall a. (a -> Bool) -> [a] -> ([a], [a])
span Char -> Bool
fn String
b'
in
if | String
a' String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
b' -> Ordering
EQ
| (String
"~" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
a') Bool -> Bool -> Bool
&& (String
"~" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
b') -> String -> String -> Ordering
rpmVerCompare (String -> String
forall a. [a] -> [a]
tail String
a') (String -> String
forall a. [a] -> [a]
tail String
b')
| (String
"~" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
a') -> Ordering
LT
| (String
"~" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
b') -> Ordering
GT
| (String
"^" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
a') Bool -> Bool -> Bool
&& (String
"^" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
b') -> String -> String -> Ordering
rpmVerCompare (String -> String
forall a. [a] -> [a]
tail String
a') (String -> String
forall a. [a] -> [a]
tail String
b')
| (String
"^" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
a') Bool -> Bool -> Bool
&& String -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
b' -> Ordering
GT
| String -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
a' Bool -> Bool -> Bool
&& (String
"^" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
b') -> Ordering
LT
| (String
"^" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
a') -> Ordering
LT
| (String
"^" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
b') -> Ordering
GT
| (String -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
a') -> Ordering
LT
| (String -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
b') -> Ordering
GT
| Char -> Bool
isDigit (String -> Char
forall a. [a] -> a
head String
a') Bool -> Bool -> Bool
&& (Bool -> Bool
not (Bool -> Bool) -> (Char -> Bool) -> Char -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Bool
isDigit) (String -> Char
forall a. [a] -> a
head String
b') -> Ordering
GT
| (Bool -> Bool
not (Bool -> Bool) -> (Char -> Bool) -> Char -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Bool
isDigit) (String -> Char
forall a. [a] -> a
head String
a') Bool -> Bool -> Bool
&& Char -> Bool
isDigit (String -> Char
forall a. [a] -> a
head String
b') -> Ordering
LT
| Char -> Bool
isDigit (String -> Char
forall a. [a] -> a
head String
a') -> (String
prefixA String -> String -> Ordering
`compareAsInts` String
prefixB) Ordering -> Ordering -> Ordering
forall a. Semigroup a => a -> a -> a
<> (String
suffixA String -> String -> Ordering
`rpmVerCompare` String
suffixB)
| Bool
otherwise -> (String
prefixA String -> String -> Ordering
forall a. Ord a => a -> a -> Ordering
`compare` String
prefixB) Ordering -> Ordering -> Ordering
forall a. Semigroup a => a -> a -> a
<> (String
suffixA String -> String -> Ordering
`rpmVerCompare` String
suffixB)
where
compareAsInts :: String -> String -> Ordering
compareAsInts :: String -> String -> Ordering
compareAsInts String
x String
y =
if | String
x String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
y -> Ordering
EQ
| String -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
x -> Ordering
LT
| String -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
y -> Ordering
GT
| Bool
otherwise ->
let x' :: String
x' = (Char -> Bool) -> String -> String
forall a. (a -> Bool) -> [a] -> [a]
dropWhile (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'0') String
x
y' :: String
y' = (Char -> Bool) -> String -> String
forall a. (a -> Bool) -> [a] -> [a]
dropWhile (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'0') String
y
in
case Int -> Int -> Ordering
forall a. Ord a => a -> a -> Ordering
compare (String -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
x') (String -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
y') of
Ordering
EQ ->
(String -> Int
forall a. Read a => String -> a
read String
x' :: Int) Int -> Int -> Ordering
forall a. Ord a => a -> a -> Ordering
`compare` String -> Int
forall a. Read a => String -> a
read String
y'
Ordering
o -> Ordering
o
isAsciiAlpha :: Char -> Bool
isAsciiAlpha :: Char -> Bool
isAsciiAlpha Char
x = Char -> Bool
isAsciiLower Char
x Bool -> Bool -> Bool
|| Char -> Bool
isAsciiUpper Char
x
isVersionChar :: Char -> Bool
isVersionChar :: Char -> Bool
isVersionChar Char
x = Char -> Bool
isDigit Char
x Bool -> Bool -> Bool
|| Char -> Bool
isAsciiAlpha Char
x Bool -> Bool -> Bool
|| Char
x Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'~' Bool -> Bool -> Bool
|| Char
x Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'^'
dropSeparators :: String -> String
dropSeparators :: String -> String
dropSeparators = (Char -> Bool) -> String -> String
forall a. (a -> Bool) -> [a] -> [a]
dropWhile (Bool -> Bool
not (Bool -> Bool) -> (Char -> Bool) -> Char -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Bool
isVersionChar)