-------------------------------------------------------------------------------- {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE Rank2Types #-} module Hakyll.Core.Rules.Internal ( RulesRead (..) , RuleSet (..) , RulesState (..) , emptyRulesState , Rules (..) , runRules ) where -------------------------------------------------------------------------------- import Control.Applicative (Applicative, (<$>)) import Control.Monad.Reader (ask) import Control.Monad.RWS (RWST, runRWST) import Control.Monad.Trans (liftIO) import Data.Monoid (Monoid, mappend, mempty) import Data.Set (Set) import qualified Data.Set as S -------------------------------------------------------------------------------- import Hakyll.Core.Compiler.Internal import Hakyll.Core.Identifier import Hakyll.Core.Identifier.Pattern import Hakyll.Core.Item.SomeItem import Hakyll.Core.Metadata import Hakyll.Core.Provider import Hakyll.Core.Routes -------------------------------------------------------------------------------- data RulesRead = RulesRead { rulesProvider :: Provider , rulesMatches :: [Identifier] , rulesVersion :: Maybe String } -------------------------------------------------------------------------------- data RuleSet = RuleSet { -- | Accumulated routes rulesRoutes :: Routes , -- | Accumulated compilers rulesCompilers :: [(Identifier, Compiler SomeItem)] , -- | A set of the actually used files rulesResources :: Set Identifier } -------------------------------------------------------------------------------- instance Monoid RuleSet where mempty = RuleSet mempty mempty mempty mappend (RuleSet r1 c1 s1) (RuleSet r2 c2 s2) = RuleSet (mappend r1 r2) (mappend c1 c2) (mappend s1 s2) -------------------------------------------------------------------------------- data RulesState = RulesState { rulesRoute :: Maybe Routes , rulesCompiler :: Maybe (Compiler SomeItem) } -------------------------------------------------------------------------------- emptyRulesState :: RulesState emptyRulesState = RulesState Nothing Nothing -------------------------------------------------------------------------------- -- | The monad used to compose rules newtype Rules a = Rules { unRules :: RWST RulesRead RuleSet RulesState IO a } deriving (Monad, Functor, Applicative) -------------------------------------------------------------------------------- instance MonadMetadata Rules where getMetadata identifier = Rules $ do provider <- rulesProvider <$> ask liftIO $ resourceMetadata provider identifier getMatches pattern = Rules $ do provider <- rulesProvider <$> ask return $ filterMatches pattern $ resourceList provider -------------------------------------------------------------------------------- -- | Run a Rules monad, resulting in a 'RuleSet' runRules :: Rules a -> Provider -> IO RuleSet runRules rules provider = do (_, _, ruleSet) <- runRWST (unRules rules) env emptyRulesState case findDuplicate (map fst $ rulesCompilers ruleSet) of Nothing -> return ruleSet Just id' -> error $ "Hakyll.Core.Rules.Internal: two different rules for " ++ show id' ++ " exist, bailing out" where env = RulesRead { rulesProvider = provider , rulesMatches = [] , rulesVersion = Nothing } -------------------------------------------------------------------------------- findDuplicate :: Ord a => [a] -> Maybe a findDuplicate = go S.empty where go _ [] = Nothing go s (x : xs) | x `S.member` s = Just x | otherwise = go (S.insert x s) xs