packages feed

fquery-0.2.2: Adelie/Depend.hs

-- Depend.hs
--
-- Module for parsing DEPEND and RDEPEND files, located in
-- portageDB/category/package/.

module Adelie.Depend (
  Version,
  Dependency(..),

  dependFromCatName,
  readDepend,
  putDependency
) where

import qualified Data.Char as C
import Data.List (nub)
import qualified Debug.Trace as DT
import Text.ParserCombinators.Parsec
import Text.ParserCombinators.Parsec.Language
import Text.ParserCombinators.Parsec.Token

import Adelie.Portage
import qualified Adelie.Util as E

type Version = String

data Dependency
  = GreaterEqual  String Version -- '>=c/p-v'
  | Greater       String Version -- '>c/p-v'
  | Equal         String Version -- '=c/p-v*'
  | LessEqual     String Version -- '<=c/p-v'
  | Less          String Version -- '<c/p-v'
  | Pinned        String Version -- '~c/p-v'
  | Blocker       String         -- '!c/p-v'
  | Any           String         -- 'c/p'
    deriving (Eq, Show)

----------------------------------------------------------------

dependFromCatName :: (String, String) -> String
dependFromCatName (cat, name) = concatPath [portageDB,cat,name,"RDEPEND"]

----------------------------------------------------------------

readDepend :: FilePath -> [String] -> IO [Dependency]
readDepend fn iUse = do
  r <- start_parser fn `E.catchIOE` (\ _ -> return $ Right [])
  case r of
    Left err -> putStr "Parse error at " >> print err >> error "Aborting"
    Right x  -> return $ nub x
  where start_parser = parseFromFile (dependParser iUse)

----------------------------------------------------------------

putDependency :: Dependency -> IO ()
putDependency (GreaterEqual p v) = putStr $ ">=" ++ p ++ '-':v
putDependency (Greater p v)      = putStr $ ">"  ++ p ++ '-':v
putDependency (Equal p v)        = putStr $ "="  ++ p ++ '-':v
putDependency (LessEqual p v)    = putStr $ "<=" ++ p ++ '-':v
putDependency (Less p v)         = putStr $ "<"  ++ p ++ '-':v
putDependency (Pinned p v)       = putStr $ "~"  ++ p ++ '-':v
putDependency (Blocker p)        = putStr $ "!"  ++ p
putDependency (Any p)            = putStr p

----------------------------------------------------------------

dependParser :: [String] -> Parser [Dependency]
dependParser iUse = do
  _skip <- spaces
  packages <- many (dependParser' iUse)
  eof
  return $ concat packages

dependParser' :: [String] -> Parser [Dependency]
dependParser' iUse = lexeme tp pp
  where tp = makeTokenParser emptyDef
        pp = parseOr iUse
         <|> parsePackageOrUse iUse

parseOr :: [String] -> Parser [Dependency]
parseOr iUse = (string "||" >> spaces >> parseBrackets iUse)
             <|>
               parseBrackets iUse

parsePackageOrUse :: [String] -> Parser [Dependency]
parsePackageOrUse iUse =
  do { p <- parsePackageOrUseWord
     ; do { _ <- char '?'        -- useFlag
          ; spaces
          ; r <- parseBrackets iUse
          ; let filt | head p == '!' = not $ tail p `elem` iUse
                     | otherwise     = p `elem` iUse
          ; if filt
              then return r
              else return []
          }
   <|> return [toDependency p]
     }

parsePackageOrUseWord :: Parser String
parsePackageOrUseWord = do { result <- many1 (satisfy cond)
                           -- skip[use]
                           -- TODO: add it to output
                           ; _ <- optionMaybe (do { _use <- between (char '[') (char ']') $ many1 (satisfy (/= ']'))
                                               -- ; return use
                                               ; return ()
                                               })
                           ; return result
                           }
  where
    cond '?' = False
    cond ')' = False
    cond '[' = False
    cond x = not $ C.isSpace x

parseBrackets :: [String] -> Parser [Dependency]
parseBrackets iUse = do { _ <- char '('
                        ; spaces
                        ; r <- manyTill (dependParser' iUse) (try (char ')'))
                        ; return $ concat r
                        }

----------------------------------------------------------------

breakVersion :: String -> (String, String)
breakVersion str = (n, v)
  where n = dropVersion str
        v = drop (length n+1) str

toDependency :: String -> Dependency
toDependency s =
    case s of
        ('>':'=':str) ->
            let (n, v) = breakVersion str
            in GreaterEqual n v

        ('>':str) ->
            let (n, v) = breakVersion str
            in Greater n v

        ('=':str) ->
            let (n, v) = breakVersion str
            in Equal n v

        ('<':'=':str) ->
            let (n, v) = breakVersion str
            in LessEqual n v

        ('<':str) ->
            let (n, v) = breakVersion str
            in Less n v

        -- TODO: can be any atom expression
        ('~':str) ->
            let (n, v) = breakVersion str
            in Pinned n v

        -- TODO: can be any atom expression
        ('!':n) ->
            Blocker n

        n@(c:_) | C.isAlpha c ->
            Any n

        n -> DT.trace ("FIXME: unknown atom type: " ++ show n) $
            Any n