{-# 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 a :: String
a b :: 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
(prefixA :: String
prefixA, suffixA :: String
suffixA) = (Char -> Bool) -> String -> (String, String)
forall a. (a -> Bool) -> [a] -> ([a], [a])
span Char -> Bool
fn String
a'
(prefixB :: String
prefixB, suffixB :: 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 -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
a') Bool -> Bool -> Bool
&& ("~" 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 -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
a') -> Ordering
LT
| ("~" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
b') -> Ordering
GT
| ("^" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
a') Bool -> Bool -> Bool
&& ("^" 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 -> 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 -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
b') -> Ordering
LT
| ("^" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
a') -> Ordering
LT
| ("^" 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 x :: String
x y :: String
y =
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
== '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
== '0') String
y
in
if String -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
x' Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> String -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
y' then Ordering
GT
else String
x' String -> String -> Ordering
forall a. Ord a => a -> a -> Ordering
`compare` String
y'
isAsciiAlpha :: Char -> Bool
isAsciiAlpha :: Char -> Bool
isAsciiAlpha x :: Char
x = Char -> Bool
isAsciiLower Char
x Bool -> Bool -> Bool
|| Char -> Bool
isAsciiUpper Char
x
isVersionChar :: Char -> Bool
isVersionChar :: Char -> Bool
isVersionChar x :: 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
== '~' Bool -> Bool -> Bool
|| Char
x Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== '^'
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)