registry-options-0.1.0.0: src/Data/Registry/Options/Decoder.hs
-- | This module contains the definition of a 'Decoder'
-- and some default decoders
--
-- A 'Decoder' reads a string and return a Haskell value or a failure
module Data.Registry.Options.Decoder where
import Data.Registry (ApplyVariadic, Typed, fun, funTo)
import qualified Data.Text as T
import Protolude
import Prelude (String)
-- | Decode a value of type a from Text
newtype Decoder a = Decoder {decode :: Text -> Either Text a}
deriving (Functor, Applicative, Monad) via (ReaderT Text (Either Text))
-- | Create a Decoder a for a given constructor of type a
decoderOf :: forall a b. (ApplyVariadic Decoder a b, Typeable a, Typeable b) => a -> Typed b
decoderOf = funTo @Decoder
-- | Add a Decoder to a registry
addDecoder :: forall a. (Typeable a) => (Text -> Either Text a) -> Typed (Decoder a)
addDecoder = fun . Decoder
-- * Common decoders
-- | Decoder for an Int
intDecoder :: Text -> Either Text Int
intDecoder t = maybe (Left $ "cannot read as an Int: " <> t) Right (readMaybe t)
-- | Decoder for a Bool
boolDecoder :: Text -> Either Text Bool
boolDecoder t = maybe (Left $ "cannot read as a Bool: " <> t) Right (readMaybe t)
-- | Decoder for some Text
textDecoder :: Text -> Either Text Text
textDecoder t = if T.null t then Left "empty text" else Right t
-- | Decoder for some String
stringDecoder :: Text -> Either Text String
stringDecoder t = if T.null t then Left "empty string" else Right (toS t)
-- | Create a Decoder for [a]
manyOf :: forall a. Typeable a => Typed (Decoder a -> Decoder [a])
manyOf = fun decodeMany
-- | Create a Decoder for [a] as a comma-separated string
decodeMany :: forall a. Typeable a => Decoder a -> Decoder [a]
decodeMany = decodeManySeparated ","
-- | Create a Decoder for [a] as a separated string
decodeManySeparated :: forall a. Typeable a => Text -> Decoder a -> Decoder [a]
decodeManySeparated separator d = Decoder $ \t -> for (T.strip <$> T.splitOn separator t) (decode d)