master-plan/app/Main.hs

138 lines
6.0 KiB
Haskell
Raw Normal View History

2017-08-10 04:32:46 +03:00
{-|
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 #-}
2017-08-10 04:17:13 +03:00
module Main (main) where
2017-08-05 04:53:52 +03:00
2017-08-10 04:17:13 +03:00
import Data.List (intercalate)
import qualified Data.List.NonEmpty as NE
import qualified Data.Map as M
2017-08-10 04:32:46 +03:00
import Data.Maybe (catMaybes, fromMaybe)
2017-08-09 22:48:09 +03:00
import Data.Semigroup ((<>))
2017-08-10 05:20:47 +03:00
import qualified Data.Text.IO as TIO
2017-08-10 04:32:46 +03:00
import qualified MasterPlan.Backend.Graph as BG
2017-08-10 05:20:47 +03:00
import qualified MasterPlan.Backend.Html as BH
2017-08-10 04:32:46 +03:00
import qualified MasterPlan.Backend.Identity as BI
import qualified MasterPlan.Backend.Text as BT
2017-08-09 22:48:09 +03:00
import MasterPlan.Data
import qualified MasterPlan.Parser as P
import Options.Applicative
2017-08-10 04:17:13 +03:00
import System.IO (hPutStr, stderr)
2017-08-09 22:48:09 +03:00
-- |Type output from the command line parser
data Opts = Opts { inputPath :: FilePath
, outputPath :: Maybe FilePath
2017-08-10 04:17:13 +03:00
, projFilter :: ProjFilter -- ^ filter to consider
2017-08-09 22:48:09 +03:00
, properties :: [ProjProperty] -- ^ which properties to consider
, prioritize :: Bool -- ^ order by priority
, renderMode :: RenderMode }
deriving (Show)
newtype ProjFilter = ProjFilter (ProjectBinding Bool)
2017-08-10 04:17:13 +03:00
noFilter ProjFilter
noFilter = ProjFilter $ const True
2017-08-09 22:48:09 +03:00
instance Show ProjFilter where
show _ = "ProjFilter"
2017-08-10 05:20:47 +03:00
data RenderMode = IdentityRenderMode | TextRenderMode | GraphRenderMode | HtmlMode
2017-08-09 22:48:09 +03:00
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" ))
2017-08-10 04:17:13 +03:00
<*> (filterParser <|> pure noFilter)
2017-08-09 22:48:09 +03:00
<*> (invertProps <$> many (option property ( long "hide"
<> help "hide a particular property"
2017-08-10 04:17:13 +03:00
<> metavar (intercalate "|" $ map fst propertyNames))))
2017-08-09 22:48:09 +03:00
<*> switch ( long "prioritize"
<> short 'p'
<> help "prioritize projects to minimize cost")
<*> option parseRenderMode ( long "mode"
<> short 'm'
<> help "render mode"
2017-08-10 04:17:13 +03:00
<> metavar (intercalate "|" $ map fst nameRenderModes))
2017-08-09 22:48:09 +03:00
where
2017-08-10 04:17:13 +03:00
propertyNames = map (\p -> (show p, p)) [minBound :: ProjProperty ..]
nameRenderModes = [ ("identity", IdentityRenderMode)
, ("text", TextRenderMode)
2017-08-10 05:20:47 +03:00
, ("graph", GraphRenderMode)
, ("html", HtmlMode) ]
2017-08-10 04:17:13 +03:00
property = readEnum propertyNames
parseRenderMode = readEnum nameRenderModes
2017-08-09 22:48:09 +03:00
invertProps [ProjProperty] [ProjProperty]
2017-08-10 04:17:13 +03:00
invertProps l = filter (`notElem` l) $ map snd propertyNames
2017-08-09 22:48:09 +03:00
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 ()
2017-08-09 22:48:09 +03:00
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" )
2017-08-10 04:17:13 +03:00
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
2017-08-09 22:48:09 +03:00
masterPlan Opts IO ()
2017-08-10 04:17:13 +03:00
masterPlan opts =
2017-08-10 05:20:47 +03:00
do contents <- TIO.readFile filename
2017-08-10 04:17:13 +03:00
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
2017-08-10 04:32:46 +03:00
filename = inputPath opts
2017-08-10 04:17:13 +03:00
maybeOptimize = if prioritize opts then optimizeSys else id
2017-08-10 04:32:46 +03:00
outputToFileOrOut s = case outputPath opts of
2017-08-10 05:20:47 +03:00
Nothing -> TIO.putStr s
Just path -> TIO.writeFile path s
2017-08-10 04:32:46 +03:00
2017-08-10 04:17:13 +03:00
render sys =
case renderMode opts of
2017-08-10 04:32:46 +03:00
IdentityRenderMode -> outputToFileOrOut $ BI.render sys $ properties opts
TextRenderMode -> outputToFileOrOut $ BT.render sys $ properties opts
2017-08-10 05:20:47 +03:00
HtmlMode -> outputToFileOrOut $ BH.render sys $ properties opts
2017-08-10 04:32:46 +03:00
GraphRenderMode -> do let outfile = fromMaybe (filename ++ ".png") $ outputPath opts
BG.render outfile sys $ properties opts