module Hackage.Utils where import Distribution.Compat.ReadP (ReadP, readP_to_S, pfail, get, look, choice, (+++)) import Distribution.Package (PackageIdentifier(..), parsePackageId) import Distribution.ParseUtils (Field(..), FieldDescr(..), ParseResult(..), PError , field, liftField, readFields , showDependency, parseDependency , warning, lineNo, locatedErrorMsg) import Distribution.Version (Version(..), Dependency(..), VersionRange(..)) import Control.Exception import Control.Monad (foldM, liftM, guard) import Data.Char (isSpace, toLower) import Data.List (intersperse) import Data.Maybe (listToMaybe) import System.IO.Error (isDoesNotExistError) import Text.PrettyPrint.HughesPJ (Doc, render, vcat, text, (<>), (<+>)) readFileIfExists :: FilePath -> IO (Maybe String) readFileIfExists path = catchJust fileNotFoundExceptions (fmap Just (readFile path)) (\_ -> return Nothing) fileNotFoundExceptions :: Exception -> Maybe IOError fileNotFoundExceptions e = ioErrors e >>= \ioe -> guard (isDoesNotExistError ioe) >> return ioe showPError :: PError -> String showPError err = let (ml,msg) = locatedErrorMsg err in maybe "" (\l -> "On line " ++ show l ++ ": ") ml ++ msg readPToMaybe :: ReadP r a -> String -> Maybe a readPToMaybe p str = listToMaybe [ r | (r,s) <- readP_to_S p str, all isSpace s ] ignoreWarnings :: ParseResult a -> ParseResult a ignoreWarnings (ParseOk _ x) = ParseOk [] x ignoreWarnings r = r parseBasicStanza :: [FieldDescr a] -> a -> String -> ParseResult a parseBasicStanza fields empty inp = readFields inp >>= foldM (setField fields) empty setField :: [FieldDescr a] -> a -> Field -> ParseResult a setField fs x (F line f val) = case lookupFieldDescr fs f of Nothing -> do warning ("Unrecognized field " ++ f ++ " on line " ++ show line) return x Just (FieldDescr _ _ set) -> set line val x setField _ x s = do warning ("Unrecognized stanza on line " ++ show (lineNo s)) return x lookupFieldDescr :: [FieldDescr a] -> String -> Maybe (FieldDescr a) lookupFieldDescr fs n = listToMaybe [f | f@(FieldDescr name _ _) <- fs, name == n] boolField :: String -> (a -> Bool) -> (Bool -> a -> a) -> FieldDescr a boolField name g s = liftField g s $ field name showBool readBool where showBool :: Bool -> Doc showBool True = text "true" showBool False = text "false" readBool :: ReadP r Bool readBool = choice [ stringNoCase "true" >> return True , stringNoCase "false" >> return False , stringNoCase "yes" >> return True , stringNoCase "no" >> return False] showFields :: [FieldDescr a] -> a -> String showFields fs x = render $ vcat [ text name <> text ":" <+> g x | FieldDescr name g _ <- fs] stringNoCase :: String -> ReadP r String stringNoCase this = look >>= scan this where scan [] _ = return this scan (x:xs) (y:ys) | toLower x == toLower y = get >> scan xs ys scan _ _ = pfail showDependencies :: [Dependency] -> String showDependencies = concat . intersperse ", " . map (show . showDependency) parseDependencyOrPackageId :: ReadP r Dependency parseDependencyOrPackageId = parseDependency +++ liftM pkgToDep parsePackageId where pkgToDep p = case pkgVersion p of Version [] _ -> Dependency (pkgName p) AnyVersion version -> Dependency (pkgName p) (ThisVersion version)