module Ecluse.Core.Version.Gem (
GemKey (..),
parseGem,
compareGemTokens,
isGemStable,
) where
import Data.Char (isDigit)
import Data.List (dropWhileEnd)
import Data.Text qualified as T
import Ecluse.Core.Version.Token (VToken (..), isAsciiAlphaNum, maxVersionLength, numOr0)
newtype GemKey = GemKey [VToken]
deriving stock (GemKey -> GemKey -> Bool
(GemKey -> GemKey -> Bool)
-> (GemKey -> GemKey -> Bool) -> Eq GemKey
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: GemKey -> GemKey -> Bool
== :: GemKey -> GemKey -> Bool
$c/= :: GemKey -> GemKey -> Bool
/= :: GemKey -> GemKey -> Bool
Eq, Int -> GemKey -> ShowS
[GemKey] -> ShowS
GemKey -> String
(Int -> GemKey -> ShowS)
-> (GemKey -> String) -> ([GemKey] -> ShowS) -> Show GemKey
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> GemKey -> ShowS
showsPrec :: Int -> GemKey -> ShowS
$cshow :: GemKey -> String
show :: GemKey -> String
$cshowList :: [GemKey] -> ShowS
showList :: [GemKey] -> ShowS
Show)
instance Ord GemKey where
compare :: GemKey -> GemKey -> Ordering
compare (GemKey [VToken]
a) (GemKey [VToken]
b) = [VToken] -> [VToken] -> Ordering
compareGemTokens [VToken]
a [VToken]
b
compareGemTokens :: [VToken] -> [VToken] -> Ordering
compareGemTokens :: [VToken] -> [VToken] -> Ordering
compareGemTokens [] [] = Ordering
EQ
compareGemTokens (VToken
x : [VToken]
xs) (VToken
y : [VToken]
ys) = VToken -> VToken -> Ordering
forall a. Ord a => a -> a -> Ordering
compare VToken
x VToken
y Ordering -> Ordering -> Ordering
forall a. Semigroup a => a -> a -> a
<> [VToken] -> [VToken] -> Ordering
compareGemTokens [VToken]
xs [VToken]
ys
compareGemTokens (VToken
x : [VToken]
xs) [] = VToken -> VToken -> Ordering
forall a. Ord a => a -> a -> Ordering
compare VToken
x (Integer -> VToken
VNum Integer
0) Ordering -> Ordering -> Ordering
forall a. Semigroup a => a -> a -> a
<> [VToken] -> [VToken] -> Ordering
compareGemTokens [VToken]
xs []
compareGemTokens [] (VToken
y : [VToken]
ys) = VToken -> VToken -> Ordering
forall a. Ord a => a -> a -> Ordering
compare (Integer -> VToken
VNum Integer
0) VToken
y Ordering -> Ordering -> Ordering
forall a. Semigroup a => a -> a -> a
<> [VToken] -> [VToken] -> Ordering
compareGemTokens [] [VToken]
ys
parseGem :: Text -> Maybe GemKey
parseGem :: Text -> Maybe GemKey
parseGem Text
raw = do
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Text -> Int -> Ordering
T.compareLength Text
raw Int
maxVersionLength Ordering -> Ordering -> Bool
forall a. Eq a => a -> a -> Bool
/= Ordering
GT)
let stripped :: Text
stripped = Text -> Text
T.strip Text
raw
segs :: [Text]
segs = HasCallStack => Text -> Text -> [Text]
Text -> Text -> [Text]
T.splitOn Text
"." (HasCallStack => Text -> Text -> Text -> Text
Text -> Text -> Text -> Text
T.replace Text
"-" Text
".pre." Text
stripped)
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Bool
not (Text -> Bool
T.null Text
stripped))
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard ((Text -> Bool) -> [Text] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Text -> Bool
validSeg [Text]
segs)
let toks :: [VToken]
toks = (Text -> [VToken]) -> [Text] -> [VToken]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Text -> [VToken]
segTokens [Text]
segs
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Bool
not ([VToken] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [VToken]
toks))
GemKey -> Maybe GemKey
forall a. a -> Maybe a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([VToken] -> GemKey
GemKey ([VToken] -> [VToken]
canonicalSegments [VToken]
toks))
where
validSeg :: Text -> Bool
validSeg Text
s = Bool -> Bool
not (Text -> Bool
T.null Text
s) Bool -> Bool -> Bool
&& (Char -> Bool) -> Text -> Bool
T.all Char -> Bool
isAsciiAlphaNum Text
s
segTokens :: Text -> [VToken]
segTokens = (Text -> VToken) -> [Text] -> [VToken]
forall a b. (a -> b) -> [a] -> [b]
map Text -> VToken
classify ([Text] -> [VToken]) -> (Text -> [Text]) -> Text -> [VToken]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> Char -> Bool) -> Text -> [Text]
T.groupBy (\Char
c1 Char
c2 -> Char -> Bool
isDigit Char
c1 Bool -> Bool -> Bool
forall a. Eq a => a -> a -> Bool
== Char -> Bool
isDigit Char
c2)
classify :: Text -> VToken
classify Text
g = if (Char -> Bool) -> Text -> Bool
T.all Char -> Bool
isDigit Text
g then Integer -> VToken
VNum (Text -> Integer
numOr0 Text
g) else Text -> VToken
VStr Text
g
canonicalSegments :: [VToken] -> [VToken]
canonicalSegments :: [VToken] -> [VToken]
canonicalSegments [VToken]
toks =
let ([VToken]
release, [VToken]
prerelease) = (VToken -> Bool) -> [VToken] -> ([VToken], [VToken])
forall a. (a -> Bool) -> [a] -> ([a], [a])
break VToken -> Bool
isText [VToken]
toks
in [VToken] -> [VToken]
dropTrailingZeros [VToken]
release [VToken] -> [VToken] -> [VToken]
forall a. Semigroup a => a -> a -> a
<> [VToken] -> [VToken]
dropTrailingZeros [VToken]
prerelease
where
isText :: VToken -> Bool
isText = \case
VStr Text
_ -> Bool
True
VNum Integer
_ -> Bool
False
dropTrailingZeros :: [VToken] -> [VToken]
dropTrailingZeros = (VToken -> Bool) -> [VToken] -> [VToken]
forall a. (a -> Bool) -> [a] -> [a]
dropWhileEnd (VToken -> VToken -> Bool
forall a. Eq a => a -> a -> Bool
== Integer -> VToken
VNum Integer
0)
isGemStable :: GemKey -> Bool
isGemStable :: GemKey -> Bool
isGemStable (GemKey [VToken]
toks) = (VToken -> Bool) -> [VToken] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all VToken -> Bool
isNumToken [VToken]
toks
where
isNumToken :: VToken -> Bool
isNumToken = \case
VNum Integer
_ -> Bool
True
VStr Text
_ -> Bool
False