module Debian.Version.Common
( DebianVersion
, prettyDebianVersion
, ParseDebianVersion(..)
, parseDebianVersion'
, evr
, epoch
, version
, revision
, buildDebianVersion
, parseDV
) where
import Data.Char (ord, isDigit, isAlpha)
import Debian.Pretty (PP(..))
import Debian.Version.Internal
import Text.ParserCombinators.Parsec
import Text.Regex
import Text.PrettyPrint (Doc, render)
import Text.PrettyPrint.HughesPJClass (Pretty(pPrint), text)
prettyDebianVersion :: DebianVersion -> Doc
prettyDebianVersion (DebianVersion s _) = text s
instance Pretty (PP DebianVersion) where
pPrint = prettyDebianVersion . unPP
instance Eq DebianVersion where
(DebianVersion _ v1) == (DebianVersion _ v2) = v1 == v2
instance Ord DebianVersion where
compare (DebianVersion _ v1) (DebianVersion _ v2) = compare v1 v2
instance Show DebianVersion where
show v = "(Debian.Version.parseDebianVersion (" ++ show (render (prettyDebianVersion v)) ++ " :: String))"
order :: Char -> Int
order c
| isDigit c = 0
| isAlpha c = ord c
| c == '~' = 1
| otherwise = (ord c) + 256
compareNonNumeric :: [Char] -> [Char] -> Ordering
compareNonNumeric "" "" = EQ
compareNonNumeric "" ('~':_cs) = GT
compareNonNumeric ('~':_cs) "" = LT
compareNonNumeric "" _ = LT
compareNonNumeric _ "" = GT
compareNonNumeric (c1:cs1) (c2:cs2) =
if (order c1) == (order c2)
then compareNonNumeric cs1 cs2
else compare (order c1) (order c2)
instance Eq NonNumeric where
(NonNumeric s1 n1) == (NonNumeric s2 n2) =
case compareNonNumeric s1 s2 of
EQ -> n1 == n2
_o -> False
instance Ord NonNumeric where
compare (NonNumeric s1 n1) (NonNumeric s2 n2) =
case compareNonNumeric s1 s2 of
EQ -> compare n1 n2
o -> o
instance Eq Numeric where
(Numeric n1 mnn1) == (Numeric n2 mnn2) =
case compare n1 n2 of
EQ -> case compareMaybeNonNumeric mnn1 mnn2 of
EQ -> True
_ -> False
_ -> False
compareMaybeNonNumeric :: Maybe NonNumeric -> Maybe NonNumeric -> Ordering
compareMaybeNonNumeric mnn1 mnn2 =
case (mnn1, mnn2) of
(Nothing, Nothing) -> EQ
(Just (NonNumeric nn _), Nothing) -> compareNonNumeric nn ""
(Nothing, Just (NonNumeric nn _)) -> compareNonNumeric "" nn
(Just nn1, Just nn2) -> compare nn1 nn2
instance Ord Numeric where
compare (Numeric n1 mnn1) (Numeric n2 mnn2) =
case compare n1 n2 of
EQ -> compareMaybeNonNumeric mnn1 mnn2
o -> o
class ParseDebianVersion a where
parseDebianVersion :: a-> Either ParseError DebianVersion
parseDebianVersion' :: ParseDebianVersion string => string -> DebianVersion
parseDebianVersion' str = either (\e -> error (show e)) id (parseDebianVersion str)
parseDV :: CharParser () (Found Int, NonNumeric, Found NonNumeric)
parseDV =
do skipMany $ oneOf " \t"
e <- parseEpoch
upstreamVersion <- parseNonNumeric True True
debianRevision <- option (Simulated (NonNumeric "" (Simulated (Numeric 0 Nothing)))) (char '-' >> parseNonNumeric True False >>= return . Found)
return (e, upstreamVersion, debianRevision)
parseEpoch :: CharParser () (Found Int)
parseEpoch =
option (Simulated 0) (try (many1 digit >>= \d -> char ':' >> return (Found (read d))))
parseNonNumeric :: Bool -> Bool -> CharParser () NonNumeric
parseNonNumeric zeroOk upstream =
do nn <- (if zeroOk then many else many1) ((noneOf "-0123456789") <|> (if upstream then upstreamDash else pzero))
n <- parseNumeric upstream
return $ NonNumeric nn n
where
upstreamDash :: CharParser () Char
upstreamDash = try $ do char '-'
lookAhead $ (many (noneOf "- \n\t") >> char '-')
return '-'
parseNumeric :: Bool -> CharParser () (Found Numeric)
parseNumeric upstream =
do n <- many1 (satisfy isDigit)
nn <- option Nothing (parseNonNumeric False upstream >>= return . Just)
return $ Found (Numeric (read n) nn)
<|>
return (Simulated (Numeric 0 Nothing))
evr :: DebianVersion -> (Maybe Int, String, Maybe String)
evr (DebianVersion s _) =
let re = mkRegex "^(([0-9]+):)?(([^-]*)|((.*)-([^-]*)))$" in
case matchRegex re s of
Just ["", _, _, v, "", _, _] -> (Nothing, v, Nothing)
Just ["", _, _, _, _, v, r] -> (Nothing, v, Just r)
Just [_, e, _, v, "", _, _] -> (Just (read e), v, Nothing)
Just [_, e, _, _, _, v, r] -> (Just (read e), v, Just r)
_ -> error ("Invalid Debian Version String: " ++ s)
epoch :: DebianVersion -> Maybe Int
epoch v = case evr v of (x, _, _) -> x
version :: DebianVersion -> String
version v = case evr v of (_, x, _) -> x
revision :: DebianVersion -> Maybe String
revision v = case evr v of (_, _, x) -> x
buildDebianVersion :: Maybe Int -> String -> Maybe String -> DebianVersion
buildDebianVersion e v r =
either (error . show) (DebianVersion str) $ parse parseDV str str
where
str = (maybe "" (\ n -> show n ++ ":") e ++ v ++ maybe "" (\ s -> "-" ++ s) r)