packages feed

harg-0.5.0.0: src/Options/Harg/Sources/YAML.hs

{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}

module Options.Harg.Sources.YAML
  ( YAMLSource (..),
  )
where

import qualified Barbies as B
import Control.Exception (displayException)
import qualified Data.ByteString as BS
import Data.Functor.Compose (Compose (..))
import Data.Functor.Identity (Identity (..))
import qualified Data.Yaml as YAML
import GHC.Generics (Generic)
import Options.Harg.Sources.Types
import Options.Harg.Types
import Options.Harg.Util (readFileBS)

-- | Source that enables a parser to read options from a YAML file.
newtype YAMLSource f = YAMLSource (f ConfigFile)
  deriving (Generic, B.FunctorB, B.TraversableB, B.ApplicativeB)

-- | The result of reading a YAML file. @YAMLSourceNotRequired@ is used when
-- the user has specified @defaultVal NoConfigFile@. It holds the contents of
-- the YAML file as a 'BS.ByteString'.
data YAMLSourceVal
  = YAMLSourceVal BS.ByteString
  | YAMLSourceNotRequired

instance GetSource YAMLSource Identity where
  type SourceVal YAMLSource = YAMLSourceVal
  getSource _ctx (YAMLSource (Identity (ConfigFile path))) =
    YAMLSourceVal <$> readFileBS path
  getSource _ctx (YAMLSource (Identity NoConfigFile)) =
    pure YAMLSourceNotRequired

instance
  ( YAML.FromJSON (a Maybe),
    B.FunctorB a
  ) =>
  RunSource YAMLSourceVal a
  where
  runSource (YAMLSourceVal j) opt =
    [runYAMLSource j opt]
  runSource YAMLSourceNotRequired _ =
    []

runYAMLSource ::
  forall a f.
  ( B.FunctorB a,
    YAML.FromJSON (a Maybe),
    Applicative f
  ) =>
  BS.ByteString ->
  a (Compose Opt f) ->
  Either SourceRunError (a (Compose SourceRunResult f))
runYAMLSource yaml _opt =
  case res of
    Right v -> Right $ B.bmap toSuccess v
    Left exc -> Left $ toError exc
  where
    res :: Either YAML.ParseException (a Maybe)
    res =
      YAML.decodeEither' yaml

    toSuccess :: Maybe x -> Compose SourceRunResult f x
    toSuccess mx =
      Compose $ pure <$> maybe OptNotFound OptParsed mx

    toError :: YAML.ParseException -> SourceRunError
    toError exc =
      SourceRunError Nothing "YAMLSource" (displayException exc)