Improve error handling
- catch errors while loading themes - use user's actual home directory to look for config files - restructure main function
This commit is contained in:
parent
ea83f1aabf
commit
bbe6cd830f
3 changed files with 90 additions and 34 deletions
104
app/Main.hs
104
app/Main.hs
|
|
@ -13,6 +13,7 @@ import qualified Brick.Themes as B
|
||||||
import qualified Data.ConfigFile as C
|
import qualified Data.ConfigFile as C
|
||||||
import qualified Database.SQLite.Simple as DB
|
import qualified Database.SQLite.Simple as DB
|
||||||
import qualified Options.Applicative as O
|
import qualified Options.Applicative as O
|
||||||
|
import qualified System.Posix.User as P
|
||||||
|
|
||||||
import qualified TaskMachine.Config as TM
|
import qualified TaskMachine.Config as TM
|
||||||
import qualified TaskMachine.Database as TM
|
import qualified TaskMachine.Database as TM
|
||||||
|
|
@ -31,9 +32,42 @@ data Options = Options
|
||||||
, oExportDefaultTheme :: [FilePath]
|
, oExportDefaultTheme :: [FilePath]
|
||||||
} deriving (Show)
|
} deriving (Show)
|
||||||
|
|
||||||
argParser :: O.Parser Options
|
{-
|
||||||
argParser = pure Options
|
- Exit codes
|
||||||
<*> ((++ TM.defaultConfigFilePaths) <$> many configFile)
|
-}
|
||||||
|
|
||||||
|
noConfigFile :: ExitCode
|
||||||
|
noConfigFile = ExitFailure 10
|
||||||
|
|
||||||
|
rethrowAs :: (Exception e, Exception f) => (e -> Maybe f) -> IO a -> IO a
|
||||||
|
rethrowAs f action = do
|
||||||
|
res <- tryJust f action
|
||||||
|
case res of
|
||||||
|
Right v -> return v
|
||||||
|
Left e -> throwIO e
|
||||||
|
|
||||||
|
rethrowAsIf :: (Exception e, Exception f) => (e -> Bool) -> f -> IO a -> IO a
|
||||||
|
rethrowAsIf check newException action = do
|
||||||
|
res <- tryJust (guard . check) action
|
||||||
|
case res of
|
||||||
|
Right v -> return v
|
||||||
|
Left _ -> throwIO newException
|
||||||
|
|
||||||
|
{-
|
||||||
|
- Useful functions
|
||||||
|
-}
|
||||||
|
|
||||||
|
-- Log an action (prefixes "-> ")
|
||||||
|
act :: String -> IO ()
|
||||||
|
act = putStrLn . ("-> " ++)
|
||||||
|
|
||||||
|
{-
|
||||||
|
- Command line options
|
||||||
|
-}
|
||||||
|
|
||||||
|
argParser :: FilePath -> O.Parser Options
|
||||||
|
argParser homedir = pure Options
|
||||||
|
<*> ((++ TM.defaultConfigFilePaths homedir) <$> many configFile)
|
||||||
<*> optional taskDB
|
<*> optional taskDB
|
||||||
<*> many themePaths
|
<*> many themePaths
|
||||||
<*> many exportDefaultConfig
|
<*> many exportDefaultConfig
|
||||||
|
|
@ -45,7 +79,7 @@ argParser = pure Options
|
||||||
, O.help $ "Where to look for a config file.\
|
, O.help $ "Where to look for a config file.\
|
||||||
\ This option can be set multiple times.\
|
\ This option can be set multiple times.\
|
||||||
\ If not specified, will look in these locations:\n"
|
\ If not specified, will look in these locations:\n"
|
||||||
++ intercalate "," (map show TM.defaultConfigFilePaths)
|
++ (intercalate "," $ map show $ TM.defaultConfigFilePaths homedir)
|
||||||
, O.metavar "CONFIGFILE"
|
, O.metavar "CONFIGFILE"
|
||||||
]
|
]
|
||||||
taskDB = O.strOption $ mconcat
|
taskDB = O.strOption $ mconcat
|
||||||
|
|
@ -77,20 +111,20 @@ argParser = pure Options
|
||||||
, O.metavar "THEMEFILE"
|
, O.metavar "THEMEFILE"
|
||||||
]
|
]
|
||||||
|
|
||||||
argParserInfo :: O.ParserInfo Options
|
argParserInfo :: FilePath -> O.ParserInfo Options
|
||||||
argParserInfo = O.info (O.helper <*> argParser) $ mconcat
|
argParserInfo homedir = O.info (O.helper <*> argParser homedir) $ mconcat
|
||||||
[ O.fullDesc
|
[ O.fullDesc
|
||||||
]
|
]
|
||||||
|
|
||||||
-- Log an action (prefixes "-> ")
|
{-
|
||||||
action :: String -> IO ()
|
- Loading config and stuff
|
||||||
action = putStrLn . ("-> " ++)
|
-}
|
||||||
|
|
||||||
-- Kinda ugly...
|
-- Kinda ugly...
|
||||||
loadConfigs :: [FilePath] -> IO (Maybe TM.Config)
|
loadConfigs :: [FilePath] -> IO (Maybe TM.Config)
|
||||||
loadConfigs [] = return Nothing
|
loadConfigs [] = return Nothing
|
||||||
loadConfigs (path:paths) = do
|
loadConfigs (path:paths) = do
|
||||||
action $ "Loading config from " ++ show path ++ "."
|
act $ "Loading config from " ++ show path ++ "."
|
||||||
mConf <- tryLoadConfig path
|
mConf <- tryLoadConfig path
|
||||||
case mConf of
|
case mConf of
|
||||||
Just conf -> return (Just conf)
|
Just conf -> return (Just conf)
|
||||||
|
|
@ -102,13 +136,14 @@ loadConfigs (path:paths) = do
|
||||||
tryLoadConfig p = handleOpenFileExceptions
|
tryLoadConfig p = handleOpenFileExceptions
|
||||||
$ handleCPException
|
$ handleCPException
|
||||||
$ Just <$> TM.loadConfig p
|
$ Just <$> TM.loadConfig p
|
||||||
|
--tryLoadConfig p = Just <$> TM.loadConfig p
|
||||||
handleCPException :: IO (Maybe a) -> IO (Maybe a)
|
handleCPException :: IO (Maybe a) -> IO (Maybe a)
|
||||||
handleCPException f = do
|
handleCPException f = do
|
||||||
res <- try f
|
res <- try f
|
||||||
case res of
|
case res of
|
||||||
Right m -> return m
|
Right m -> return m
|
||||||
Left (TM.CPException (C.ParseError msg)) -> Nothing <$ putStrLn msg
|
Left (TM.CPException (C.ParseError msg)) -> Nothing <$ putStrLn msg
|
||||||
Left (TM.CPException _ ) -> return Nothing
|
Left (TM.CPException _ ) -> putStrLn "Bleep" >> return Nothing
|
||||||
handleOpenFileExceptions :: IO (Maybe a) -> IO (Maybe a)
|
handleOpenFileExceptions :: IO (Maybe a) -> IO (Maybe a)
|
||||||
handleOpenFileExceptions f = do
|
handleOpenFileExceptions f = do
|
||||||
res <- tryJust (guard . isRelevantError) f
|
res <- tryJust (guard . isRelevantError) f
|
||||||
|
|
@ -134,36 +169,55 @@ mergeWithOptions = mergeThemePaths <=< mergeTaskDB
|
||||||
loadThemes :: B.Theme -> [FilePath] -> IO B.Theme
|
loadThemes :: B.Theme -> [FilePath] -> IO B.Theme
|
||||||
loadThemes theme [] = return theme
|
loadThemes theme [] = return theme
|
||||||
loadThemes theme (path:paths) = do
|
loadThemes theme (path:paths) = do
|
||||||
action $ "Loading theme " ++ show path ++ "."
|
act $ "Loading theme from " ++ show path ++ "."
|
||||||
eModifiedTheme <- B.loadCustomizations path theme
|
mNewTheme <- tryLoadCustomizations path theme
|
||||||
case eModifiedTheme of
|
case mNewTheme of
|
||||||
Left errMsg -> die errMsg
|
Nothing -> do
|
||||||
Right modifiedTheme -> loadThemes modifiedTheme paths
|
putStrLn $ "Could not load theme from " ++ show path ++ "."
|
||||||
|
loadThemes theme paths
|
||||||
|
Just (Right newTheme) -> loadThemes newTheme paths
|
||||||
|
Just (Left errMsg) -> do
|
||||||
|
putStrLn $ "Could not load theme from " ++ show path ++ ": " ++ errMsg
|
||||||
|
loadThemes theme paths
|
||||||
|
where
|
||||||
|
tryLoadCustomizations :: FilePath -> B.Theme -> IO (Maybe (Either String B.Theme))
|
||||||
|
tryLoadCustomizations p t = handleOpenFileExceptions
|
||||||
|
$ Just <$> B.loadCustomizations p t
|
||||||
|
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
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = do
|
main = do
|
||||||
options <- O.execParser argParserInfo
|
homedir <- P.homeDirectory <$> (P.getUserEntryForID =<< P.getRealUserID)
|
||||||
|
options <- O.execParser $ argParserInfo homedir
|
||||||
|
|
||||||
-- Export default config
|
-- Export default config
|
||||||
forM_ (oExportDefaultConfig options) $ \path -> do
|
forM_ (oExportDefaultConfig options) $ \path -> do
|
||||||
action $ "Exporting default config to " ++ show path ++ "."
|
act $ "Exporting default config to " ++ show path ++ "."
|
||||||
TM.saveConfig path TM.defaultConfig
|
TM.saveConfig path TM.defaultConfig
|
||||||
|
|
||||||
-- Export default theme
|
-- Export default theme
|
||||||
forM_ (oExportDefaultTheme options) $ \path -> do
|
forM_ (oExportDefaultTheme options) $ \path -> do
|
||||||
action $ "Exporting default theme to " ++ show path ++ "."
|
act $ "Exporting default theme to " ++ show path ++ "."
|
||||||
B.saveTheme path TM.defaultTheme
|
B.saveTheme path TM.defaultTheme
|
||||||
|
|
||||||
-- Load config
|
-- Load config
|
||||||
mConfig <- loadConfigs $ oConfigFile options
|
mConfig <- loadConfigs $ oConfigFile options
|
||||||
case mConfig of
|
config <- case mConfig of
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
putStrLn "Could not load any config."
|
putStrLn ""
|
||||||
|
putStrLn "Could not find any config file."
|
||||||
putStrLn "Use the -C CONFIGFILE flag to generate a default config file."
|
putStrLn "Use the -C CONFIGFILE flag to generate a default config file."
|
||||||
die "No config file"
|
exitWith noConfigFile
|
||||||
Just unmergedConfig -> do
|
Just unmergedConfig -> return $ mergeWithOptions unmergedConfig options
|
||||||
-- Add command line options into config
|
|
||||||
let config = mergeWithOptions unmergedConfig options
|
|
||||||
|
|
||||||
-- According to config, load themes and initialize db
|
-- According to config, load themes and initialize db
|
||||||
theme <- loadThemes TM.defaultTheme $ TM.cThemes config
|
theme <- loadThemes TM.defaultTheme $ TM.cThemes config
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ dependencies:
|
||||||
- vty
|
- vty
|
||||||
- optparse-applicative
|
- optparse-applicative
|
||||||
- ConfigFile
|
- ConfigFile
|
||||||
|
- unix
|
||||||
#- containers
|
#- containers
|
||||||
#- unordered-containers
|
#- unordered-containers
|
||||||
#- text
|
#- text
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,9 @@ defaultConfig = Config
|
||||||
, cTaskDB = "~/.taskmachine/tasks.db"
|
, cTaskDB = "~/.taskmachine/tasks.db"
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultConfigFilePaths :: [FilePath]
|
defaultConfigFilePaths :: FilePath -> [FilePath]
|
||||||
defaultConfigFilePaths = ["tasks.conf", "~/.taskmachine/tasks.conf"]
|
defaultConfigFilePaths homedir =
|
||||||
|
[homedir ++ "/.taskmachine/tasks.conf", "tasks.conf"]
|
||||||
|
|
||||||
newtype CPException = CPException C.CPErrorData
|
newtype CPException = CPException C.CPErrorData
|
||||||
deriving (Show)
|
deriving (Show)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue