mirror of
https://github.com/rodrigosetti/master-plan.git
synced 2024-11-26 07:20:58 +03:00
138 lines
6.0 KiB
Haskell
138 lines
6.0 KiB
Haskell
{-|
|
|
Module : Main
|
|
Description : Parses command line and dispatches to correct backend
|
|
Copyright : (c) Rodrigo Setti, 2017
|
|
License : MIT
|
|
Maintainer : rodrigosetti@gmail.com
|
|
Stability : experimental
|
|
Portability : POSIX
|
|
-}
|
|
{-# LANGUAGE UnicodeSyntax #-}
|
|
module Main (main) where
|
|
|
|
import Data.List (intercalate)
|
|
import qualified Data.List.NonEmpty as NE
|
|
import qualified Data.Map as M
|
|
import Data.Maybe (catMaybes, fromMaybe)
|
|
import Data.Semigroup ((<>))
|
|
import qualified Data.Text.IO as TIO
|
|
import qualified MasterPlan.Backend.Graph as BG
|
|
import qualified MasterPlan.Backend.Html as BH
|
|
import qualified MasterPlan.Backend.Identity as BI
|
|
import qualified MasterPlan.Backend.Text as BT
|
|
import MasterPlan.Data
|
|
import qualified MasterPlan.Parser as P
|
|
import Options.Applicative
|
|
import System.IO (hPutStr, stderr)
|
|
|
|
-- |Type output from the command line parser
|
|
data Opts = Opts { inputPath :: FilePath
|
|
, outputPath :: Maybe FilePath
|
|
, projFilter :: ProjFilter -- ^ filter to consider
|
|
, properties :: [ProjProperty] -- ^ which properties to consider
|
|
, prioritize :: Bool -- ^ order by priority
|
|
, renderMode :: RenderMode }
|
|
deriving (Show)
|
|
|
|
newtype ProjFilter = ProjFilter (ProjectBinding → Bool)
|
|
|
|
noFilter ∷ ProjFilter
|
|
noFilter = ProjFilter $ const True
|
|
|
|
instance Show ProjFilter where
|
|
show _ = "ProjFilter"
|
|
|
|
data RenderMode = IdentityRenderMode | TextRenderMode | GraphRenderMode | HtmlMode
|
|
deriving (Eq, Show)
|
|
|
|
readEnum ∷ [(String, a)] → ReadM a
|
|
readEnum mapping = maybeReader $ flip lookup mapping
|
|
|
|
-- |The command line parser
|
|
cmdParser ∷ Parser Opts
|
|
cmdParser = Opts <$> strOption ( long "input"
|
|
<> short 'i'
|
|
<> help "plan file to read from"
|
|
<> value "master.plan"
|
|
<> showDefault
|
|
<> metavar "FILENAME" )
|
|
<*> optional (strOption ( long "output"
|
|
<> short 'o'
|
|
<> help "output file name"
|
|
<> metavar "FILENAME" ))
|
|
<*> (filterParser <|> pure noFilter)
|
|
<*> (invertProps <$> many (option property ( long "hide"
|
|
<> help "hide a particular property"
|
|
<> metavar (intercalate "|" $ map fst propertyNames))))
|
|
<*> switch ( long "prioritize"
|
|
<> short 'p'
|
|
<> help "prioritize projects to minimize cost")
|
|
<*> option parseRenderMode ( long "mode"
|
|
<> short 'm'
|
|
<> help "render mode"
|
|
<> metavar (intercalate "|" $ map fst nameRenderModes))
|
|
where
|
|
propertyNames = map (\p -> (show p, p)) [minBound :: ProjProperty ..]
|
|
nameRenderModes = [ ("identity", IdentityRenderMode)
|
|
, ("text", TextRenderMode)
|
|
, ("graph", GraphRenderMode)
|
|
, ("html", HtmlMode) ]
|
|
property = readEnum propertyNames
|
|
parseRenderMode = readEnum nameRenderModes
|
|
|
|
invertProps ∷ [ProjProperty] → [ProjProperty]
|
|
invertProps l = filter (`notElem` l) $ map snd propertyNames
|
|
|
|
filterParser ∷ Parser ProjFilter
|
|
filterParser = (ProjFilter . mkProgressFilter) <$> option auto ( long "progress-below"
|
|
<> help "only display projects which progress is < N%"
|
|
<> metavar "N" )
|
|
where
|
|
mkProgressFilter n (TaskProj _ _ _ p) = p*100 < n
|
|
mkProgressFilter _ _ = True
|
|
|
|
main ∷ IO ()
|
|
main = masterPlan =<< execParser opts
|
|
where
|
|
opts = info (cmdParser <**> helper)
|
|
( fullDesc
|
|
<> progDesc "See documentation on how to write project plan files"
|
|
<> header "master-plan - project management tool for hackers" )
|
|
|
|
filterBinding ∷ ProjectSystem → ProjFilter → ProjectBinding → Maybe ProjectBinding
|
|
filterBinding sys (ProjFilter f) (ExpressionProj r p) = ExpressionProj r <$> filterProj p
|
|
where
|
|
filterProj (SumProj ps) = SumProj <$> filterProjs ps
|
|
filterProj (ProductProj ps) = ProductProj <$> filterProjs ps
|
|
filterProj (SequenceProj ps) = SequenceProj <$> filterProjs ps
|
|
filterProj rp@(RefProj n) = do b <- M.lookup n $ bindings sys
|
|
if f b then Just rp else Nothing
|
|
|
|
filterProjs ps = NE.nonEmpty (catMaybes $ NE.toList $ NE.map filterProj ps)
|
|
filterBinding _ _ b = Just b
|
|
|
|
masterPlan ∷ Opts → IO ()
|
|
masterPlan opts =
|
|
do contents <- TIO.readFile filename
|
|
case P.runParser filename contents of
|
|
Left e -> hPutStr stderr e
|
|
Right sys@(ProjectSystem b) ->
|
|
render $ maybeOptimize $ ProjectSystem $ M.mapMaybe
|
|
(filterBinding sys $ projFilter opts) b
|
|
where
|
|
filename = inputPath opts
|
|
|
|
maybeOptimize = if prioritize opts then optimizeSys else id
|
|
|
|
outputToFileOrOut s = case outputPath opts of
|
|
Nothing -> TIO.putStr s
|
|
Just path -> TIO.writeFile path s
|
|
|
|
render sys =
|
|
case renderMode opts of
|
|
IdentityRenderMode -> outputToFileOrOut $ BI.render sys $ properties opts
|
|
TextRenderMode -> outputToFileOrOut $ BT.render sys $ properties opts
|
|
HtmlMode -> outputToFileOrOut $ BH.render sys $ properties opts
|
|
GraphRenderMode -> do let outfile = fromMaybe (filename ++ ".png") $ outputPath opts
|
|
BG.render outfile sys $ properties opts
|