{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
module Data.SemVer.Internal where
import Control.Applicative
import Control.DeepSeq
import Control.Monad
import Data.Attoparsec.Text
import Data.Function (on)
import Data.Hashable
import Data.List (intersperse)
import Data.Monoid
import Data.Text (Text)
data Version = Version
{ _versionMajor :: !Int
, _versionMinor :: !Int
, _versionPatch :: !Int
, _versionRelease :: [Identifier]
, _versionMeta :: [Identifier]
} deriving (Eq, Show)
instance Ord Version where
compare a b = on compare versions a b <> release
where
versions Version{..} =
[ _versionMajor
, _versionMinor
, _versionPatch
]
release =
case (_versionRelease a, _versionRelease b) of
([], _:_) -> GT
(_:_, []) -> LT
(x, y) -> x `compare` y
instance NFData Version where
rnf Version{..} =
rnf _versionMajor
`seq` rnf _versionMinor
`seq` rnf _versionPatch
`seq` rnf _versionRelease
`seq` rnf _versionMeta
instance Hashable Version where
hashWithSalt s Version {..} =
s `hashWithSalt` _versionMajor
`hashWithSalt` _versionMinor
`hashWithSalt` _versionPatch
`hashWithSalt` _versionRelease
`hashWithSalt` _versionMeta
data Identifier
= INum !Int
| IText !Text
deriving (Eq, Show)
instance Ord Identifier where
compare a b = case (a, b) of
(INum x, INum y) -> x `compare` y
(IText x, IText y) -> x `compare` y
(INum _, _) -> LT
(IText _, _) -> GT
instance NFData Identifier where
rnf (INum n) = rnf n
rnf (IText t) = rnf t
instance Hashable Identifier where
hashWithSalt s (INum n) = s `hashWithSalt` (0 :: Int) `hashWithSalt` n
hashWithSalt s (IText t) = s `hashWithSalt` (1 :: Int) `hashWithSalt` t
identifierParser :: Parser () -> Parser Identifier
identifierParser p =
either INum IText <$> eitherP (numericParser p) (textualParser p)
numericParser :: Parser () -> Parser Int
numericParser p = nonNegative <* (p <|> endOfInput)
textualParser :: Parser () -> Parser Text
textualParser p = takeWhile1 (inClass "0-9A-Za-z-") <* optional p
nonNegative :: (Show a, Integral a) => Parser a
nonNegative = do
n <- decimal
when (n < 0) $
fail ("Numeric value must be non-negative: " ++ show n)
return n
data Delimiters = Delimiters
{ _delimMinor :: !Char
, _delimPatch :: !Char
, _delimRelease :: !Char
, _delimMeta :: !Char
, _delimIdent :: !Char
} deriving (Eq, Ord, Show)
instance NFData Delimiters where
rnf Delimiters{..} =
rnf _delimMinor
`seq` rnf _delimPatch
`seq` rnf _delimRelease
`seq` rnf _delimMeta
`seq` rnf _delimIdent
toMonoid :: Monoid m
=> (Char -> m)
-> (Int -> m)
-> (Text -> m)
-> Delimiters
-> Version
-> m
toMonoid del int txt Delimiters{..} Version{..} = mconcat
[ int _versionMajor
, del _delimMinor
, int _versionMinor
, del _delimPatch
, int _versionPatch
, f _delimRelease _versionRelease
, f _delimMeta _versionMeta
]
where
f _ [] = mempty
f c xs = del c <> mconcat (intersperse (del _delimIdent) (map g xs))
g (INum n) = int n
g (IText t) = txt t
{-# INLINE toMonoid #-}