Toml.Codec.Combinator.Custom

tomland-1.3.3.3: Bidirectional TOML serialization

Copyright | (c) 2018-2022 Kowainik
License | MPL-2.0
Maintainer | Kowainik <xrom.xkov@gmail.com>
Stability | Stable
Portability | Portable
Safe Haskell | None
Language | Haskell2010

Toml.Codec.Combinator.Custom

Contents

- `Text` combinators
- Validation

Description

Contains TOML-specific custom combinators for converting between TOML and special user data types.

See examples below of the situations you may need the following combinators.

Since: 1.3.0.0

Synopsis

- textBy :: (a -> Text) -> (Text -> Either Text a) -> Key -> TomlCodec a
- read :: (Show a, Read a) => Key -> TomlCodec a
- enumBounded :: (Bounded a, Enum a, Show a) => Key -> TomlCodec a
- hardcoded :: (Show a, Eq a) => a -> TomlBiMap a AnyValue -> Key -> TomlCodec a
- validate :: (a -> Either Text a) -> TomlBiMap a AnyValue -> Key -> TomlCodec a
- validateIf :: (a -> Bool) -> TomlBiMap a AnyValue -> Key -> TomlCodec a

# `Text` combinators

textBy :: (a -> Text) -> (Text -> Either Text a) -> Key -> TomlCodec a #

Codec for text values with custom error messages for parsing.

Example:

We have the following type that represents the image format:

    data Format
        = Jpeg
        | Png
        | Gif
        deriving (Show, Read, Enum)

But we want to be able to decode and encode this data type through the custom text representation, that can be formilised in the following functions:

    showFormat :: Format -> Text
    showFormat = case
        Jpeg -> ".jpeg"
        Png  -> ".png"
        Gif  -> ".gif"

    parseFormat :: Text -> Either Text Format
    parseFormat = case
        ".jpeg" -> Right Jpeg
        ".png"  -> Right Png
        ".gif"  -> Right Gif
        other   -> Left $ "Unsupported format: " <> other

To write the codec for `Format` data type using the above rules we can use `textBy` combinator:

    formatCodec :: Key -> TomlCodec Format
    formatCodec = textBy showFormat parseFormat

And now with the `formatCodec "foo"` we can have the following line in our `TOML` perfectly encoded:

    foo = ".gif"

But the `foo = "jif"` will lead to the following error:

    tomland decode error:  Unsupported format: jif

Since: 1.0.0

read :: (Show a, Read a) => Key -> TomlCodec a #

Codec for values with a `Read` and `Show` instance.

Example:

We have the following type that represents the image format:

    data Format
        = Jpeg
        | Png
        | Gif
        deriving (Show, Read, Enum)

And we want to be able to decode and encode this data type through the `Show` and `Read` instances.

To write the codec for `Format` data type using the existing instances we can use `read` combinator. And now with the `Toml.`read` "foo"` we can have the following line in our `TOML` perfectly encoded:

    foo = Gif

But the `foo = ".gif"` will lead to the following error:

    tomland decode error:  Prelude.read: no parse

Since: 0.5.0

enumBounded :: (Bounded a, Enum a, Show a) => Key -> TomlCodec a #

Codec for general nullary sum data types with a `Bounded`, `Enum`, and `Show` instance. This codec is similar to `read` but provides much better error messages than `read` for nullary sum types.

E.g. for the same `Format` example from `read` function, but with the `Toml.`enumBounded` "foo"` codec the error for `foo = "Jif"` in the `TOML` file will look like this:

    tomland decode error:  Value is Jif but expected one of: Jpeg, Png, Gif

Since: 1.1.0.0

hardcoded :: (Show a, Eq a) => a -> TomlBiMap a AnyValue -> Key -> TomlCodec a #

Codec for hardcoded provided values and its `BiMap`.

If you want to decode a single key-value pair where only one value is allowed. Like in the example below:

    scope = "all"

To decode that you could use the following function:

    Toml.hardcoded "all" Toml._Text "scope"

in case if the value in `TOML` is not the same as hardcoded, you will get the following error:

    tomland decode error:  BiMap error in key scope : Value '"foo"' doesn't align with the hardcoded value '"all"'

Since: 1.3.2.0

# Validation

validate :: (a -> Either Text a) -> TomlBiMap a AnyValue -> Key -> TomlCodec a #

Codec that checks the `BiMap` on the given predicate. The predicate function returns the value, if the validation is successful, or the `Text` of the error that should be returned in case of validation failure.

Example:

Let's imagine that we want to have the list in `TOML` that could only have even `Int`s inside. In this case, you can write the following codec:

    allEven :: [Int] -> Either Text [Int]
    allEven xs =
        if all even xs
        then Right xs
        else Left "This is wrong, I asked you for even only :("

    allEvenCodec :: TomlCodec [Int]
    allEvenCodec = Toml.validate allEven (Toml._Array Toml._Int) "myEvenList"

Then in your `TOML` file you can have:

    myEvenList = [2, 4, 6]

But the following one will lead to the error:

    myEvenList = [1, 2, 3]

    tomland decode error:  This is wrong, I asked you for even only :(

Since: 1.3.0.0

validateIf :: (a -> Bool) -> TomlBiMap a AnyValue -> Key -> TomlCodec a #

Similar to `validate` but takes the predicate that returnes `Bool`. The error in case of the validation failure looks like this:

    Value does not pass the validation for key: KEY_NAME

Example:

Let's imagine that we want to have the `Text` field in `TOML` that could only have 3 chars in it. In this case, you can write the following codec:

    my3charTextCodec :: TomlCodec Text
    my3CharTextCodec = Toml.validateIf ((==) 3 . Text.length) Toml._Text "myKeyName"

The in your `TOML` file you can have:

    myKeyName = "abc"

But the following one will lead to the error:

    myKeyName = "I have more than enough"

    tomland decode error:  Value does not pass the validation for key: myKeyName

Since: 1.3.0.0