1 {-# LANGUAGE DeriveDataTypeable #-}
2 {-# LANGUAGE DeriveGeneric #-}
5 module Distribution
.Types
.PkgconfigVersion
6 ( PkgconfigVersion
(..)
10 import Distribution
.Compat
.Prelude
13 import Distribution
.Parsec
14 import Distribution
.Pretty
15 import Distribution
.Utils
.Generic
(isAsciiAlphaNum
)
17 import qualified Data
.ByteString
as BS
18 import qualified Data
.ByteString
.Char8
as BS8
19 import qualified Distribution
.Compat
.CharParsing
as P
20 import qualified Text
.PrettyPrint
as PP
22 -- | @pkg-config@ versions.
24 -- In fact, this can be arbitrary 'BS.ByteString',
25 -- but 'Parsec' instance is a little pickier.
28 newtype PkgconfigVersion
= PkgconfigVersion BS
.ByteString
29 deriving (Generic
, Read, Show, Typeable
, Data
)
31 instance Eq PkgconfigVersion
where
32 PkgconfigVersion a
== PkgconfigVersion b
= rpmvercmp a b
== EQ
34 instance Ord PkgconfigVersion
where
35 PkgconfigVersion a `
compare` PkgconfigVersion b
= rpmvercmp a b
37 instance Binary PkgconfigVersion
38 instance Structured PkgconfigVersion
39 instance NFData PkgconfigVersion
where rnf
= genericRnf
41 instance Pretty PkgconfigVersion
where
42 pretty
(PkgconfigVersion bs
) = PP
.text
(BS8
.unpack bs
)
46 -- >>> simpleParsec "1.0.2n" :: Maybe PkgconfigVersion
47 -- Just (PkgconfigVersion "1.0.2n")
49 -- >>> simpleParsec "0.3.5+ds" :: Maybe PkgconfigVersion
51 instance Parsec PkgconfigVersion
where
52 parsec
= PkgconfigVersion
. BS8
.pack
<$> P
.munch1 predicate
54 predicate c
= isAsciiAlphaNum c || c
== '.' || c
== '-'
56 -------------------------------------------------------------------------------
57 -- rpmvercmp - pure Haskell implementation
58 -------------------------------------------------------------------------------
60 -- | Compare two version strings as @pkg-config@ would compare them.
63 rpmvercmp
:: BS
.ByteString
-> BS
.ByteString
-> Ordering
64 rpmvercmp a b
= go0
(BS
.unpack a
) (BS
.unpack b
)
66 go0
:: [Word8
] -> [Word8
] -> Ordering
67 -- if there is _any_ trailing "garbage", it seems to affect result
68 -- https://github.com/haskell/cabal/issues/6805
72 go0 xs ys
= go1
(dropNonAlnum8 xs
) (dropNonAlnum8 ys
)
74 go1
:: [Word8
] -> [Word8
] -> Ordering
80 let (xs1
, xs2
) = span isDigit8 xs
81 (ys1
, ys2
) = span isDigit8 ys
82 in -- numeric segments are always newer than alpha segments
85 else compareInt xs1 ys1
<> go0 xs2 ys2
88 let (xs1
, xs2
) = span isAlpha8 xs
89 (ys1
, ys2
) = span isAlpha8 ys
92 else compareStr xs1 ys1
<> go0 xs2 ys2
95 compareInt
:: [Word8
] -> [Word8
] -> Ordering
97 -- whichever number has more digits wins
98 compare (length xs
') (length ys
')
100 -- equal length: use per character compare, "strcmp"
103 -- drop leading zeros
104 xs
' = dropWhile (== 0x30) xs
105 ys
' = dropWhile (== 0x30) ys
108 compareStr
:: [Word8
] -> [Word8
] -> Ordering
111 dropNonAlnum8
:: [Word8
] -> [Word8
]
112 dropNonAlnum8
= dropWhile (\w
-> not (isDigit8 w || isAlpha8 w
))
114 isDigit8
:: Word8
-> Bool
115 isDigit8 w
= 0x30 <= w
&& w
<= 0x39
117 isAlpha8
:: Word8
-> Bool
118 isAlpha8 w
= (0x41 <= w
&& w
<= 0x5A) ||
(0x61 <= w
&& w
<= 0x7A)