Coadjute-0.0.1: Coadjute/Rule.hs
-- File created: 2008-01-12 16:06:32
-- |Holds the 'Rule' and 'Coadjute' types and relevant functions which act on
-- them. For user code, the important parts here are the 'rule' family of
-- functions and sourceToDatum.
module Coadjute.Rule(
Rule(..),
Coadjute, runCoadjute,
getUserArgs,
rule, ruleM,
rule', ruleM',
sourceToDatum, sourceToDatum',
TaskDatum, SingleDatum, MultiDatum
) where
import Control.Arrow (first)
import Control.Monad.State (StateT(..), modify, gets)
import Control.Monad.Trans (MonadIO, liftIO)
import qualified Data.Set as Set
import Coadjute.CoData
import Coadjute.Task (Task(..), Source, Target)
data Rule =
Rule { rName :: String
, rTasks :: [Task]
}
data RuleList = RL [Rule]
addRule :: Rule -> RuleList -> RuleList
addRule r (RL rs) = RL (r:rs)
-- |Coadjute is the main monad you'll be working in. You can use the 'rule'
-- family of functions to add rules whilst within this monad, and you can have
-- a look at what arguments were given to you with 'getUserArgs'.
--
-- For your convenience, a 'MonadIO' instance is provided.
newtype Coadjute a = Co { unCo :: StateT (RuleList, [String]) CoData a }
instance Monad Coadjute where
return = Co . return
(Co rs) >>= f = Co (rs >>= unCo.f)
instance MonadIO Coadjute where liftIO = Co . liftIO
runCoadjute :: Coadjute a -> CoData ([Rule], a)
runCoadjute (Co st) = do
ua <- asks coUserArgs
(ret, (RL l, _)) <- runStateT st (RL [], ua)
return (reverse l, ret)
-- | You should use this instead of 'System.Environment.getArgs', to let
-- Coadjute handle its own command line arguments first.
getUserArgs :: Coadjute [String]
getUserArgs = Co $ gets snd
newtype TaskDatum a = TD ([Source], a)
-- | A SingleDatum stores a single 'Target'.
type SingleDatum = TaskDatum Target
-- | A MultiDatum stores multiple 'Target's, to be built all at once in one
-- action.
type MultiDatum = TaskDatum [Target]
-- |A rule for building targets individually.
rule :: String
-> [String]
-> ([Source] -> Target -> IO ())
-> [SingleDatum]
-> Coadjute ()
rule name args action =
Co . modify . first . addRule . Rule name .
map (\(TD (d, t)) -> Task name (Set.fromList args) [t] d (action d t))
-- |A rule for building multiple targets at once.
ruleM :: String
-> [String]
-> ([Source] -> [Target] -> IO ())
-> [MultiDatum]
-> Coadjute ()
ruleM name args action =
Co . modify . first . addRule . Rule name .
map (\(TD (d, t)) -> Task name (Set.fromList args) t d (action d t))
-- | > rule' = flip rule []
rule' :: String
-> ([Source] -> Target -> IO ())
-> [SingleDatum]
-> Coadjute ()
rule' = flip rule []
-- | > ruleM' = flip ruleM []
ruleM' :: String
-> ([Source] -> [Target] -> IO ())
-> [MultiDatum]
-> Coadjute ()
ruleM' = flip ruleM []
-- |Use a supplied function of type @Source -> ([Source], Target)@ or @Source
-- -> ([Source], [Target])@ to turn a list of Sources to 'SingleDatum's or
-- 'MultiDatum's, for passing to one of the 'rule' functions.
--
-- The original Source is prepended to the list of dependencies in the Datum.
sourceToDatum :: (Source -> ([Source], a)) -> [Source] -> [TaskDatum a]
sourceToDatum f = map (\x -> TD $ first (x:) (f x))
-- |A convenience around 'sourceToDatum' for when you don't wish to provide
-- extra dependencies.
sourceToDatum' :: (Source -> a) -> [Source] -> [TaskDatum a]
sourceToDatum' f = sourceToDatum ((,) [] . f)