trial-tomland-0.0.0.0: src/Trial/Tomland.hs
{-# LANGUAGE ApplicativeDo #-}
{- |
Copyright: (c) 2020 Kowainik
SPDX-License-Identifier: MPL-2.0
Maintainer: Kowainik <xrom.xkov@gmail.com>
Trial helpers for @tomland@.
-}
module Trial.Tomland
( trialCodec
, trialStrCodec
, trialMaybeCodec
, taggedTrialCodec
, taggedTrialStrCodec
, taggedTrialMaybeCodec
, taggedTrialListCodec
) where
import Control.Monad (join)
import Data.String (IsString (..))
import Toml (Key, TomlCodec)
import Trial (TaggedTrial, Trial (..), fiasco, maybeToTrial, trialToMaybe, unTag, withTag)
import qualified Data.Text as Text
import qualified Toml
{- | 'TomlCodec' for 'Trial' that adds a given event @e@ if a given
codec fails.
@since 0.0.0.0
-}
trialCodec :: e -> TomlCodec a -> TomlCodec (Trial e a)
trialCodec e = Toml.dimap trialToMaybe (maybeToTrial e) . Toml.dioptional
{- | 'TomlCodec' for 'Trial' that adds an informative message if a
given codec fails.
@since 0.0.0.0
-}
trialStrCodec
:: forall e a
. (IsString e, Semigroup e)
=> (Key -> TomlCodec a)
-> Key
-> TomlCodec (Trial e a)
trialStrCodec codecA key =
Toml.dimap (withTag "TOML") unTag $ taggedTrialStrCodec codecA key
{- | 'TomlCodec' for 'Maybe' inside 'Trial'. Never fails,
doesn't change history of events.
@since 0.0.0.0
-}
trialMaybeCodec :: TomlCodec a -> TomlCodec (Trial e (Maybe a))
trialMaybeCodec = Toml.dimap (join . trialToMaybe) pure . Toml.dioptional
{- | 'TomlCodec' for 'TaggedTrial' that uses given @tag@ in a 'Fiasco'
if a given codec fails, and also adds @tag@ to the result.
@since 0.0.0.0
-}
taggedTrialCodec
:: forall tag a
. tag
-> (Key -> TomlCodec a)
-> Key
-> TomlCodec (TaggedTrial tag a)
taggedTrialCodec tag codecA key =
Toml.dimap (fmap snd . trialToMaybe) handleMaybe
$ Toml.dioptional (codecA key)
where
handleMaybe :: Maybe a -> TaggedTrial tag a
handleMaybe = \case
Nothing -> fiasco tag
Just a -> withTag tag $ pure a
{- | 'TomlCodec' for 'TaggedTrial' that adds an informative message if
a given codec fails, and also adds a tag where the field comes from.
@since 0.0.0.0
-}
taggedTrialStrCodec
:: forall tag a
. (IsString tag, Semigroup tag)
=> (Key -> TomlCodec a)
-> Key
-> TomlCodec (TaggedTrial tag a)
taggedTrialStrCodec codecA key =
Toml.dimap (fmap snd . trialToMaybe) handleMaybe
$ Toml.dioptional (codecA key)
where
keyS :: tag
keyS = fromString $ Text.unpack $ Toml.prettyKey key
handleMaybe :: Maybe a -> TaggedTrial tag a
handleMaybe = \case
Nothing -> fiasco $ "No TOML option specified for key: " <> keyS
Just a -> withTag "TOML" $ pure a
{- | 'TomlCodec' for 'Maybe' inside 'TaggedTrial'. Never fails,
doesn't change history of events, and adds a tag.
@since 0.0.0.0
-}
taggedTrialMaybeCodec :: IsString e => TomlCodec a -> TomlCodec (TaggedTrial e (Maybe a))
taggedTrialMaybeCodec =
Toml.dimap taggedTrialToMaybe (withTag "TOML" . pure) . Toml.dioptional
where
taggedTrialToMaybe :: TaggedTrial e (Maybe a) -> Maybe a
taggedTrialToMaybe = join . fmap snd . trialToMaybe
{- | 'TomlCodec' that decodes with 'Toml.list' and adds 'fiasco' to
the result if the resulting list is empty. It's helpful to handle the
case when the list is not specified at all.
@since 0.0.0.0
-}
taggedTrialListCodec
:: forall e a
. (IsString e, Semigroup e)
=> Key
-> TomlCodec a
-> TomlCodec (TaggedTrial e [a])
taggedTrialListCodec key aCodec = do
res <- taggedTrialStrCodec (Toml.list aCodec) key
pure $ case res of
Result _ (_, []) ->
res <> fiasco ("No TOML value is specified for key: " <> keyToStr key)
Result _ _ -> res
Fiasco _ -> res
where
keyToStr :: Key -> e
keyToStr = fromString . Text.unpack . Toml.prettyKey