versions 3.5.3 → 3.5.4
raw patch · 6 files changed
+142/−39 lines, 6 filesdep −base-preludedep −checkerssetup-changed
Dependencies removed: base-prelude, checkers
Files
- CHANGELOG.md +13/−0
- Data/Versions.hs +100/−17
- README.md +1/−0
- Setup.hs +2/−0
- test/Test.hs +24/−18
- versions.cabal +2/−4
CHANGELOG.md view
@@ -1,5 +1,18 @@ # Changelog +## 3.5.4 (2020-05-12)++#### Added++- The functions `isIdeal`, `isGeneral`, and `isComplex` for `Bool`-based+ inspection of parse results.+- `messMajor`, `messMinor`, `messPatch`, and `messPatchChunk` for improved+ introspection into `Mess` values.++#### Changed++- Improved `Mess` comparison logic.+ ## 3.5.3 - GHC 8.10 support.
Data/Versions.hs view
@@ -37,11 +37,11 @@ module Data.Versions ( -- * Types- Versioning(..)+ Versioning(..), isIdeal, isGeneral, isComplex , SemVer(..) , PVP(..) , Version(..)- , Mess(..)+ , Mess(..), messMajor, messMinor, messPatch, messPatchChunk , VUnit(..), digits, str , VChunk , VSep(..)@@ -96,6 +96,21 @@ data Versioning = Ideal SemVer | General Version | Complex Mess deriving (Eq, Show, Generic, NFData, Hashable) +-- | Short-hand for detecting a `SemVer`.+isIdeal :: Versioning -> Bool+isIdeal (Ideal _) = True+isIdeal _ = False++-- | Short-hand for detecting a `Version`.+isGeneral :: Versioning -> Bool+isGeneral (General _) = True+isGeneral _ = False++-- | Short-hand for detecting a `Mess`.+isComplex :: Versioning -> Bool+isComplex (Complex _) = True+isComplex _ = False+ -- | Comparison of @Ideal@s is always well defined. -- -- If comparison of @General@s is well-defined, then comparison of @Ideal@ and@@ -106,15 +121,15 @@ -- and @Complex@ is well defined for the same reason. This implies comparison of -- @Ideal@ and @Complex@ is also well-defined. instance Ord Versioning where- compare (Ideal s) (Ideal s') = compare s s'- compare (General v) (General v') = compare v v'- compare (Complex m) (Complex m') = compare m m'- compare (Ideal s) (General v) = compare (vFromS s) v- compare (General v) (Ideal s) = opposite $ compare (vFromS s) v- compare (General v) (Complex m) = compare (mFromV v) m- compare (Complex m) (General v) = opposite $ compare (mFromV v) m- compare (Ideal s) m@(Complex _) = compare (General $ vFromS s) m- compare m@(Complex _) (Ideal s) = compare m (General $ vFromS s)+ compare (Ideal s) (Ideal s') = compare s s'+ compare (General v) (General v') = compare v v'+ compare (Complex m) (Complex m') = compare m m'+ compare (Ideal s) (General v) = compare (vFromS s) v+ compare (General v) (Ideal s) = opposite $ compare (vFromS s) v+ compare (General v) (Complex m) = compare (mFromV v) m+ compare (Complex m) (General v) = opposite $ compare (mFromV v) m+ compare (Ideal s) (Complex m) = semverAndMess s m+ compare (Complex m) (Ideal s) = opposite $ semverAndMess s m -- | Convert a `SemVer` to a `Version`. vFromS :: SemVer -> Version@@ -123,8 +138,40 @@ -- | Convert a `Version` to a `Mess`. mFromV :: Version -> Mess mFromV (Version e v r) = maybe affix (\a -> VNode [showt a] VColon affix) e- where affix = VNode (chunksAsT v) VHyphen $ VLeaf (chunksAsT r)+ where+ affix :: Mess+ affix = VNode (chunksAsT v) VHyphen $ VLeaf (chunksAsT r) +-- | Special logic for when semver-like values can be extracted from a `Mess`.+-- This avoids having to "downcast" the `SemVer` into a `Mess` before comparing,+-- and in some cases can offer better comparison results.+semverAndMess :: SemVer -> Mess -> Ordering+semverAndMess s@(SemVer ma mi pa _ _) m = case compare ma <$> messMajor m of+ Nothing -> fallback+ Just LT -> LT+ Just GT -> GT+ Just EQ -> case compare mi <$> messMinor m of+ Nothing -> fallback+ Just LT -> LT+ Just GT -> GT+ Just EQ -> case compare pa <$> messPatch m of+ Just LT -> LT+ Just GT -> GT+ -- If they've been equal up to this point, the `Mess`+ -- will by definition have more to it, meaning that+ -- it's more likely to be newer, despite its poor shape.+ Just EQ -> fallback+ Nothing -> case messPatchChunk m of+ Nothing -> fallback+ Just (Digits pa':_) -> case compare pa pa' of+ LT -> LT+ GT -> GT+ EQ -> GT -- This follows semver's rule!+ Just _ -> fallback+ where+ fallback :: Ordering+ fallback = compare (General $ vFromS s) (Complex m)+ instance Semantic Versioning where major f (Ideal v) = Ideal <$> major f v major f (General v) = General <$> major f v@@ -302,7 +349,7 @@ instance Monoid SemVer where mempty = SemVer 0 0 0 [] [] -#if __GLASGOW_HASKELL__ < 841+#if !MIN_VERSION_base(4,11,0) mappend = (<>) #endif @@ -341,7 +388,7 @@ instance Monoid VUnit where mempty = Str "" -#if __GLASGOW_HASKELL__ < 841+#if !MIN_VERSION_base(4,11,0) mappend = (<>) #endif @@ -401,7 +448,7 @@ instance Monoid PVP where mempty = PVP (0 :| []) -#if __GLASGOW_HASKELL__ < 841+#if !MIN_VERSION_base(4,11,0) mappend = (<>) #endif @@ -455,7 +502,7 @@ instance Monoid Version where mempty = Version Nothing [] [] -#if __GLASGOW_HASKELL__ < 841+#if !MIN_VERSION_base(4,11,0) mappend = (<>) #endif @@ -561,12 +608,44 @@ -- Unfortunately, @VChunk@s cannot be used here, as some developers have numbers -- like @1.003.04@ which make parsers quite sad. --+-- Some `Mess` values have a shape that is tantalizingly close to a `SemVer`.+-- Example: @1.6.0a+2014+m872b87e73dfb-1@. For values like these, we can extract+-- the semver-compatible values out with `messMajor`, etc.+-- -- Not guaranteed to have well-defined ordering (@Ord@) behaviour, but so far--- internal tests show consistency.+-- internal tests show consistency. `messMajor`, etc., are used internally where+-- appropriate to enhance accuracy. data Mess = VLeaf [T.Text] | VNode [T.Text] VSep Mess deriving stock (Eq, Show, Generic) deriving anyclass (NFData, Hashable) +-- | Try to extract the "major" version number from `Mess`, as if it were a+-- `SemVer`.+messMajor :: Mess -> Maybe Word+messMajor (VNode (m:_) _ _) = hush $ parse (digitsP <* eof) "Major" m+messMajor _ = Nothing++-- | Try to extract the "minor" version number from `Mess`, as if it were a+-- `SemVer`.+messMinor :: Mess -> Maybe Word+messMinor (VNode (_:m:_) _ _) = hush $ parse (digitsP <* eof) "Minor" m+messMinor _ = Nothing++-- | Try to extract the "patch" version number from `Mess`, as if it were a+-- `SemVer`.+messPatch :: Mess -> Maybe Word+messPatch (VNode (_:_:p:_) _ _) = hush $ parse (digitsP <* eof) "Patch" p+messPatch _ = Nothing++-- | Okay, fine, say `messPatch` couldn't find a nice value. But some `Mess`es+-- have a "proper" patch-plus-release-candidate value in their patch position,+-- which is parsable as a `VChunk`.+--+-- Example: @1.6.0a+2014+m872b87e73dfb-1@ We should be able to extract @0a@ safely.+messPatchChunk :: Mess -> Maybe VChunk+messPatchChunk (VNode (_:_:p:_) _ _) = hush $ parse chunk "Chunk" p+messPatchChunk _ = Nothing+ instance Ord Mess where compare (VLeaf l1) (VLeaf l2) = compare l1 l2 compare (VNode t1 _ _) (VLeaf t2) = compare t1 t2@@ -782,3 +861,7 @@ -- Yes, `text-show` exists, but this reduces external dependencies. showt :: Show a => a -> T.Text showt = T.pack . show++hush :: Either a b -> Maybe b+hush (Left _) = Nothing+hush (Right b) = Just b
README.md view
@@ -1,6 +1,7 @@ versions ======== + [](https://hackage.haskell.org/package/versions) [](http://stackage.org/nightly/package/versions) [](http://stackage.org/lts/package/versions)
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
test/Test.hs view
@@ -1,17 +1,18 @@-{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Main where -import BasePrelude hiding (Version, try)+import Data.Char (chr)+import Data.Either (isLeft)+import Data.Foldable (fold)+import Data.List (groupBy) import qualified Data.Text as T import Data.Versions+import Data.Void (Void) import Lens.Micro import Test.QuickCheck-import Test.QuickCheck.Checkers-import Test.QuickCheck.Classes import Test.Tasty import Test.Tasty.HUnit import Test.Tasty.QuickCheck@@ -33,16 +34,10 @@ f (Str _) (Str _) = True f _ _ = False -instance EqProp SemVer where- a =-= b = eq a b- instance Arbitrary VUnit where arbitrary = frequency [ (1, Digits . (+ 1) <$> arbitrary) , (1, s) ] where s = Str . T.pack . map unletter <$> resize 10 (listOf1 arbitrary) -instance EqProp VUnit where- a =-= b = eq a b- -- | An ASCII letter. newtype Letter = Letter { unletter :: Char } @@ -52,9 +47,6 @@ instance Arbitrary Version where arbitrary = Version <$> arbitrary <*> chunks <*> chunks -instance EqProp Version where- a =-= b = eq a b- -- | These don't need to parse as a SemVer. goodVers :: [T.Text] goodVers = [ "1", "1.2", "1.0rc0", "1.0rc1", "1.1rc1", "1.58.0-3", "44.0.2403.157-1"@@ -100,14 +92,10 @@ suite :: TestTree suite = testGroup "Tests" [ testGroup "Property Tests"- [ testGroup "SemVer - Monoid" $- map (uncurry testProperty) . unbatch $ monoid (SemVer 1 2 3 [] [])- , testProperty "SemVer - Arbitrary" $ \a -> semver (prettySemVer a) == Right a+ [ testProperty "SemVer - Arbitrary" $ \a -> semver (prettySemVer a) == Right a , testProperty "Version - Arbitrary" $ \a -> version (prettyVer a) == Right a -- , testGroup "Version - Monoid" $ -- map (\(name, test) -> testProperty name test) . unbatch $ monoid (Version (Just 1) [[digits 2], [digits 3]])- , testGroup "VUnit - Monoid" $- map (uncurry testProperty) . unbatch $ monoid (Digits 0) ] , testGroup "Unit Tests" [ testGroup "(Ideal) Semantic Versioning"@@ -150,6 +138,18 @@ , testGroup "Comparisons" $ map (\(a,b) -> testCase (T.unpack $ a <> " < " <> b) $ comp mess a b) $ zip messComps (tail messComps)+ , testGroup "SemVer-like Value Extraction"+ [ testCase "messMajor" $+ (hush (mess "1.6.0a+2014+m872b87e73dfb-1") >>= messMajor) @?= Just 1+ , testCase "messMinor" $+ (hush (mess "1.6.0a+2014+m872b87e73dfb-1") >>= messMinor) @?= Just 6+ , testCase "messPatch - Good" $+ (hush (mess "1.6.0+2014+m872b87e73dfb-1") >>= messPatch) @?= Just 0+ , testCase "messPatch - Bad" $+ (hush (mess "1.6.0a+2014+m872b87e73dfb-1") >>= messPatch) @?= Nothing+ , testCase "messPatchChunk" $+ (hush (mess "1.6.0a+2014+m872b87e73dfb-1") >>= messPatchChunk) @?= Just [Digits 0, Str "a"]+ ] ] , testGroup "Mixed Versioning" [ testGroup "Identification"@@ -171,6 +171,8 @@ , testCase "1.2.3-1 < 2+0007-1" $ comp versioning "1.2.3-1" "2+0007-1" , testCase "1.2.3r1-1 < 2+0007-1" $ comp versioning "1.2.3r1-1" "2+0007-1" , testCase "1.2-5 < 1.2.3-1" $ comp versioning "1.2-5" "1.2.3-1"+ , testCase "1.6.0a+2014+m872b87e73dfb-1 < 1.6.0-1"+ $ comp versioning "1.6.0a+2014+m872b87e73dfb-1" "1.6.0-1" ] ] , testGroup "Lenses and Traversals"@@ -250,3 +252,7 @@ versionGrab = manyTill anySingle (try finished) *> ver where finished = char '-' *> lookAhead digitChar ver = fmap Ideal semver' <|> fmap General version' <|> fmap Complex mess'++hush :: Either a b -> Maybe b+hush (Left _) = Nothing+hush (Right b) = Just b
versions.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.2 name: versions-version: 3.5.3+version: 3.5.4 synopsis: Types and parsers for software version numbers. description: A library for parsing and comparing software version numbers. We like to give@@ -21,7 +21,7 @@ the best constraints on comparisons. category: Data-homepage: https://gitlab.com/fosskers/versions+homepage: https://github.com/fosskers/versions author: Colin Woodbury maintainer: colin@fosskers.ca license: BSD-3-Clause@@ -56,8 +56,6 @@ hs-source-dirs: test ghc-options: -threaded -with-rtsopts=-N build-depends:- , base-prelude- , checkers >=0.4 , microlens >=0.4 , QuickCheck >=2.9 , tasty >=0.10.1.2