imp: Hledger.Cli.Conf: refactor, expose more info about config files

This commit is contained in:
Simon Michael 2025-04-17 17:18:13 -10:00
parent 320d197735
commit daf550bbb0

View File

@ -8,7 +8,14 @@ Read extra CLI arguments from a hledger config file.
module Hledger.Cli.Conf (
getConf
,nullconf
,confLookup
,activeConfFile
,activeLocalConfFile
,activeUserConfFile
,confFiles
,userConfFiles
,parseConf
)
where
@ -19,6 +26,7 @@ import qualified Data.Map as M
import Data.Maybe (catMaybes)
import Data.Text (Text)
import qualified Data.Text as T (pack)
import Safe (headMay)
import System.Directory (getHomeDirectory, getXdgDirectory, XdgDirectory (XdgConfig), doesFileExist, getCurrentDirectory)
import System.FilePath ((</>), takeDirectory)
import Text.Megaparsec
@ -99,12 +107,9 @@ getConf rawopts = do
NoConfFile -> return $ traceAt 1 "ignoring config files" (nullconf, Nothing)
SomeConfFile f -> getCurrentDirectory >>= flip expandPath f >>= readConfFile . dbg1 "using specified config file"
AutoConfFile -> do
defconfpaths <- defaultConfFilePaths
conffiles <- fmap catMaybes $ forM defconfpaths $ \f -> do
exists <- doesFileExist f
return $ if exists then Just f else Nothing
case conffiles of
f:_ -> dbg8IO "found config files" conffiles >> dbg1IO "using nearest config file" f >> readConfFile f
fs <- confFiles
case fs of
f:_ -> dbg8IO "found config files" fs >> dbg1IO "using nearest config file" f >> readConfFile f
[] -> return $ traceAt 1 "no config file found" (nullconf, Nothing)
-- | Read this config file and parse its contents, or raise an error.
@ -113,7 +118,6 @@ readConfFile f = do
-- avoid GHC 9.10.1's ugly stack trace when calling readFile on a nonexistent file
exists <- doesFileExist f
when (not exists) $ error' $ f <> " does not exist"
ecs <- readFile f <&> parseConf f . T.pack
case ecs of
Left err -> error' $ errorBundlePretty err -- customErrorBundlePretty err
@ -125,17 +129,67 @@ readConfFile f = do
Just f
)
-- | Get the possible paths for a hledger config file, depending on the current directory.
defaultConfFilePaths :: IO [FilePath]
defaultConfFilePaths = do
ds <- confDirs
-- | Get the highest precedence config file, based on the current directory.
activeConfFile :: IO (Maybe FilePath)
activeConfFile = headMay <$> confFiles
-- | Get the highest precedence local config file:
-- a config file in the current directory or above, that is not a user-wide config file.
activeLocalConfFile :: IO (Maybe FilePath)
activeLocalConfFile = do
ufs <- userConfFiles
mf <- headMay <$> confFiles
return $ case mf of
Just f | f `notElem` ufs -> Just f
_ -> Nothing
-- | Get the highest precedence user-wide config file, based on the current directory.
-- (This may not be the active config file.)
activeUserConfFile :: IO (Maybe FilePath)
activeUserConfFile = headMay <$> userConfFiles
-- | Get the possibleConfFiles which exist, based on the current directory.
confFiles :: IO [FilePath]
confFiles = possibleConfFiles >>= existingFiles
-- | Get the possibleUserConfFiles which exist, based on the current directory.
userConfFiles :: IO [FilePath]
userConfFiles = possibleUserConfFiles >>= existingFiles
-- | Filter a list of paths to just the existing files.
existingFiles :: [FilePath] -> IO [FilePath]
existingFiles fs =
fmap catMaybes $ forM fs $ \f -> do
exists <- doesFileExist f
return $ if exists then Just f else Nothing
-- | Get the possible paths for a hledger config file, highest precedence first:
-- hledger.conf in the current directory,
-- hledger.conf in any parent directory,
-- .hledger.conf in the home directory,
-- or hledger.conf in the XdgConfig directory.
possibleConfFiles :: IO [FilePath]
possibleConfFiles = do
ds <- possibleConfDirs
home <- getHomeDirectory
return $ dbg8 "possible config file paths" $
flip map ds $ \d -> d </> if d==home then ".hledger.conf" else "hledger.conf"
-- | Get the directories to check for a hledger config file.
confDirs :: IO [FilePath]
confDirs = do
-- | Like possibleConfFiles, but consider only user-wide hledger config files:
-- .hledger.conf in the home directory,
-- or hledger.conf in the XdgConfig directory.
possibleUserConfFiles :: IO [FilePath]
possibleUserConfFiles = do
home <- getHomeDirectory
xdgc <- getXdgDirectory XdgConfig "hledger"
let ds = [home,xdgc]
return $ dbg8 "possible user config file paths" $
flip map ds $ \d -> d </> if d==home then ".hledger.conf" else "hledger.conf"
-- | Get the directories where a hledger config file could be, highest precedence first:
-- the current directory, any parent directory, the home directory, or the XdgConfig directory.
possibleConfDirs :: IO [FilePath]
possibleConfDirs = do
xdgc <- getXdgDirectory XdgConfig "hledger"
home <- getHomeDirectory
here <- getCurrentDirectory