195 lines
7.3 KiB
Haskell
195 lines
7.3 KiB
Haskell
{-# LANGUAGE OverloadedStrings #-}
|
|
|
|
module Main where
|
|
|
|
import Control.Applicative
|
|
import Control.Exception
|
|
import Control.Monad
|
|
import Data.List
|
|
import System.Exit
|
|
import System.IO.Error
|
|
|
|
import qualified Brick.Themes as B
|
|
import qualified Data.ConfigFile as C
|
|
import qualified Database.SQLite.Simple as DB
|
|
import qualified Options.Applicative as O
|
|
|
|
import qualified TaskMachine.Config as TM
|
|
import qualified TaskMachine.Database as TM
|
|
import qualified TaskMachine.UI as TM
|
|
|
|
-- TODO: When adding oConfigFile back, make oTaskDB a Maybe FilePath.
|
|
-- This way, it won't always overwrite the task db set in the config.
|
|
-- TODO: Add a [-c|--export-default-config CONFIGFILE] option
|
|
-- TODO: Add a [--initialize] flag to create a ~/.taskmachine/ folder and fill it with a default config and theme.
|
|
-- TODO: Have a look at other programs to see how they deal with this issue.
|
|
data Options = Options
|
|
{ oConfigFile :: [FilePath]
|
|
, oTaskDB :: Maybe FilePath
|
|
, oThemePaths :: [FilePath]
|
|
, oExportDefaultConfig :: [FilePath]
|
|
, oExportDefaultTheme :: [FilePath]
|
|
} deriving (Show)
|
|
|
|
argParser :: O.Parser Options
|
|
argParser = pure Options
|
|
<*> ((++ TM.defaultConfigFilePaths) <$> many configFile)
|
|
<*> optional taskDB
|
|
<*> many themePaths
|
|
<*> many exportDefaultConfig
|
|
<*> many exportDefaultTheme
|
|
where
|
|
configFile = O.strOption $ mconcat
|
|
[ O.short 'c'
|
|
, O.long "config"
|
|
, O.help $ "Where to look for a config file.\
|
|
\ This option can be set multiple times.\
|
|
\ If not specified, will look in these locations:\n"
|
|
++ intercalate "," (map show TM.defaultConfigFilePaths)
|
|
, O.metavar "CONFIGFILE"
|
|
]
|
|
taskDB = O.strOption $ mconcat
|
|
[ O.short 'd'
|
|
, O.long "task-db"
|
|
, O.help "Specify the database file where the tasks are saved.\
|
|
\ This option overwrites the config file."
|
|
, O.metavar "TASKDB"
|
|
]
|
|
themePaths = O.strOption $ mconcat
|
|
[ O.short 't'
|
|
, O.long "theme"
|
|
, O.help "Specify one or more theme files to load.\
|
|
\ This option can be set zero or more times.\
|
|
\ This option overwrites the config file."
|
|
, O.metavar "THEMEFILE"
|
|
]
|
|
exportDefaultConfig = O.strOption $ mconcat
|
|
[ O.short 'C'
|
|
, O.long "export-default-config"
|
|
, O.help "Export the application's default config to a file."
|
|
, O.metavar "CONFIGFILE"
|
|
]
|
|
exportDefaultTheme = O.strOption $ mconcat
|
|
[ O.short 'T'
|
|
, O.long "export-default-theme"
|
|
, O.help "Export the application's default theme to a file.\
|
|
\ This can be used as a starting point for a custom theme."
|
|
, O.metavar "THEMEFILE"
|
|
]
|
|
|
|
argParserInfo :: O.ParserInfo Options
|
|
argParserInfo = O.info (O.helper <*> argParser) $ mconcat
|
|
[ O.fullDesc
|
|
]
|
|
|
|
-- Log an action (prefixes "-> ")
|
|
action :: String -> IO ()
|
|
action = putStrLn . ("-> " ++)
|
|
|
|
-- Kinda ugly...
|
|
loadConfigs :: [FilePath] -> IO (Maybe TM.Config)
|
|
loadConfigs [] = return Nothing
|
|
loadConfigs (path:paths) = do
|
|
action $ "Loading config from " ++ show path ++ "."
|
|
mConf <- tryLoadConfig path
|
|
case mConf of
|
|
Just conf -> return (Just conf)
|
|
Nothing -> do
|
|
putStrLn $ "Could not load config from " ++ show path ++ "."
|
|
loadConfigs paths
|
|
where
|
|
tryLoadConfig :: FilePath -> IO (Maybe TM.Config)
|
|
tryLoadConfig p = handleOpenFileExceptions
|
|
$ handleCPException
|
|
$ Just <$> TM.loadConfig p
|
|
handleCPException :: IO (Maybe a) -> IO (Maybe a)
|
|
handleCPException f = do
|
|
res <- try f
|
|
case res of
|
|
Right m -> return m
|
|
Left (TM.CPException (C.ParseError msg)) -> Nothing <$ putStrLn msg
|
|
Left (TM.CPException _ ) -> return Nothing
|
|
handleOpenFileExceptions :: IO (Maybe a) -> IO (Maybe a)
|
|
handleOpenFileExceptions f = do
|
|
res <- tryJust (guard . isRelevantError) f
|
|
case res of
|
|
Right m -> return m
|
|
Left _ -> return Nothing
|
|
isRelevantError :: IOError -> Bool
|
|
isRelevantError e = isAlreadyInUseError e
|
|
|| isDoesNotExistError e
|
|
|| isPermissionError e
|
|
|
|
mergeWithOptions :: TM.Config -> Options -> TM.Config
|
|
mergeWithOptions = mergeThemePaths <=< mergeTaskDB
|
|
where
|
|
mergeThemePaths conf opt = case oThemePaths opt of
|
|
[] -> conf
|
|
themes -> conf { TM.cThemes = themes }
|
|
mergeTaskDB conf opt = case oTaskDB opt of
|
|
Nothing -> conf
|
|
Just taskdb -> conf { TM.cTaskDB = taskdb }
|
|
|
|
-- Could probably implement using EitherT, but too lazy :)
|
|
loadThemes :: B.Theme -> [FilePath] -> IO B.Theme
|
|
loadThemes theme [] = return theme
|
|
loadThemes theme (path:paths) = do
|
|
action $ "Loading theme " ++ show path ++ "."
|
|
eModifiedTheme <- B.loadCustomizations path theme
|
|
case eModifiedTheme of
|
|
Left errMsg -> die errMsg
|
|
Right modifiedTheme -> loadThemes modifiedTheme paths
|
|
|
|
main :: IO ()
|
|
main = do
|
|
options <- O.execParser argParserInfo
|
|
|
|
-- Export default config
|
|
forM_ (oExportDefaultConfig options) $ \path -> do
|
|
action $ "Exporting default config to " ++ show path ++ "."
|
|
TM.saveConfig path TM.defaultConfig
|
|
|
|
-- Export default theme
|
|
forM_ (oExportDefaultTheme options) $ \path -> do
|
|
action $ "Exporting default theme to " ++ show path ++ "."
|
|
B.saveTheme path TM.defaultTheme
|
|
|
|
-- Load config
|
|
mConfig <- loadConfigs $ oConfigFile options
|
|
case mConfig of
|
|
Nothing -> do
|
|
putStrLn "Could not load any config."
|
|
putStrLn "Use the -C CONFIGFILE flag to generate a default config file."
|
|
die "No config file"
|
|
Just unmergedConfig -> do
|
|
-- Add command line options into config
|
|
let config = mergeWithOptions unmergedConfig options
|
|
|
|
-- According to config, load themes and initialize db
|
|
theme <- loadThemes TM.defaultTheme $ TM.cThemes config
|
|
DB.withConnection (TM.cTaskDB config) TM.initializeNewDB
|
|
|
|
-- Start the UI
|
|
error "Implement UI" theme config
|
|
|
|
--import qualified Database.SQLite.Simple as DB
|
|
--import qualified TaskMachine.Database as TMD
|
|
--main = DB.withConnection "test.db" TMD.initializeNewDB
|
|
|
|
--data ResourceName = Asdf
|
|
-- deriving (Eq, Ord)
|
|
--
|
|
--myApp :: B.App () () ResourceName
|
|
--myApp = B.App
|
|
-- { B.appDraw = \_ -> [myTestWidget]
|
|
-- , B.appHandleEvent = B.resizeOrQuit
|
|
-- , B.appStartEvent = \s -> return s
|
|
-- , B.appChooseCursor = B.neverShowCursor
|
|
-- , B.appAttrMap = const $ B.themeToAttrMap TM.defaultTheme
|
|
-- }
|
|
-- where
|
|
-- myTestWidget = normal B.<=> urgent B.<=> veryUrgent B.<=> overdue
|
|
-- normal = B.withAttr ("taskList" <> "normal") (B.str " normal ") B.<+> B.withAttr ("taskList" <> "highlight") (B.str "style")
|
|
-- urgent = B.withAttr ("taskList" <> "urgent" <> "normal") (B.str " urgent ") B.<+> B.withAttr ("taskList" <> "urgent" <> "highlight") (B.str "style")
|
|
-- veryUrgent = B.withAttr ("taskList" <> "veryUrgent" <> "normal") (B.str "very urgent ") B.<+> B.withAttr ("taskList" <> "veryUrgent" <> "highlight") (B.str "style")
|
|
-- overdue = B.withAttr ("taskList" <> "overdue" <> "normal") (B.str " overdue ") B.<+> B.withAttr ("taskList" <> "overdue" <> "highlight") (B.str "style")
|