module Debian.Control.Policy
(
DebianControl(unDebianControl)
, validateDebianControl
, unsafeDebianControl
, parseDebianControlFromFile
, parseDebianControl
, ControlFileError(..)
, HasDebianControl(debianControl)
, debianSourceParagraph
, debianBinaryParagraphs
, debianPackageParagraphs
, debianPackageNames
, debianSourcePackageName
, debianBinaryPackageNames
, debianRelations
, debianBuildDeps
, debianBuildDepsIndep
) where
import Control.Exception (Exception, throw)
import Control.Monad.Catch (MonadCatch, try)
import Data.List (intercalate)
import Data.Text (Text)
import Data.Typeable (Typeable)
import Data.ListLike (toList)
import Debian.Control.Common (Control'(..), Paragraph'(..), Field'(..), fieldValue, ControlFunctions(parseControlFromFile, parseControl))
import Debian.Control.Text ()
import Debian.Loc (__LOC__)
import Debian.Pretty (prettyShow)
import Debian.Relation (SrcPkgName(..), BinPkgName(..), Relations, parseRelations)
import Debian.Relation.Text ()
import Language.Haskell.TH (Loc(..))
import Prelude hiding (ioError)
import Text.Parsec.Error (ParseError)
data DebianControl = DebianControl {unDebianControl :: Control' Text}
instance Show DebianControl where
show c = "(parseDebianControl \"\" " ++ show (prettyShow (unDebianControl c)) ++ ")"
validateDebianControl :: MonadCatch m => Control' Text -> m (Either ControlFileError DebianControl)
validateDebianControl ctl =
try (do _ <- return $ debianPackageNames (DebianControl ctl)
_ <- return $ debianBuildDeps (DebianControl ctl)
_ <- return $ debianBuildDepsIndep (DebianControl ctl)
return ()) >>=
return . either Left (\ _ -> Right $ DebianControl ctl)
unsafeDebianControl :: Control' Text -> DebianControl
unsafeDebianControl = DebianControl
parseDebianControl :: MonadCatch m => String -> Text -> m (Either ControlFileError DebianControl)
parseDebianControl sourceName s = either (return . Left . ParseControlError [$__LOC__]) validateDebianControl (parseControl sourceName s)
parseDebianControlFromFile :: FilePath -> IO (Either ControlFileError DebianControl)
parseDebianControlFromFile controlPath =
try (parseControlFromFile controlPath) >>=
either (return . Left . IOError [$__LOC__])
(either (return . Left . ParseControlError [$__LOC__]) validateDebianControl)
class Show a => HasDebianControl a where
debianControl :: a -> DebianControl
instance HasDebianControl DebianControl where
debianControl = id
class HasControl a where
control :: a -> Control' Text
instance HasControl (Control' Text) where
control = id
instance HasControl DebianControl where
control = unDebianControl
data ControlFileError
= NoParagraphs {locs :: [Loc]}
| NoBinaryParagraphs {locs :: [Loc], ctl :: String}
| MissingField {locs :: [Loc], field :: String}
| ParseRelationsError {locs :: [Loc], parseError :: ParseError}
| ParseControlError {locs :: [Loc], parseError :: ParseError}
| IOError {locs :: [Loc], ioError :: IOError}
deriving Typeable
instance Show ControlFileError where
show (NoParagraphs {..}) = intercalate ", " (map showLoc locs) ++ ": NoParagraphs"
show (NoBinaryParagraphs {..}) = intercalate ", " (map showLoc locs) ++ ": NoBinaryParagraphs (" ++ show ctl ++ ")"
show (MissingField {..}) = intercalate ", " (map showLoc locs) ++ ": MissingField " ++ show field
show (ParseRelationsError {..}) = intercalate ", " (map showLoc locs) ++ ": ParseRelationsError " ++ show parseError
show (ParseControlError {..}) = intercalate ", " (map showLoc locs) ++ ": ParseControlError " ++ show parseError
show (IOError {..}) = intercalate ", " (map showLoc locs) ++ ": IOError " ++ show ioError
showLoc :: Loc -> String
showLoc x = show (loc_filename x) ++ "(line " ++ show (fst (loc_start x)) ++ ", column " ++ show (snd (loc_start x)) ++ ")"
instance Exception ControlFileError
instance Eq ControlFileError where
_ == _ = False
debianPackageParagraphs :: HasDebianControl a => a -> (Paragraph' Text, [Paragraph' Text])
debianPackageParagraphs ctl =
case removeCommentParagraphs ctl of
DebianControl (Control [_]) -> throw $ NoBinaryParagraphs [$__LOC__] (show ctl)
DebianControl (Control []) -> throw $ NoParagraphs [$__LOC__]
DebianControl (Control (sourceParagraph : binParagraphs)) -> (sourceParagraph, binParagraphs)
removeCommentParagraphs :: HasDebianControl a => a -> DebianControl
removeCommentParagraphs c =
DebianControl (Control (filter (not . isCommentParagraph) (unControl (unDebianControl (debianControl c)))))
where
isCommentParagraph (Paragraph fields) = all isCommentField fields
isCommentField (Comment _) = True
isCommentField _ = False
debianSourceParagraph :: HasDebianControl a => a -> Paragraph' Text
debianSourceParagraph = fst . debianPackageParagraphs
debianBinaryParagraphs :: HasDebianControl a => a -> [Paragraph' Text]
debianBinaryParagraphs = snd . debianPackageParagraphs
debianPackageNames :: HasDebianControl a => a -> (SrcPkgName, [BinPkgName])
debianPackageNames c =
let (srcParagraph, binParagraphs) = debianPackageParagraphs c in
(mapFieldValue (SrcPkgName . toList) "Source" srcParagraph, map (mapFieldValue (BinPkgName . toList) "Package") binParagraphs)
debianSourcePackageName :: HasDebianControl a => a -> SrcPkgName
debianSourcePackageName = fst . debianPackageNames
debianBinaryPackageNames :: HasDebianControl a => a -> [BinPkgName]
debianBinaryPackageNames = snd . debianPackageNames
debianBuildDepsIndep :: HasDebianControl a => a -> Maybe Relations
debianBuildDepsIndep ctl = either throw id $ debianRelations "Build-Depends-Indep" (debianControl ctl)
debianBuildDeps :: HasDebianControl a => a -> Maybe Relations
debianBuildDeps ctl = either throw id $ debianRelations "Build-Depends" (debianControl ctl)
fieldValue' :: ControlFunctions text => String -> Paragraph' text -> text
fieldValue' fieldName paragraph = maybe (throw $ MissingField [$__LOC__] fieldName) id $ fieldValue fieldName paragraph
debianRelations :: HasDebianControl a => String -> a -> Either ControlFileError (Maybe Relations)
debianRelations fieldName ctl = maybe (Right Nothing) (either (Left . ParseRelationsError [$__LOC__]) (Right . Just) . parseRelations) $ fieldValue fieldName (debianSourceParagraph ctl)
mapFieldValue :: (Text -> a) -> String -> Paragraph' Text -> a
mapFieldValue f fieldName paragraph = f $ fieldValue' fieldName paragraph