{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE ImpredicativeTypes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE AllowAmbiguousTypes #-}

module Conftrack.Source (ConfigSource(..), SomeSource(..)) where

import Conftrack.Value (Key, Value(..), ConfigError(..))

import Control.Monad.State (StateT (..))
import Data.Text (Text)


-- | An abstraction over "config sources". This might mean file formats,
-- environment variables, or any other kind of format that can be seen as a
-- key-value store.
class ConfigSource s where
  -- | Some sources require state, e.g. to keep track of which values were
  -- already read.
  type SourceState s

  -- | read a single value from the source.
  fetchValue :: Key -> s -> StateT (SourceState s) IO (Either ConfigError (Value, Text))

  -- | given @s@, determine if any keys are "left over" and were not used.
  -- This is used to produce warnings for unknown configuration options;
  -- since not all sources can support this, this function's return type
  -- includes @Maybe@ and sources are free to return @Nothing@ if they
  -- cannot determine if any unknown keys are present.
  leftovers :: s -> StateT (SourceState s) IO (Maybe [Key])

-- | An opaque type for any kind of config sources. Values of this type can be
-- acquired from they @Conftrack.Source.*@ modules, or by implementing the
-- 'ConfigSource' type class.
data SomeSource = forall source. ConfigSource source
   => SomeSource (source, SourceState source)