module Debian.Package.Data.Packages
( DebianVersion, versionFromHackageVersion, readDebianVersion, origVersion', isNative'
, Source, mkSource, sourceName, version, origVersion, isNative
, origArchiveName, nativeArchiveName, sourceDirName, deriveHackageVersion
, parseChangeLog
, PackageType (..), takeChangesType, isSourcePackage, isBinaryPackage
, Control (..), parseControlEntry, parseControl
, HaskellPackage, hackage, package
, haskellPackageDefault, haskellPackageFromPackage
) where
import Control.Applicative ((<$>), pure, (<*>), (*>), (<*), empty, (<|>), many, some, optional)
import Control.Monad.Trans.State (StateT, runStateT, get, put)
import Data.Maybe (listToMaybe, maybeToList, mapMaybe)
import Data.Char (isSpace, isDigit)
import Data.Version (Version (Version, versionBranch), showVersion)
import Data.List.Split (splitOn)
import System.FilePath ((<.>), takeFileName, splitExtension)
import Debian.Package.Data.Hackage
(HackageVersion, mkHackageVersion', hackageVersionNumbers,
Hackage, mkHackageDefault, NameRule (Simple), debianNamesFromSourceName)
type Parser = StateT String Maybe
satisfy :: (Char -> Bool) -> Parser Char
satisfy p = do
s <- get
case s of
c:cs -> if p c
then put cs *> pure c
else empty
[] -> empty
_look :: Parser String
_look = get
eof :: Parser ()
eof = do
s <- get
case s of
[] -> pure ()
_:_ -> empty
runParser :: Parser a -> String -> Maybe (a, String)
runParser = runStateT
anyChar :: Parser Char
anyChar = satisfy (const True)
char :: Char -> Parser Char
char x = satisfy (== x)
notChar :: Char -> Parser Char
notChar x = satisfy (/= x)
space :: Parser Char
space = char ' '
digit :: Parser Char
digit = satisfy isDigit
int :: Parser Int
int = read <$> some digit
string :: String -> Parser String
string = mapM char
data DebianVersion
= DebianNative Version (Maybe Int)
| DebianNonNative Version String
debianNativeVersion :: [Int] -> Maybe Int -> DebianVersion
debianNativeVersion v = DebianNative (Version v [])
debianNonNativeVersion :: [Int] -> String -> DebianVersion
debianNonNativeVersion v = DebianNonNative (Version v [])
versionFromHackageVersion :: HackageVersion -> Maybe String -> DebianVersion
versionFromHackageVersion hv = d where
d (Just rev) = debianNonNativeVersion ns rev
d Nothing = debianNativeVersion ns Nothing
ns = hackageVersionNumbers hv
origVersion' :: DebianVersion -> Version
origVersion' = d where
d (DebianNative v _) = v
d (DebianNonNative v _) = v
isNative' :: DebianVersion -> Bool
isNative' = d where
d (DebianNative _ _) = True
d (DebianNonNative _ _) = False
parseVersion' :: Parser Version
parseVersion' =
Version
<$> ((:) <$> int <*> many (char '.' *> int))
<*> pure []
parseDebianVersion :: Parser DebianVersion
parseDebianVersion = do
v <- parseVersion'
(DebianNonNative v <$> (char '-' *> some (satisfy (not . isSpace)))
<|>
DebianNative v <$> optional (string "+nmu" *> int))
_testParseDebianVersion :: [Maybe (DebianVersion, String)]
_testParseDebianVersion =
[ runParser parseDebianVersion s | s <- [ "1.23.3-4", "1.23", "12.3+nmu2" ] ]
instance Show DebianVersion where
show = d where
d (DebianNative v nr) = showVersion v ++ maybe "" (("+nmu" ++) . show) nr
d (DebianNonNative v r) = showVersion v ++ '-': r
instance Read DebianVersion where
readsPrec _ = maybeToList . runParser parseDebianVersion
readMaybe' :: Read a => String -> Maybe a
readMaybe' = fmap fst . listToMaybe . filter ((== "") . snd) . reads
readDebianVersion :: String -> Maybe DebianVersion
readDebianVersion = readMaybe'
data Source = Source String DebianVersion deriving Show
mkSource :: String -> DebianVersion -> Source
mkSource = Source
sourceName :: Source -> String
sourceName (Source n _) = n
version :: Source -> DebianVersion
version (Source _ v) = v
origVersion :: Source -> Version
origVersion = origVersion' . version
isNative :: Source -> Bool
isNative = isNative' . version
origArchiveName :: Source -> FilePath
origArchiveName pkg = sourceName pkg ++ '_' : showVersion (origVersion pkg) <.> "orig" <.> "tar" <.> "gz"
nativeArchiveName :: Source -> String
nativeArchiveName pkg = sourceName pkg ++ '_' : show (version pkg) <.> "tar" <.> "gz"
sourceDirName :: Source -> FilePath
sourceDirName pkg = sourceName pkg ++ '-' : showVersion (origVersion pkg)
deriveHackageVersion :: Source -> HackageVersion
deriveHackageVersion = mkHackageVersion' . versionBranch . origVersion where
parseColonLine :: String -> Maybe (String, String)
parseColonLine =
(fmap fst .) . runParser $
(,) <$> some (notChar ':') <*> (char ':' *> many space *> many anyChar <* eof)
parseChangeLog :: String
-> Maybe Source
parseChangeLog log' = do
deb <- mayDebSrc
dver <- mayDebVer
return $ mkSource deb dver
where
pairs = mapMaybe parseColonLine . lines $ log'
lookup' = (`lookup` pairs)
mayDebSrc = lookup' "Source"
mayDebVer = do
dverS <- lookup' "Version"
readDebianVersion dverS
data PackageType
= PackageArch (Maybe String)
| PackageAll
| PackageSource
deriving (Eq, Show)
takeChangesType :: FilePath -> Maybe PackageType
takeChangesType path = d . splitExtension $ takeFileName path where
d (n, ".changes") = case xs of
[_, _, a] -> case a of
"all" -> Just PackageAll
"source" -> Just PackageSource
_ -> Just . PackageArch $ Just a
_ -> Nothing
where xs = splitOn "_" n
d (_, _) = Nothing
isSourcePackage :: PackageType -> Bool
isSourcePackage = d where
d (PackageArch _) = False
d PackageAll = False
d PackageSource = True
isBinaryPackage :: PackageType -> Bool
isBinaryPackage = not . isSourcePackage
data Control =
Control
{ controlSource :: String
, controlArch :: [String]
, controlAll :: [String]
} deriving (Eq, Show)
parseControlEntry :: [String] -> Maybe (PackageType, String)
parseControlEntry b =
do a <- lookup' "Architecture"
p <- lookup' "Package"
Just $ if a == "all"
then (PackageAll, p)
else (PackageArch $ Just a, p)
<|>
do s <- lookup' "Source"
Just (PackageSource, s)
where ps = mapMaybe parseColonLine b
lookup' = (`lookup` ps)
packagesPartition :: [(PackageType, a)] -> ([a], [a], [a])
packagesPartition = rec' where
rec' [] = ([], [], [])
rec' (x:xs) = case x of
(PackageSource, a) -> (a:p, q, r)
(PackageArch _, a) -> (p, a:q, r)
(PackageAll , a) -> (p, q, a:r)
where (p, q, r) = rec' xs
parseControl :: String -> Maybe Control
parseControl in' = do
let (src, arch, all') =
packagesPartition . mapMaybe parseControlEntry
. filter (not . null) . splitOn [""] . lines $ in'
s <- listToMaybe src
Just $ Control s arch all'
data HaskellPackage = HaskellPackage Hackage Source deriving Show
hackage :: HaskellPackage -> Hackage
hackage (HaskellPackage h _) = h
package :: HaskellPackage -> Source
package (HaskellPackage _ p) = p
haskellPackageDefault :: NameRule
-> String
-> HackageVersion
-> Maybe String
-> HaskellPackage
haskellPackageDefault rule hname hver mayDevRev =
HaskellPackage
(mkHackageDefault rule hname hver)
(mkSource sn (versionFromHackageVersion hver mayDevRev))
where
(sn, _) = debianNamesFromSourceName rule hname
haskellPackageFromPackage :: String
-> Source
-> HaskellPackage
haskellPackageFromPackage hname pkg = HaskellPackage hkg pkg where
hv = deriveHackageVersion pkg
hkg = mkHackageDefault Simple hname hv