{-# LANGUAGE RecordWildCards, ScopedTypeVariables #-}
{-# LANGUAGE GeneralizedNewtypeDeriving, ConstraintKinds #-}
{-# LANGUAGE ExistentialQuantification, RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
module Development.Shake.Internal.Core.Rules(
Rules, runRules,
RuleResult, addBuiltinRule, addBuiltinRuleEx, noLint,
getShakeOptionsRules, userRuleMatch,
getUserRules, addUserRule, alternatives, priority,
action, withoutActions
) where
import Control.Applicative
import Data.Tuple.Extra
import Control.Monad.Extra
import Control.Monad.Fix
import Control.Monad.IO.Class
import Control.Monad.Trans.Class
import Control.Monad.Trans.Reader
import Control.Monad.Trans.Writer.Strict
import Development.Shake.Classes
import General.Binary
import Data.Typeable.Extra
import Data.Function
import Data.List.Extra
import qualified Data.HashMap.Strict as Map
import Data.Maybe
import System.IO.Extra
import System.IO.Unsafe
import Data.Semigroup (Semigroup (..))
import Data.Monoid hiding ((<>))
import qualified Data.ByteString.Lazy as LBS
import qualified Data.Binary.Builder as Bin
import Data.Binary.Put
import Data.Binary.Get
import General.ListBuilder
import Development.Shake.Internal.Core.Types
import Development.Shake.Internal.Core.Monad
import Development.Shake.Internal.Value
import Development.Shake.Internal.Options
import Development.Shake.Internal.Errors
import Prelude
getUserRules :: Typeable a => Action (UserRule a)
getUserRules = f where
f :: forall a . Typeable a => Action (UserRule a)
f = do
Global{..} <- Action getRO
return $ case Map.lookup (typeRep (Proxy :: Proxy a)) globalUserRules of
Nothing -> Unordered []
Just (UserRule_ r) -> fromJust $ cast r
getShakeOptionsRules :: Rules ShakeOptions
getShakeOptionsRules = Rules $ lift ask
userRuleMatch :: UserRule a -> (a -> Maybe b) -> [b]
userRuleMatch u test = head $ (map snd $ reverse $ groupSort $ f Nothing $ fmap test u) ++ [[]]
where
f :: Maybe Double -> UserRule (Maybe a) -> [(Double,a)]
f p (UserRule x) = maybe [] (\x -> [(fromMaybe 1 p,x)]) x
f p (Unordered xs) = concatMap (f p) xs
f p (Priority p2 x) = f (Just $ fromMaybe p2 p) x
f p (Alternative x) = case f p x of
[] -> []
xs -> [(maximum $ map fst xs, snd $ head xs)]
newtype Rules a = Rules (WriterT SRules (ReaderT ShakeOptions IO) a)
deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
newRules :: SRules -> Rules ()
newRules = Rules . tell
modifyRules :: (SRules -> SRules) -> Rules () -> Rules ()
modifyRules f (Rules r) = Rules $ censor f r
runRules :: ShakeOptions -> Rules () -> IO ([Action ()], Map.HashMap TypeRep BuiltinRule, Map.HashMap TypeRep UserRule_)
runRules opts (Rules r) = do
SRules{..} <- runReaderT (execWriterT r) opts
return (runListBuilder actions, builtinRules, userRules)
data SRules = SRules
{actions :: !(ListBuilder (Action ()))
,builtinRules :: !(Map.HashMap TypeRep BuiltinRule)
,userRules :: !(Map.HashMap TypeRep UserRule_)
}
instance Semigroup SRules where
(SRules x1 x2 x3) <> (SRules y1 y2 y3) = SRules (mappend x1 y1) (Map.unionWithKey f x2 y2) (Map.unionWith g x3 y3)
where
f k _ _ = unsafePerformIO $ errorRuleDefinedMultipleTimes k
g (UserRule_ x) (UserRule_ y) = UserRule_ $ Unordered $ fromUnordered x ++ fromUnordered (fromJust $ cast y)
fromUnordered (Unordered xs) = xs
fromUnordered x = [x]
instance Monoid SRules where
mempty = SRules mempty Map.empty Map.empty
mappend = (<>)
instance Semigroup a => Semigroup (Rules a) where
(<>) = liftA2 (<>)
instance (Semigroup a, Monoid a) => Monoid (Rules a) where
mempty = return mempty
mappend = (<>)
addUserRule :: Typeable a => a -> Rules ()
addUserRule r = newRules mempty{userRules = Map.singleton (typeOf r) $ UserRule_ $ UserRule r}
noLint :: BuiltinLint key value
noLint _ _ = return Nothing
type family RuleResult key
addBuiltinRule :: (RuleResult key ~ value, ShakeValue key, ShakeValue value) => BuiltinLint key value -> BuiltinRun key value -> Rules ()
addBuiltinRule = addBuiltinRuleInternal $ BinaryOp
(putEx . Bin.toLazyByteString . execPut . put)
(runGet get . LBS.fromChunks . return)
addBuiltinRuleEx :: (RuleResult key ~ value, ShakeValue key, BinaryEx key, Typeable value, NFData value, Show value) => BuiltinLint key value -> BuiltinRun key value -> Rules ()
addBuiltinRuleEx = addBuiltinRuleInternal $ BinaryOp putEx getEx
addBuiltinRuleInternal :: (RuleResult key ~ value, ShakeValue key, Typeable value, NFData value, Show value) => BinaryOp key -> BuiltinLint key value -> BuiltinRun key value -> Rules ()
addBuiltinRuleInternal binary lint (run :: BuiltinRun key value) = do
let k = Proxy :: Proxy key
v = Proxy :: Proxy value
let run_ k v b = fmap newValue <$> run (fromKey k) v b
let lint_ k v = lint (fromKey k) (fromValue v)
let binary_ = BinaryOp (putOp binary . fromKey) (newKey . getOp binary)
newRules mempty{builtinRules = Map.singleton (typeRep k) $ BuiltinRule run_ lint_ (typeRep v) binary_}
priority :: Double -> Rules () -> Rules ()
priority d = modifyRules $ \s -> s{userRules = Map.map f $ userRules s}
where f (UserRule_ s) = UserRule_ $ Priority d s
alternatives :: Rules () -> Rules ()
alternatives = modifyRules $ \r -> r{userRules = Map.map f $ userRules r}
where f (UserRule_ s) = UserRule_ $ Alternative s
action :: Action a -> Rules ()
action a = newRules mempty{actions=newListBuilder $ void a}
withoutActions :: Rules () -> Rules ()
withoutActions = modifyRules $ \x -> x{actions=mempty}