{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}

-- | Module: Data.ContentStore.Config
-- Copyright: (c) 2017 Red Hat, Inc.
-- License: LGPL
--
-- Maintainer: https://github.com/weldr
-- Stability: alpha
-- Portability: portable
--
-- Manage the content store's internal config file.

module Data.ContentStore.Config(Config(..),
                                defaultConfig,
                                readConfig,
                                writeConfig)
 where

-- Objects in the store are given file names that are their hash.
-- Lots of hash algorithms are available, which means we need to
-- know which one was used in a given store if we ever want to be
-- able to pull objects back out of it.  Thus, a config file.
--
-- Right now the only thing it's storing is the name of the hash
-- algorithm.  This may make using aeson seem like overkill, but
-- most of the config file parsing modules are unpleasant to use
-- and if this config file ever becomes too much more complicated,
-- we'll end up using aeson anyway.  Might as well start with it.

import           Data.Aeson
import           Data.Aeson.Types(Result(..), parseJSON)
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
import           Text.Toml(parseTomlDoc)

-- | Configuration information needed by the content store.
data Config = Config {
    -- | Is the data in the content store compressed?  If this option is missing
    -- in a config file, we assume that the data is not compressed.  This is to
    -- keep backwards compatibility with previous versions of the content store
    -- that did not support compression.  New content stores are created supporting
    -- compressed contents by default.
    confCompressed :: Bool,
    -- | What 'DigestAlgorithm' is in use by this content store?  While we do support
    -- several different algorithms, only one can ever be in use by a single content
    -- store.  Note that the 'ContentStore' record also stores this piece of
    -- information.  The difference is that here, we are only storing the text
    -- representation as given in a config file.
    confHash :: T.Text
 }

instance FromJSON Config where
    parseJSON = withObject "Config" $ \v ->
        Config <$> v .:? "compressed" .!= False
               <*> v .:? "hash" .!= "BLAKE2b256"

instance ToJSON Config where
    toJSON Config{..} = object [ "hash" .= toJSON confHash ]

-- | Construct a default 'Config' record useful for when creating a new 'ContentStore',
-- as with 'mkContentStore'.  Among other things, this is where the default hash
-- algorithm is defined.
defaultConfig :: Config
defaultConfig =
    Config { confCompressed = True,
             confHash = "BLAKE2b256" }

-- | Read a config file on disk, returning the 'Config' record on success and an error
-- message on error.  This function is typically not useful outside of content store
-- internals.
readConfig :: FilePath -> IO (Either T.Text Config)
readConfig path = do
    contents <- TIO.readFile path
    case parseTomlDoc "" contents of
        Left err  -> return $ Left $ T.pack $ show err
        Right tbl -> do let j = toJSON tbl
                        case (fromJSON j :: Result Config) of
                            Error err -> return $ Left $ T.pack $ show err
                            Success c -> return $ Right c

-- | Write a 'Config' object to disk.  This function is typically not useful outside
-- of content store internals.
writeConfig :: FilePath -> Config -> IO ()
writeConfig path Config{..} = TIO.writeFile path configText
 where
    configText = T.concat ["compressed = ", if confCompressed then "true" else "false", "\n",
                           "hash = \"", confHash, "\"\n"]