{-# LANGUAGE QuasiQuotes #-} {- | Module : HCovGuard.Config.Config Description : Main configuration type and loading functions Copyright : (c) Trevis Elser, 2026 License : MIT Maintainer : oss@treviselser.com -} module HCovGuard.Config.Config ( Config (..) , loadConfig , ConfigError (..) , showConfigError ) where import qualified Control.Exception as Exception import qualified Data.String.Interpolate as Interpolate import qualified Data.Text as Text import qualified Toml import qualified HCovGuard.Config.ForAnyModule as ForAnyModule import qualified HCovGuard.Config.ForSpecifiedModule as ForSpecifiedModule import qualified System.OsPath as OsPath import qualified TomlHelper {- | The complete configuration for hcovguard @since 0.1.0.0 -} data Config = Config { forAnyModule :: !ForAnyModule.ForAnyModule , forSpecifiedModules :: !ForSpecifiedModule.ForSpecifiedModules } -- | Internal type for parsing before validation data RawConfig = RawConfig { rawForAnyModule :: !ForAnyModule.ForAnyModule , rawForSpecifiedModules :: ![ForSpecifiedModule.RawForSpecifiedModule] } rawConfigCodec :: Toml.TomlCodec RawConfig rawConfigCodec = RawConfig <$> TomlHelper.addField "forAnyModule" rawForAnyModule (TomlHelper.setDefault ForAnyModule.emptyForAnyModule (Toml.table ForAnyModule.forAnyModuleCodec)) <*> TomlHelper.addField "forSpecifiedModules" rawForSpecifiedModules (TomlHelper.setDefault mempty ForSpecifiedModule.forSpecifiedModulesCodec) -- | Errors that can occur when loading configuration data ConfigError = ConfigPathEncodingError !OsPath.OsPath !Text.Text | ConfigValidationError !ForSpecifiedModule.ConfigValidationError {- | Format a config error as a human-readable message @since 0.1.0.0 -} showConfigError :: ConfigError -> Text.Text showConfigError (ConfigPathEncodingError path msg) = Text.unlines [ [Interpolate.i|Error: [Hcovguard-6839]|] , [Interpolate.i|Invalid config file path encoding|] , [Interpolate.i| Path: |] <> Text.pack (show path) , [Interpolate.i| Error: |] <> msg ] showConfigError (ConfigValidationError validationErr) = ForSpecifiedModule.showConfigValidationError validationErr loadConfig :: OsPath.OsPath -> IO (Either ConfigError Config) loadConfig path = do let tryDecode :: IO (Either Exception.SomeException FilePath) tryDecode = Exception.try (OsPath.decodeFS path) toConfig :: RawConfig -> ForSpecifiedModule.ForSpecifiedModules -> Config toConfig raw validated = Config { forAnyModule = rawForAnyModule raw , forSpecifiedModules = validated } decodeResult <- tryDecode case decodeResult of Left err -> pure . Left $ ConfigPathEncodingError path (Text.pack $ Exception.displayException err) Right filePath -> do rawConfig <- Toml.decodeFile rawConfigCodec filePath pure $ either (Left . ConfigValidationError) (Right . toConfig rawConfig) (ForSpecifiedModule.validateForSpecifiedModules (rawForSpecifiedModules rawConfig))