{-# LANGUAGE GADTs #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Toml.Bi.Combinators
(
BiToml
, Env
, St
, EncodeException
, DecodeException
, encode
, decode
, unsafeDecode
, bijectionMaker
, bool
, int
, double
, str
) where
import Control.Monad.Except (ExceptT, runExceptT, throwError)
import Control.Monad.Reader (Reader, asks, runReader)
import Control.Monad.State (State, gets, modify, runState)
import Data.Bifunctor (first)
import Data.Text (Text)
import Toml.Bi.Monad (Bi, Bijection (..))
import Toml.Parser (ParseException, parse)
import Toml.PrefixTree (Key)
import Toml.Printer (prettyToml)
import Toml.Type (AnyValue (..), TOML (..), Value (..))
import qualified Data.HashMap.Strict as HashMap
data EncodeException
= KeyNotFound Key
| TypeMismatch Text
| ParseError ParseException
deriving (Eq, Show)
type Env = ExceptT EncodeException (Reader TOML)
data DecodeException
= DuplicateKey Key AnyValue
deriving (Eq, Show)
type St = ExceptT DecodeException (State TOML)
type BiToml a = Bi Env St a
encode :: BiToml a -> Text -> Either EncodeException a
encode biToml text = do
toml <- first ParseError (parse text)
runReader (runExceptT $ biRead biToml) toml
decode :: BiToml a -> a -> Either DecodeException Text
decode biToml obj = do
let (result, toml) = runState (runExceptT $ biWrite biToml obj) (TOML mempty mempty)
_ <- result
pure $ prettyToml toml
fromRight :: b -> Either a b -> b
fromRight b (Left _) = b
fromRight _ (Right b) = b
unsafeDecode :: BiToml a -> a -> Text
unsafeDecode biToml text = fromRight (error "Unsafe decode") $ decode biToml text
bijectionMaker :: forall a t .
Text
-> (forall f . Value f -> Maybe a)
-> (a -> Value t)
-> Key
-> BiToml a
bijectionMaker typeTag fromVal toVal key = Bijection input output
where
input :: Env a
input = do
mVal <- asks $ HashMap.lookup key . tomlPairs
case mVal of
Nothing -> throwError $ KeyNotFound key
Just (AnyValue val) -> case fromVal val of
Just v -> pure v
Nothing -> throwError $ TypeMismatch typeTag
output :: a -> St a
output a = do
let val = AnyValue (toVal a)
mVal <- gets $ HashMap.lookup key . tomlPairs
case mVal of
Nothing -> a <$ modify (\(TOML vals nested) -> TOML (HashMap.insert key val vals) nested)
Just _ -> throwError $ DuplicateKey key val
bool :: Key -> BiToml Bool
bool = bijectionMaker "Boolean" fromBool Bool
where
fromBool :: Value f -> Maybe Bool
fromBool (Bool b) = Just b
fromBool _ = Nothing
int :: Key -> BiToml Int
int = bijectionMaker "Int" fromInt (Int . toInteger)
where
fromInt :: Value f -> Maybe Int
fromInt (Int n) = Just (fromIntegral n)
fromInt _ = Nothing
double :: Key -> BiToml Double
double = bijectionMaker "Double" fromDouble Float
where
fromDouble :: Value f -> Maybe Double
fromDouble (Float f) = Just f
fromDouble _ = Nothing
str :: Key -> BiToml Text
str = bijectionMaker "String" fromString String
where
fromString :: Value f -> Maybe Text
fromString (String s) = Just s
fromString _ = Nothing