tomland 1.3.1.0 → 1.3.2.0
raw patch · 55 files changed
+732/−189 lines, 55 filesdep ~parser-combinators
Dependency ranges changed: parser-combinators
Files
- CHANGELOG.md +17/−0
- README.lhs +2/−2
- README.md +2/−2
- examples/Main.hs +32/−30
- src/Toml.hs +6/−3
- src/Toml/Codec.hs +6/−3
- src/Toml/Codec/BiMap.hs +8/−5
- src/Toml/Codec/BiMap/Conversion.hs +31/−5
- src/Toml/Codec/Code.hs +41/−4
- src/Toml/Codec/Combinator.hs +16/−13
- src/Toml/Codec/Combinator/Common.hs +6/−4
- src/Toml/Codec/Combinator/Custom.hs +35/−4
- src/Toml/Codec/Combinator/List.hs +6/−3
- src/Toml/Codec/Combinator/Map.hs +6/−3
- src/Toml/Codec/Combinator/Monoid.hs +6/−3
- src/Toml/Codec/Combinator/Primitive.hs +6/−3
- src/Toml/Codec/Combinator/Set.hs +6/−3
- src/Toml/Codec/Combinator/Table.hs +6/−3
- src/Toml/Codec/Combinator/Time.hs +6/−3
- src/Toml/Codec/Combinator/Tuple.hs +6/−3
- src/Toml/Codec/Di.hs +6/−3
- src/Toml/Codec/Error.hs +17/−5
- src/Toml/Codec/Generic.hs +43/−6
- src/Toml/Codec/Types.hs +6/−3
- src/Toml/Parser.hs +6/−3
- src/Toml/Parser/Core.hs +6/−3
- src/Toml/Parser/Item.hs +7/−4
- src/Toml/Parser/Key.hs +6/−3
- src/Toml/Parser/String.hs +6/−3
- src/Toml/Parser/Validate.hs +6/−3
- src/Toml/Parser/Value.hs +22/−7
- src/Toml/Type.hs +6/−3
- src/Toml/Type/AnyValue.hs +6/−3
- src/Toml/Type/Edsl.hs +6/−3
- src/Toml/Type/Key.hs +6/−3
- src/Toml/Type/PrefixTree.hs +81/−3
- src/Toml/Type/Printer.hs +63/−8
- src/Toml/Type/TOML.hs +47/−3
- src/Toml/Type/UValue.hs +6/−3
- src/Toml/Type/Value.hs +6/−3
- test/Test/Toml/Codec/BiMap/Conversion.hs +3/−0
- test/Test/Toml/Codec/Code.hs +28/−1
- test/Test/Toml/Codec/Combinator/Common.hs +27/−5
- test/Test/Toml/Codec/Combinator/Custom.hs +1/−0
- test/Test/Toml/Codec/SmallType.hs +3/−2
- test/Test/Toml/Gen.hs +9/−0
- test/Test/Toml/Parser/Integer.hs +4/−2
- test/Test/Toml/Type/Printer.hs +4/−1
- test/golden/pretty_custom_sorted.golden +2/−0
- test/golden/pretty_default.golden +2/−0
- test/golden/pretty_indented_only.golden +2/−0
- test/golden/pretty_lines.golden +31/−0
- test/golden/pretty_sorted_only.golden +2/−0
- test/golden/pretty_unformatted.golden +2/−0
- tomland.cabal +4/−4
CHANGELOG.md view
@@ -3,6 +3,23 @@ `tomland` uses [PVP Versioning][1]. The changelog is available [on GitHub][2]. +## 🐂 1.3.2.0 — Feb 12, 2021++* [#186](https://github.com/kowainik/tomland/issues/186):+ Implement TOML difference. Add `decodeExact` and `decodeFileExact`.+* [#325](https://github.com/kowainik/tomland/issues/325):+ Add ability to one or multiline printing to `PrintOptions` for arrays.+* [#329](https://github.com/kowainik/tomland/issues/329):+ Add `_Harcoded` codec and `hardcoded` combinator.+* [#333](https://github.com/kowainik/tomland/issues/333):+ Fix bug with parsing leading zeroes in numeric values.+* [#334](https://github.com/kowainik/tomland/issues/334):+ Escape unicode characters correctly in `encode`.+* [#364](https://github.com/kowainik/tomland/issues/364):+ Update GHC from `8.10.2` to `8.10.4`.+* [#358](https://github.com/kowainik/tomland/issues/358):+ Upgrade `parser-combinators` upper bound to allow `1.3`.+ ## 1.3.1.0 — Sep 21, 2020 * [#331](https://github.com/kowainik/tomland/issues/331):
README.lhs view
@@ -3,11 +3,11 @@  [](https://github.com/kowainik/tomland/actions)-[](https://ci.appveyor.com/project/kowainik/tomland)+[](https://ci.appveyor.com/project/kowainik/tomland) [](https://hackage.haskell.org/package/tomland) [](http://stackage.org/lts/package/tomland) [](http://stackage.org/nightly/package/tomland)-[](https://github.com/kowainik/tomland/blob/master/LICENSE)+[](https://github.com/kowainik/tomland/blob/main/LICENSE) > “A library is like an island in the middle of a vast sea of ignorance, > particularly if the library is very tall and the surrounding area has been
README.md view
@@ -3,11 +3,11 @@  [](https://github.com/kowainik/tomland/actions)-[](https://ci.appveyor.com/project/kowainik/tomland)+[](https://ci.appveyor.com/project/kowainik/tomland) [](https://hackage.haskell.org/package/tomland) [](http://stackage.org/lts/package/tomland) [](http://stackage.org/nightly/package/tomland)-[](https://github.com/kowainik/tomland/blob/master/LICENSE)+[](https://github.com/kowainik/tomland/blob/main/LICENSE) > “A library is like an island in the middle of a vast sea of ignorance, > particularly if the library is very tall and the surrounding area has been
examples/Main.hs view
@@ -8,8 +8,8 @@ import Control.Applicative ((<|>)) import Control.Arrow ((>>>)) import Data.ByteString (ByteString)-import Data.Hashable (Hashable) import Data.HashSet (HashSet)+import Data.Hashable (Hashable) import Data.IntSet (IntSet) import Data.List.NonEmpty (NonEmpty) import Data.Map.Strict (Map)@@ -19,7 +19,7 @@ import GHC.Generics (Generic) import Toml (TomlCodec, TomlParseError (..), pretty, (.=), (<!>))-import Toml.Codec.Generic (ByteStringAsBytes (..), HasCodec (..), TomlTable (..),+import Toml.Codec.Generic (ByteStringAsBytes (..), HasCodec (..), TomlTableStrip (..), stripTypeNameCodec) import Toml.Type (TOML (..), Value (..)) import Toml.Type.Edsl (mkToml, table, (=:))@@ -84,12 +84,12 @@ matchHex :: Colour -> Maybe Text matchHex = \case Hex t -> Just t- _ -> Nothing+ _ -> Nothing matchRgb :: Colour -> Maybe Rgb matchRgb = \case RGB rgb -> Just rgb- _ -> Nothing+ _ -> Nothing colourCodec :: Toml.Key -> TomlCodec Colour colourCodec key =@@ -124,31 +124,32 @@ <$> Toml.tableMap Toml._KeyText (Toml.list innerCodec) "mapList" .= mapList data Test = Test- { testB :: !Bool- , testI :: !Int- , testF :: !Double- , testS :: !Text- , testA :: ![Text]- , testNE :: !(NonEmpty Text)- , testNET :: !(NonEmpty Int)- , testM :: !(Maybe Bool)- , testX :: !TestInside- , testY :: !(Maybe TestInside)- , testEven :: !Int- , testN :: !N- , testC :: !ColorScheme- , testPair :: !(Int, Text)- , testTriple :: !(Int, Text, Bool)- , testE1 :: !(Either Integer String)- , testE2 :: !(Either String Double)- , testStatus :: !UserStatus- , users :: ![User]- , susers :: !(Set User)- , husers :: !(HashSet User)- , intset :: !IntSet- , payloads :: !(Map Text Int)- , colours :: !(Map Text Colour)- , tableList :: !MapWithList+ { testB :: !Bool+ , testI :: !Int+ , testF :: !Double+ , testS :: !Text+ , testA :: ![Text]+ , testNE :: !(NonEmpty Text)+ , testNET :: !(NonEmpty Int)+ , testM :: !(Maybe Bool)+ , testX :: !TestInside+ , testY :: !(Maybe TestInside)+ , testEven :: !Int+ , testN :: !N+ , testC :: !ColorScheme+ , testPair :: !(Int, Text)+ , testTriple :: !(Int, Text, Bool)+ , testE1 :: !(Either Integer String)+ , testE2 :: !(Either String Double)+ , testStatus :: !UserStatus+ , users :: ![User]+ , susers :: !(Set User)+ , husers :: !(HashSet User)+ , intset :: !IntSet+ , payloads :: !(Map Text Int)+ , colours :: !(Map Text Colour)+ , tableList :: !MapWithList+ , testHardcoded :: !Text } @@ -179,6 +180,7 @@ <*> Toml.map (Toml.text "name") (Toml.int "payload") "payloads" .= payloads <*> Toml.tableMap Toml._KeyText colourCodec "colours" .= colours <*> mapWithListCodec .= tableList+ <*> Toml.hardcoded "abc" Toml._Text "testHardcoded" .= testHardcoded where pairC :: TomlCodec (Int, Text) pairC = Toml.pair (Toml.int "pNum") (Toml.text "pName")@@ -208,7 +210,7 @@ { addressStreet :: !Text , addressHouse :: !Int } deriving stock (Generic)- deriving HasCodec via (TomlTable Address)+ deriving HasCodec via (TomlTableStrip Address) testGeneric :: TomlCodec GenericPerson testGeneric = stripTypeNameCodec
src/Toml.hs view
@@ -1,7 +1,10 @@ {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable This module reexports all functionality of the @tomland@ package. It's recommended to import this module qualified, like this:
src/Toml/Codec.hs view
@@ -1,7 +1,10 @@ {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml.Codec+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable This module provides utilities for implementing and using bidirectional TOML codecs. The concept of bidirectional conversion in
src/Toml/Codec/BiMap.hs view
@@ -6,12 +6,15 @@ {-# LANGUAGE Rank2Types #-} {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml.Codec.BiMap+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable -Implementation of /Tagged Partial Bidirectional Isomorphism/. This-module contains the 'BiMap' type that represents conversion between+Implementation of /Tagged Partial Bidirectional Isomorphism/.+This module contains the 'BiMap' type that represents conversion between two types with the possibility of failure. See "Toml.Codec.BiMap.Conversion" for examples of 'BiMap' with
src/Toml/Codec/BiMap/Conversion.hs view
@@ -1,13 +1,16 @@ {-# LANGUAGE GADTs #-} {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml.Codec.BiMap.Conversion+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable Implementations of 'BiMap' for specific Haskell types and TOML-values. Usually, you use codecs from the "Toml.Codec.Combinator"-module. You may need to use these 'BiMap's instead of codecs in the+values. Usually, you use codecs from the "Toml.Codec.Combinator" module.+You may need to use these 'BiMap's instead of codecs in the following situations: 1. When using 'Toml.Codec.Combinator.List.arrayOf' combinator (or similar).@@ -60,6 +63,7 @@ , _Read , _TextBy , _Validate+ , _Hardcoded -- * 'Key's , _KeyText@@ -548,6 +552,28 @@ _EnumBounded :: (Show a, Enum a, Bounded a) => TomlBiMap a AnyValue _EnumBounded = _EnumBoundedText >>> _Text {-# INLINE _EnumBounded #-}+++{- | 'BiMap' for hardcoded values.+It returns the same value in case of success and 'ArbitraryError' in other case.++@since 1.3.2.0+-}+_Hardcoded :: forall a . (Show a, Eq a) => a -> TomlBiMap a a+_Hardcoded a = BiMap+ { forward = const (Right a)+ , backward = checkValue+ }+ where+ checkValue :: a -> Either TomlBiMapError a+ checkValue v = if v == a+ then Right v+ else Left (ArbitraryError msg)+ where+ msg :: Text+ msg = "Value '" <> T.pack (show v)+ <> "' doesn't align with the hardcoded value '"+ <> T.pack (show a) <> "'" ---------------------------------------------------------------------------- -- Keys
src/Toml/Codec/Code.hs view
@@ -1,7 +1,10 @@ {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml.Codec.Code+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable Conversion from textual representation of @TOML@ values to the Haskell data types by the given 'TomlCodec'.@@ -12,9 +15,11 @@ module Toml.Codec.Code ( -- * Decode decode+ , decodeExact , decodeValidation , decodeFileEither , decodeFile+ , decodeFileExact -- * Encode , encode , encodeToFile@@ -31,7 +36,7 @@ import Toml.Codec.Error (LoadTomlException (..), TomlDecodeError (..), prettyTomlDecodeErrors) import Toml.Codec.Types (Codec (..), TomlCodec, TomlState (..)) import Toml.Parser (parse)-import Toml.Type (TOML (..))+import Toml.Type (TOML (..), tomlDiff) import Toml.Type.Printer (pretty) import qualified Data.Text.IO as TIO@@ -55,6 +60,26 @@ decode :: TomlCodec a -> Text -> Either [TomlDecodeError] a decode codec = validationToEither . decodeValidation codec +{- | Convert textual representation of @TOML@ into user data type by the+provided codec.++Unlike 'decode', this function returns 'NotExactDecode' error in case if given+@TOML@ has redundant fields and other elements.++@since 1.3.2.0+-}+decodeExact :: TomlCodec a -> Text -> Either [TomlDecodeError] a+decodeExact codec text = case parse text of+ Left err -> Left [ParseError err]+ Right toml -> case runTomlCodec codec toml of+ Failure errs -> Left errs+ Success a ->+ let tomlExpected = execTomlCodec codec a+ aDiff = tomlDiff toml tomlExpected in+ if aDiff == mempty+ then Right a+ else Left [NotExactDecode aDiff]+ {- | Similar to 'decodeValidation', but takes a path to a file with textual @TOML@ values from which it decodes them with the provided codec. @@ -78,6 +103,18 @@ -> FilePath -> m (Either [TomlDecodeError] a) decodeFileEither codec = fmap validationToEither . decodeFileValidation codec++{- | Similar to 'decodeExact', but takes a path to a file with textual @TOML@+values from which it decodes them with the provided codec.++@since 1.3.2.0+-}+decodeFileExact+ :: forall a m . (MonadIO m)+ => TomlCodec a+ -> FilePath+ -> m (Either [TomlDecodeError] a)+decodeFileExact codec = fmap (decodeExact codec) . liftIO . TIO.readFile {- | Similar to 'decodeFileEither', throws 'LoadTomlException' in case of parse errors ('TomlDecodeError').
src/Toml/Codec/Combinator.hs view
@@ -1,7 +1,10 @@ {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml.Codec.Combinator+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable Contains TOML-specific combinators for converting between TOML and user data types.@@ -63,7 +66,7 @@ TOML-specific combinators for converting between TOML and Haskell primitive types, e.g. 'int' \<-\> 'Int', 'byteString' \<-\> 'ByteString'. -See the "Toml.Codec.Combinators.Primitive" module documentation for the overview+See the "Toml.Codec.Combinator.Primitive" module documentation for the overview table and more examples. -} @@ -73,21 +76,21 @@ can use in your configuration. @tomland@ provides mapping of those primitives to types from the @time@ library. -See the "Toml.Codec.Combinators.Time" module documentation for the overview+See the "Toml.Codec.Combinator.Time" module documentation for the overview table and more examples. -} {- $table Combinators for the @TOML@ tables. -See the "Toml.Codec.Combinators.Table" module documentation for more examples.+See the "Toml.Codec.Combinator.Table" module documentation for more examples. -} {- $list TOML-specific combinators for converting between TOML and Haskell list-like data types. -See the "Toml.Codec.Combinators.List" module documentation for the overview+See the "Toml.Codec.Combinator.List" module documentation for the overview table and more examples. -} @@ -95,7 +98,7 @@ TOML-specific combinators for converting between TOML and Haskell set-like data types. -See the "Toml.Codec.Combinators.Set" module documentation for the overview+See the "Toml.Codec.Combinator.Set" module documentation for the overview table and more examples. -} @@ -103,7 +106,7 @@ TOML-specific combinators for converting between TOML and Haskell map-like data types. -See the "Toml.Codec.Combinators.Map" module documentation for the overview+See the "Toml.Codec.Combinator.Map" module documentation for the overview table and more examples. -} @@ -114,7 +117,7 @@ constructors of sum types), you can find codecs from this module helpful. -See the "Toml.Codec.Combinators.Tuple" module documentation for the overview+See the "Toml.Codec.Combinator.Tuple" module documentation for the overview table and more examples. -} @@ -124,7 +127,7 @@ the [Partial Options Monoid](https://medium.com/@jonathangfischoff/the-partial-options-monoid-pattern-31914a71fc67) pattern. -See the "Toml.Codec.Combinators.Monoid" module documentation for the overview+See the "Toml.Codec.Combinator.Monoid" module documentation for the overview table and more examples. -} @@ -137,7 +140,7 @@ 'Bounded' instances of the data type only and provides descriptive error messages at the same time. -See the "Toml.Codec.Combinators.Custom" module documentation for the overview+See the "Toml.Codec.Combinator.Custom" module documentation for the overview table and more examples. -} @@ -148,5 +151,5 @@ ones. But if you need something that library doesn't provide, you can find functions in this module useful. -See the "Toml.Codec.Combinators.Common" module documentation for more examples.+See the "Toml.Codec.Combinator.Common" module documentation for more examples. -}
src/Toml/Codec/Combinator/Common.hs view
@@ -1,9 +1,12 @@ {-# LANGUAGE FlexibleContexts #-} {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml.Codec.Combinator.Common+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable This module implements common utilities for writing custom codecs without diving into internal implementation details. Most of the time@@ -61,7 +64,6 @@ output a = do anyVal <- eitherToTomlState $ forward a a <$ modify (insertKeyAnyVal key anyVal)- {- | Throw error on 'Left', or perform a given action with 'Right'.
src/Toml/Codec/Combinator/Custom.hs view
@@ -1,7 +1,10 @@ {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml.Codec.Combinator.Custom+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable Contains TOML-specific custom combinators for converting between TOML and special user data types.@@ -16,6 +19,7 @@ textBy , read , enumBounded+ , hardcoded -- * Validation , validate@@ -24,10 +28,11 @@ import Prelude hiding (read) +import Control.Category ((>>>)) import Data.Text (Text) import Toml.Codec.BiMap (TomlBiMap)-import Toml.Codec.BiMap.Conversion (_EnumBounded, _Read, _TextBy, _Validate)+import Toml.Codec.BiMap.Conversion (_EnumBounded, _Read, _TextBy, _Validate, _Hardcoded) import Toml.Codec.Combinator.Common (match) import Toml.Codec.Types (TomlCodec) import Toml.Type.AnyValue (AnyValue)@@ -149,6 +154,32 @@ enumBounded :: (Bounded a, Enum a, Show a) => Key -> TomlCodec a enumBounded = match _EnumBounded {-# INLINE enumBounded #-}++{- | 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+-}+hardcoded :: (Show a, Eq a) => a -> TomlBiMap a AnyValue -> Key -> TomlCodec a+hardcoded a aBm = match (_Hardcoded a >>> aBm) {- | Codec that checks the 'BiMap' on the given predicate. The predicate function returns the value, if the validation is successful, or
src/Toml/Codec/Combinator/List.hs view
@@ -1,7 +1,10 @@ {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml.Codec.Combinator.List+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable TOML-specific combinators for converting between TOML and Haskell list-like data types.
src/Toml/Codec/Combinator/Map.hs view
@@ -3,9 +3,12 @@ {-# LANGUAGE TupleSections #-} {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml.Codec.Combinator.Map+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable TOML-specific combinators for converting between TOML and Haskell Map-like data types.
src/Toml/Codec/Combinator/Monoid.hs view
@@ -1,7 +1,10 @@ {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml.Codec.Combinator.Monoid+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable TOML-specific combinators for converting between TOML and Haskell 'Monoid' wrapper data types. These codecs are especially handy when you are implementing
src/Toml/Codec/Combinator/Primitive.hs view
@@ -1,7 +1,10 @@ {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml.Codec.Combinator.Primitive+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable TOML-specific combinators for converting between TOML and Haskell primitive types, e.g. 'Int', 'ByteString'.
src/Toml/Codec/Combinator/Set.hs view
@@ -1,7 +1,10 @@ {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml.Codec.Combinator.Set+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable TOML-specific combinators for converting between TOML and Haskell Set-like data types.
src/Toml/Codec/Combinator/Table.hs view
@@ -1,7 +1,10 @@ {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml.Codec.Combinator.Table+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable Contains TOML-specific combinators for converting between TOML and user data types.
src/Toml/Codec/Combinator/Time.hs view
@@ -1,7 +1,10 @@ {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml.Codec.Combinator.Time+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable TOML-specific combinators for converting between TOML and Haskell date and time data types. TOML specification describes date and time primitives you
src/Toml/Codec/Combinator/Tuple.hs view
@@ -1,7 +1,10 @@ {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml.Codec.Combinator.Tuple+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable TOML-specific combinators for converting between TOML and Haskell tuples. It's recommended to create your custom data types and implement codecs
src/Toml/Codec/Di.hs view
@@ -1,7 +1,10 @@ {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml.Codec.Di+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable Forward and backward mapping functions and combinators (similar to profunctors).
src/Toml/Codec/Error.hs view
@@ -1,9 +1,12 @@ {-# LANGUAGE DeriveAnyClass #-} {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml.Codec.Error+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable Core error types, including 'TomlDecodeError' and 'LoadTomlException'. @@ -26,7 +29,8 @@ import Toml.Codec.BiMap (TomlBiMapError, prettyBiMapError) import Toml.Parser (TomlParseError (..)) import Toml.Type.Key (Key (..))-import Toml.Type.Printer (prettyKey)+import Toml.Type.TOML (TOML)+import Toml.Type.Printer (prettyKey, pretty) import qualified Data.Text as Text @@ -45,6 +49,11 @@ @since 1.3.0.0 -} | ParseError !TomlParseError -- ^ Exception during parsing+ | NotExactDecode !TOML+ {- ^ Unused field left in the decoded TOML.++ @since 1.3.2.0+ -} deriving stock (Show, Eq, Generic) deriving anyclass (NFData) @@ -69,7 +78,10 @@ TableNotFound name -> "Table [" <> prettyKey name <> "] is not found" TableArrayNotFound name -> "Table array [[" <> prettyKey name <> "]] is not found" ParseError (TomlParseError msg) ->- "Parse error during conversion from TOML to custom user type: \n " <> msg+ "Parse error during conversion from TOML to custom user type:\n " <> msg+ NotExactDecode toml ->+ "The following fields are present in TOML but not used:\n"+ <> pretty toml {- | File loading error data type.
src/Toml/Codec/Generic.hs view
@@ -10,9 +10,12 @@ {-# LANGUAGE UndecidableInstances #-} {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml.Codec.Generic+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable This module contains implementation of the 'Generic' TOML codec. If your data types are big and nested, and you want to have codecs for them without writing a lot of@@ -128,14 +131,15 @@ -- * Deriving Via , TomlTable (..)+ , TomlTableStrip (..) ) where import Data.ByteString (ByteString) import Data.Char (isLower, toLower) import Data.Coerce (coerce)-import Data.Hashable (Hashable) import Data.HashMap.Strict (HashMap) import Data.HashSet (HashSet)+import Data.Hashable (Hashable) import Data.IntMap.Strict (IntMap) import Data.IntSet (IntSet) import Data.Kind (Type)@@ -150,8 +154,8 @@ import Data.Time (Day, LocalTime, TimeOfDay, ZonedTime) import Data.Typeable (Typeable, typeRep) import Data.Word (Word8)-import GHC.Generics ((:*:) (..), (:+:), C1, D1, Generic (..), K1 (..), M1 (..), Rec0, S1,- Selector (..))+import GHC.Generics (C1, D1, Generic (..), K1 (..), M1 (..), Rec0, S1, Selector (..), (:*:) (..),+ (:+:)) import GHC.TypeLits (ErrorMessage (..), TypeError) import Numeric.Natural (Natural) @@ -716,6 +720,39 @@ -- | @since 1.3.0.0 instance (Generic a, GenericCodec (Rep a)) => HasItemCodec (TomlTable a) where hasItemCodec = Right $ Toml.diwrap $ genericCodec @a+ {-# INLINE hasItemCodec #-}++{- | @newtype@ for generic deriving of 'HasCodec' typeclass for custom data+types that should be wrapped into a separate table.++Similar to 'TomlTable' but also strips the data type name prefix from+TOML keys.++@personCodec@ from the 'TomlTable' comment corresponds to the TOML of+the following structure:++@+name = "foo"+[address]+ street = \"Bar\"+ house = 42+@++@since 1.3.2.0+-}+newtype TomlTableStrip a = TomlTableStrip+ { unTomlTableStrip :: a+ }++-- | @since 1.3.2.0+instance (Generic a, GenericCodec (Rep a), Typeable a) => HasCodec (TomlTableStrip a) where+ hasCodec :: Key -> TomlCodec (TomlTableStrip a)+ hasCodec = Toml.diwrap . Toml.table (stripTypeNameCodec @a)+ {-# INLINE hasCodec #-}++-- | @since 1.3.2.0+instance (Generic a, GenericCodec (Rep a), Typeable a) => HasItemCodec (TomlTableStrip a) where+ hasItemCodec = Right $ Toml.diwrap $ stripTypeNameCodec @a {-# INLINE hasItemCodec #-} {- $bytestring
src/Toml/Codec/Types.hs view
@@ -5,9 +5,12 @@ {-# LANGUAGE UndecidableInstances #-} {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml.Codec.Types+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable Contains general underlying monad for bidirectional conversion.
src/Toml/Parser.hs view
@@ -1,7 +1,10 @@ {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml.Parser+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable Parser for text to TOML AST.
src/Toml/Parser/Core.hs view
@@ -1,7 +1,10 @@ {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml.Parser.Core+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable Core functions for TOML parser. -}
src/Toml/Parser/Item.hs view
@@ -1,7 +1,10 @@-{-|-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+{- |+Module : Toml.Parser.Item+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable This module contains the definition of the 'TomlItem' data type which represents either key-value pair or table name. This data type serves the
src/Toml/Parser/Key.hs view
@@ -1,7 +1,10 @@ {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml.Parser.Key+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable Parsers for keys and table names.
src/Toml/Parser/String.hs view
@@ -1,7 +1,10 @@ {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml.Parser.String+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable Parsers for strings in TOML format, including basic and literal strings both singleline and multiline.
src/Toml/Parser/Validate.hs view
@@ -1,7 +1,10 @@ {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml.Parser.Validate+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable This module contains functions that aggregate the result of 'Toml.Parser.Item.tomlP' parser into 'TOML'. This approach allows to keep parser
src/Toml/Parser/Value.hs view
@@ -1,7 +1,10 @@ {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml.Parser.Value+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable Parser for 'UValue'. -}@@ -25,7 +28,7 @@ import Data.String (fromString) import Text.Read (readMaybe)-import Text.Megaparsec (parseMaybe)+import Text.Megaparsec (observing, parseMaybe) import Toml.Parser.Core (Parser, char, digitChar, hexDigitChar, octDigitChar, binDigitChar, hexadecimal, octal, binary, lexeme, sc, signed, string, text, try, (<?>))@@ -35,14 +38,26 @@ -- | Parser for decimap 'Integer': included parsing of underscore. decimalP :: Parser Integer-decimalP = zero <|> more+decimalP = do+ value <- observing $ try leadingZeroP+ case value of+ Left _ -> do+ try more+ Right _ -> + fail "Leading zero."+ where- zero, more :: Parser Integer- zero = 0 <$ char '0'+ leadingZeroP :: Parser String+ leadingZeroP = do+ count 1 (char '0') >>= (\_ -> some digitChar)++ more :: Parser Integer more = check =<< readMaybe . concat <$> sepBy1 (some digitChar) (char '_') check :: Maybe Integer -> Parser Integer check = maybe (fail "Not an integer") pure++ -- | Parser for hexadecimal, octal and binary numbers : included parsing numberP :: Parser Integer -> Parser Char -> String -> Parser Integer
src/Toml/Type.hs view
@@ -1,7 +1,10 @@ {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml.Type+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable Core types for TOML AST. -}
src/Toml/Type/AnyValue.hs view
@@ -6,9 +6,12 @@ {-# LANGUAGE KindSignatures #-} {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml.Type.AnyValue+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable Existential wrapper over 'Value' type and matching functions.
src/Toml/Type/Edsl.hs view
@@ -1,7 +1,10 @@ {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml.Type.Edsl+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable This module introduces EDSL for manually specifying 'TOML' data types.
src/Toml/Type/Key.hs view
@@ -1,9 +1,12 @@ {-# LANGUAGE PatternSynonyms #-} {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml.Type.Key+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable Implementation of key type. The type is used for key-value pairs and table names.
src/Toml/Type/PrefixTree.hs view
@@ -2,9 +2,12 @@ {-# LANGUAGE PatternSynonyms #-} {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml.Type.PrefixTree+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable Implementation of prefix tree for TOML AST. @@ -19,6 +22,8 @@ , insertT , lookupT , toListT+ , addPrefixT+ , differenceWithT -- * Prefix map that stores roots of 'PrefixTree' , PrefixMap@@ -27,6 +32,7 @@ , lookup , fromList , toList+ , differenceWith ) where import Prelude hiding (lookup)@@ -67,7 +73,29 @@ instance Semigroup (PrefixTree a) where a <> b = foldl' (\tree (k, v) -> insertT k v tree) a (toListT b) +{- | Push 'Prefix' inside the given 'PrefixTree'. +@since 1.3.2.0+-}+addPrefixT :: Prefix -> PrefixTree a -> PrefixTree a+addPrefixT pref = \case+ Leaf k a -> Leaf (pref <> k) a+ Branch k ma pma -> Branch (pref <> k) ma pma++{- | Convert branches to 'Leaf' or remove them at all.++@since 1.3.2.0+-}+compressTree :: PrefixTree a -> Maybe (PrefixTree a)+compressTree = \case+ l@(Leaf _ _) -> Just l+ b@(Branch p ma pma) -> case HashMap.toList pma of+ [] -> ma >>= \a -> Just (Leaf p a)+ [(_, child)] -> case ma of+ Just _ -> Just b+ Nothing -> compressTree $ addPrefixT p child+ _ : _ : _ -> Just b+ {- | Creates a 'PrefixTree' of one key-value element. @since 0.0.0@@ -165,3 +193,53 @@ -} toList :: PrefixMap a -> [(Key, a)] toList = concatMap (\(p, tr) -> first (p <|) <$> toListT tr) . HashMap.toList++{- | Difference of two 'PrefixMap's. Returns elements of the first 'PrefixMap'+that are not existing in the second one.++@since 1.3.2.0+-}+differenceWith :: (a -> b -> Maybe a) -> PrefixMap a -> PrefixMap b -> PrefixMap a+differenceWith f = HashMap.differenceWith (differenceWithT f)++{- | Difference of two 'PrefixTree's. Returns elements of the first 'PrefixTree'+that are not existing in the second one.++@since 1.3.2.0+-}+differenceWithT :: (a -> b -> Maybe a) -> PrefixTree a -> PrefixTree b -> Maybe (PrefixTree a)+differenceWithT f pt1 pt2 = case (pt1, pt2) of+ (Leaf k1 a, Leaf k2 b)+ | k1 == k2 -> f a b >>= \aNew -> Just (Leaf k1 aNew)+ | otherwise -> Just (Leaf k1 a)++ (l@(Leaf k a), Branch p mb pmb) -> case keysDiff k p of+ Equal -> mb >>= f a >>= \aNew -> Just (Leaf k aNew)+ NoPrefix -> Just l+ FstIsPref _ -> Just l+ SndIsPref kSuf -> case HashMap.toList $ differenceWith f (single kSuf a) pmb of+ -- zero elements+ [] -> Nothing+ -- our single key+ [(_, aNew)] -> Just $ addPrefixT k aNew+ -- shouldn't happen, but for some reasons+ _ : _ : _ -> Nothing+ Diff _ _ _ -> Just l++ (br@(Branch p ma pma), Leaf k b) -> case keysDiff p k of+ Equal -> compressTree $ Branch p (ma >>= \a -> f a b) pma+ NoPrefix -> Just br+ FstIsPref kSuf -> compressTree $ Branch p ma (differenceWith f pma $ single kSuf b)+ SndIsPref _ -> Just br+ Diff _ _ _ -> Just br++ (b1@(Branch p1 ma pma), Branch p2 mb pmb) -> case keysDiff p1 p2 of+ Equal -> compressTree $+ Branch p1 (ma >>= \a -> mb >>= \b -> f a b) (differenceWith f pma pmb)+ NoPrefix -> Just b1+ FstIsPref p2Suf@(p2Head :|| _) -> compressTree $+ Branch p1 ma (differenceWith f pma $ HashMap.singleton p2Head $ Branch p2Suf mb pmb)+ SndIsPref p1Suf@(p1Head :|| _) -> case HashMap.lookup p1Head pmb of+ Nothing -> Just b1+ Just ch -> addPrefixT p2 <$> differenceWithT f (Branch p1Suf ma pma) ch+ Diff _ _ _ -> Just b1
src/Toml/Type/Printer.hs view
@@ -1,9 +1,12 @@ {-# LANGUAGE TypeFamilies #-} {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml.Type.Printer+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable Contains functions for pretty printing @toml@ types. @@ -12,6 +15,7 @@ module Toml.Type.Printer ( PrintOptions(..)+ , Lines(..) , defaultOptions , pretty , prettyOptions@@ -19,14 +23,18 @@ ) where import Data.Bifunctor (first)+import Data.Char (isAscii, ord) import Data.Coerce (coerce) import Data.Function (on) import Data.HashMap.Strict (HashMap)-import Data.List (sortBy)+import Data.List (sortBy, foldl') import Data.List.NonEmpty (NonEmpty)+import Data.Semigroup (stimes) import Data.Text (Text) import Data.Time (ZonedTime, defaultTimeLocale, formatTime) +import Text.Printf (printf)+ import Toml.Type.AnyValue (AnyValue (..)) import Toml.Type.Key (Key (..), Piece (..)) import Toml.Type.PrefixTree (PrefixMap, PrefixTree (..))@@ -38,6 +46,7 @@ import qualified Data.Text as Text + {- | Configures the pretty printer. @since 0.5.0@@ -54,6 +63,25 @@ @since 1.1.0.0 -} , printOptionsIndent :: !Int+ {- | How to print Array.+ OneLine:++ @+ foo = [a, b]+ @++ MultiLine:++ @+ foo =+ [ a+ , b+ ]+ @++ Default is 'OneLine'.+ -}+ , printOptionsLines :: !Lines } {- | Default printing options.@@ -64,8 +92,10 @@ @since 0.5.0 -} defaultOptions :: PrintOptions-defaultOptions = PrintOptions (Just compare) 2+defaultOptions = PrintOptions (Just compare) 2 OneLine +data Lines = OneLine | MultiLine+ {- | Converts 'TOML' type into 'Data.Text.Text' (using 'defaultOptions'). For example, this@@ -132,22 +162,39 @@ where kvText :: (Key, AnyValue) -> Text kvText (k, AnyValue v) =- tabWith options i <> prettyKey k <> " = " <> valText v+ tabWith options i <> prettyKey k <> " = " <> valText v valText :: Value t -> Text valText (Bool b) = Text.toLower $ showText b valText (Integer n) = showText n valText (Double d) = showDouble d- valText (Text s) = showText s+ valText (Text s) = showTextUnicode s valText (Zoned z) = showZonedTime z valText (Local l) = showText l valText (Day d) = showText d valText (Hours h) = showText h- valText (Array a) = "[" <> Text.intercalate ", " (map valText a) <> "]"+ valText (Array a) = withLines options valText a showText :: Show a => a -> Text showText = Text.pack . show + + -- | Function encodes all non-ascii characters in TOML defined form using the isAscii function+ showTextUnicode :: Text -> Text+ showTextUnicode text = Text.pack $ show finalText+ where+ xss = Text.unpack text+ finalText = foldl' (\acc (ch, asciiCh) -> acc ++ getCh ch asciiCh) "" asciiArr++ asciiArr = zip xss $ asciiStatus xss++ getCh :: Char -> Bool -> String+ getCh ch True = [ch] -- it is true ascii character+ getCh ch False = printf "\\U%08x" (ord ch) :: String -- it is not true ascii character, it must be encoded++ asciiStatus :: String -> [Bool]+ asciiStatus = map isAscii+ showDouble :: Double -> Text showDouble d | isInfinite d && d < 0 = "-inf" | isInfinite d = "inf"@@ -227,3 +274,11 @@ addPrefix key = \case "" -> prettyKey key prefix -> prefix <> "." <> prettyKey key++withLines :: PrintOptions -> (Value t -> Text) -> [Value t] -> Text+withLines PrintOptions{..} valTxt a = case printOptionsLines of+ OneLine -> "[" <> Text.intercalate ", " (map valTxt a) <> "]"+ MultiLine -> off <> "[ " <> Text.intercalate (off <> ", ") (map valTxt a) <> off <> "]"+ where+ off :: Text+ off = "\n" <> stimes printOptionsIndent " "
src/Toml/Type/TOML.hs view
@@ -1,9 +1,12 @@ {-# LANGUAGE DeriveAnyClass #-} {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml.Type.TOML+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable Type of TOML AST. This is intermediate representation of TOML parsed from text. -}@@ -14,6 +17,8 @@ , insertKeyAnyVal , insertTable , insertTableArrays++ , tomlDiff ) where import Control.DeepSeq (NFData)@@ -27,6 +32,7 @@ import Toml.Type.Value (Value) import qualified Data.HashMap.Strict as HashMap+import qualified Data.List.NonEmpty as NE import qualified Toml.Type.PrefixTree as Prefix @@ -142,3 +148,41 @@ { tomlTableArrays = HashMap.insert k arr (tomlTableArrays toml) } {-# INLINE insertTableArrays #-}++{- | Difference of two 'TOML's. Returns elements of the first 'TOML' that are+not existing in the second one.++@since 1.3.2.0+-}+tomlDiff :: TOML -> TOML -> TOML+tomlDiff t1 t2 = TOML+ { tomlPairs = HashMap.difference (tomlPairs t1) (tomlPairs t2)+ , tomlTables = prefixMapDiff (tomlTables t1) (tomlTables t2)+ , tomlTableArrays = HashMap.differenceWith interTomlsDiff+ (tomlTableArrays t1)+ (tomlTableArrays t2)+ }+ where+ interTomlsDiff :: NonEmpty TOML -> NonEmpty TOML -> Maybe (NonEmpty TOML)+ interTomlsDiff tl1 tl2 = NE.nonEmpty $ tomlListDiff (NE.toList tl1) (NE.toList tl2)+{-# INLINE tomlDiff #-}++{- | Difference of two 'PrefixMap's. Returns elements of the first 'PrefixMap'+that are not existing in the second one.++@since 1.3.2.0+-}+prefixMapDiff :: PrefixMap TOML -> PrefixMap TOML -> PrefixMap TOML+prefixMapDiff = Prefix.differenceWith $ \toml1 toml2 -> let diff = tomlDiff toml1 toml2 in+ if diff == mempty+ then Nothing+ else Just diff+++tomlListDiff :: [TOML] -> [TOML] -> [TOML]+tomlListDiff [] _ = []+tomlListDiff ts [] = ts+tomlListDiff (t1:t1s) (t2:t2s) = let diff = tomlDiff t1 t2 in+ if diff == mempty+ then tomlListDiff t1s t2s+ else diff : tomlListDiff t1s t2s
src/Toml/Type/UValue.hs view
@@ -1,9 +1,12 @@ {-# LANGUAGE GADTs #-} {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml.Type.UValue+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable Intermediate untype value representation used for parsing.
src/Toml/Type/Value.hs view
@@ -7,9 +7,12 @@ {-# LANGUAGE TypeOperators #-} {- |-Copyright: (c) 2018-2020 Kowainik-SPDX-License-Identifier: MPL-2.0-Maintainer: Kowainik <xrom.xkov@gmail.com>+Module : Toml.Type.Value+Copyright : (c) 2018-2021 Kowainik+SPDX-License-Identifier : MPL-2.0+Maintainer : Kowainik <xrom.xkov@gmail.com>+Stability : Stable+Portability : Portable GADT value for TOML.
test/Test/Toml/Codec/BiMap/Conversion.hs view
@@ -55,6 +55,9 @@ it "EnumBounded (Ordering)" $ testBiMap B._EnumBounded $ Gen.enumBounded @_ @Ordering it "Read (Integer)" $ testBiMap B._Read G.genInteger it "TextBy (Text)" $ testBiMap (B._TextBy id Right) G.genText+ it "Hardcoded (Text)" $ do+ txt <- forAll G.genText+ testBiMap (B._Hardcoded txt) (pure txt) describe "Key" $ do it "KeyText" $ testBiMap B._KeyText G.genKey
test/Test/Toml/Codec/Code.hs view
@@ -5,13 +5,15 @@ import Test.Hspec (Spec, describe, it, shouldBe) import Toml.Codec.BiMap (TomlBiMapError (..))-import Toml.Codec.Code (decode)+import Toml.Codec.Code (decode, decodeExact) import Toml.Codec.Error (TomlDecodeError (..)) import Toml.Parser (TomlParseError (..)) import Toml.Type.AnyValue (AnyValue (..), MatchError (..)) import Toml.Type.Key (Key) import Toml.Type.Value (TValue (..), Value (..))+import Toml.Type.Edsl (mkToml, table, tableArray, (=:)) +import qualified Data.List.NonEmpty as NE import qualified Data.Map.Strict as Map import qualified Data.Text as Text import qualified Toml.Codec as Toml@@ -33,6 +35,13 @@ it "fails decode text as Toml.int" $ decode (Toml.int "a") "a = 'foo'" `shouldBe` Left [ matchErr "a" TInteger $ AnyValue $ Text "foo"]+ it "exact decode" $+ decodeExact (Toml.int "a") "a = 4" `shouldBe`+ Right 4+ it "fails to decode exact when there is other field" $+ decodeExact (Toml.int "a") "a = 4\nb = 'foo'" `shouldBe`+ Left [ NotExactDecode $ mkToml $ "b" =: "foo"]+ -- table it "fails to decode table on missing field" $ decode (Toml.table (Toml.int "x") "foo") "" `shouldBe`@@ -40,6 +49,12 @@ it "fails to decode table on missing field" $ decode (Toml.table (Toml.int "x") "foo") "[foo]" `shouldBe` Left [ KeyNotFound "foo.x"]+ it "exact decode table" $+ decodeExact (Toml.table (Toml.int "x") "foo") "[foo]\nx = 5" `shouldBe`+ Right 5+ it "fails to decode exact table on missing field" $+ decodeExact (Toml.table (Toml.int "x") "foo") "[foo]\ny = 'abc'\nx = 5" `shouldBe`+ Left [ NotExactDecode $ mkToml $ table "foo" $ "y" =: "abc"] -- lists @@ -69,6 +84,15 @@ decode (Toml.nonEmpty (Toml.int "i") "foo") "foo = []" `shouldBe` Left [TableArrayNotFound "foo"] + -- table arrays+ it "exact decode table array" $+ decodeExact (Toml.list (Toml.int "x") "foo") "[[foo]]\nx = 1" `shouldBe`+ Right [1]++ it "fails to exact decode table array with redundant field" $+ decodeExact (Toml.list (Toml.int "x") "foo") "[[foo]]\nx = 1\ny = 'abc'" `shouldBe`+ Left [NotExactDecode $ mkToml $ tableArray "foo" $ ("y" =: "abc") NE.:| []]+ -- map it "map: decodes to an empty map when field is missing" $ decode (Toml.map (Toml.int "key") (Toml.text "val") "foo") "" `shouldBe`@@ -105,6 +129,9 @@ it "fails to validateIf" $ decode (Toml.validateIf even Toml._Int "foo") "foo = 5" `shouldBe` Left [BiMapError "foo" $ ArbitraryError "Value does not pass the validation for key: foo"]+ it "fails to hardcoded" $+ decode (Toml.hardcoded "bar" Toml._Text "foo") "foo = \"baz\"" `shouldBe`+ Left [BiMapError "foo" $ ArbitraryError "Value '\"baz\"' doesn't align with the hardcoded value '\"bar\"'"] where matchErr :: Key -> TValue -> AnyValue -> TomlDecodeError matchErr key valueExpected valueActual = BiMapError key $ WrongValue $ MatchError {..}
test/Test/Toml/Codec/Combinator/Common.hs view
@@ -1,5 +1,6 @@ module Test.Toml.Codec.Combinator.Common ( codecRoundtrip+ , exactCodecRoundtrip -- * Double helpers , Batman (..)@@ -8,12 +9,14 @@ , batmanFloatCodec ) where +import Data.Text (Text) import Hedgehog (Gen, forAll, tripping) import Test.Hspec (Arg, Expectation, SpecWith, it) import Test.Hspec.Hedgehog (hedgehog) import Toml.Codec.BiMap (TomlBiMap)-import Toml.Codec.Code (decode, encode)+import Toml.Codec.Code (decode, decodeExact, encode)+import Toml.Codec.Error (TomlDecodeError) import Toml.Codec.Types (TomlCodec) import Toml.Type.AnyValue (AnyValue) import Toml.Type.Key (Key)@@ -22,20 +25,39 @@ import qualified Toml.Codec as Toml -codecRoundtrip+codecRoundtripWith :: forall a . (Eq a, Show a)- => String+ => (TomlCodec a -> Text -> Either [TomlDecodeError] a)+ -> String -> (Key -> TomlCodec a) -> Gen a -> SpecWith (Arg Expectation)-codecRoundtrip typeName mkCodec genA = it label $ hedgehog $ do+codecRoundtripWith dcode typeName mkCodec genA = it label $ hedgehog $ do a <- forAll genA let codec = mkCodec "a"- tripping a (encode codec) (decode codec)+ tripping a (encode codec) (dcode codec) where label :: String label = typeName ++ ": decode . encode ≡ id"++codecRoundtrip+ :: forall a+ . (Eq a, Show a)+ => String+ -> (Key -> TomlCodec a)+ -> Gen a+ -> SpecWith (Arg Expectation)+codecRoundtrip = codecRoundtripWith decode++exactCodecRoundtrip+ :: forall a+ . (Eq a, Show a)+ => String+ -> (Key -> TomlCodec a)+ -> Gen a+ -> SpecWith (Arg Expectation)+exactCodecRoundtrip str = codecRoundtripWith decodeExact ("Exact " <> str) -- | Wrapper over 'Double' and 'Float' to be equal on @NaN@ values. newtype Batman a = Batman
test/Test/Toml/Codec/Combinator/Custom.hs view
@@ -25,3 +25,4 @@ codecRoundtrip "TextBy " (Toml.textBy (Text.pack . show) (first Text.pack . readEither . Text.unpack)) Gen.genInt+ codecRoundtrip "Hardcoded (Text)" (Toml.hardcoded "abc" Toml._Text) (pure "abc")
test/Test/Toml/Codec/SmallType.hs view
@@ -14,7 +14,7 @@ import Hedgehog (Gen) import Test.Hspec (Spec, describe) -import Test.Toml.Codec.Combinator.Common (codecRoundtrip)+import Test.Toml.Codec.Combinator.Common (codecRoundtrip, exactCodecRoundtrip) import Toml.Codec (ByteStringAsBytes (..), TomlCodec, (.=)) import qualified Hedgehog.Gen as Gen@@ -23,8 +23,9 @@ smallTypeSpec :: Spec-smallTypeSpec = describe "SmallType: tests for custom data type" $+smallTypeSpec = describe "SmallType: tests for custom data type" $ do codecRoundtrip "SmallType" (Toml.table smallTypeCodec) genSmallType+ exactCodecRoundtrip "SmallType" (Toml.table smallTypeCodec) genSmallType data SmallType = SmallType { smallTypeInt :: !Int
test/Test/Toml/Gen.hs view
@@ -299,6 +299,13 @@ hex <- genDiffHex 8 pure . Text.pack $ "\\U" ++ hex +-- | Generates some unescaped unicode string+genUnicodeChar :: Gen Text+genUnicodeChar = Gen.element+ [ "č", "ć", "š", "đ", "ž", "Ö", "ё"+ , "в", "ь", "ж", "ю", "ч", "ü", "я"+ ]+ -- | Generates text from different symbols. genText :: Gen Text genText = genNotEscape $ fmap Text.concat $ Gen.list (Range.constant 0 256) $ Gen.choice@@ -307,7 +314,9 @@ , genPunctuation , genUniHex4Color , genUniHex8Color+ --, genUnicodeChar ]+ genString :: Gen String genString = Text.unpack <$> genText
test/Test/Toml/Parser/Integer.hs view
@@ -33,8 +33,10 @@ integerFailOn "_13" integerFailOn "_" it "does not parse numbers with leading zeros" $ do- parseInteger "0123" 0- parseInteger "-023" 0+ integerFailOn "0123"+ integerFailOn "00123"+ integerFailOn "-023"+ integerFailOn "-0023" context "when the integer is in binary representation" $ do it "can parse numbers prefixed with `0b`" $ do parseInteger "0b1101" 13
test/Test/Toml/Type/Printer.hs view
@@ -11,7 +11,7 @@ import Toml.Type.Edsl (empty, mkToml, table, tableArray, (=:)) import Toml.Type.Key (Key (..), (<|))-import Toml.Type.Printer (PrintOptions (..), defaultOptions, prettyOptions)+import Toml.Type.Printer (PrintOptions (..), Lines(..), defaultOptions, prettyOptions) import Toml.Type.TOML (TOML) import Toml.Type.Value (Value (..)) @@ -25,6 +25,7 @@ test "pretty_indented_only" noFormatting { printOptionsIndent = 4 } test "pretty_unformatted" noFormatting test "pretty_custom_sorted" noFormatting { printOptionsSorting = Just spamEgg }+ test "pretty_lines" defaultOptions { printOptionsLines = MultiLine } where test :: String -> PrintOptions -> SpecWith (Arg Expectation) test name options = it ("Golden " ++ name) $@@ -44,6 +45,7 @@ table "foo" empty table "doo" empty table "baz" empty+ "list" =: Array ["one", "two"] tableArray "deepest" $ "ping" =: "pong" :| [empty]@@ -55,6 +57,7 @@ noFormatting = PrintOptions { printOptionsSorting = Nothing , printOptionsIndent = 0+ , printOptionsLines = OneLine } -- | Decorate keys as tuples so spam comes before egg
test/golden/pretty_custom_sorted.golden view
@@ -9,6 +9,8 @@ [foo] +list = ["one", "two"]+ [qux.doo] spam = "!" egg = "?"
test/golden/pretty_default.golden view
@@ -5,6 +5,8 @@ [baz] +list = ["one", "two"]+ [doo] [foo]
test/golden/pretty_indented_only.golden view
@@ -9,6 +9,8 @@ [baz] +list = ["one", "two"]+ [qux.doo] egg = "?" spam = "!"
+ test/golden/pretty_lines.golden view
@@ -0,0 +1,31 @@+a = "a"+b = "bb"+c = "cccc"+d = "ddd"++[baz]++list =+ [ "one"+ , "two"+ ]++[doo]++[foo]++[qux.doo]+ egg = "?"+ spam = "!"++[[deeper]]+ green = true++[[deeper]]+ [deeper.blue]+ red = 255++[[deepest]]+ ping = "pong"++[[deepest]]
test/golden/pretty_sorted_only.golden view
@@ -5,6 +5,8 @@ [baz] +list = ["one", "two"]+ [doo] [foo]
test/golden/pretty_unformatted.golden view
@@ -9,6 +9,8 @@ [baz] +list = ["one", "two"]+ [qux.doo] egg = "?" spam = "!"
tomland.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 name: tomland-version: 1.3.1.0+version: 1.3.2.0 synopsis: Bidirectional TOML serialization description: Implementation of bidirectional TOML serialization. Simple codecs look like this:@@ -27,7 +27,7 @@ license-file: LICENSE author: Dmitrii Kovanikov, Veronika Romashkina maintainer: Kowainik <xrom.xkov@gmail.com>-copyright: 2018-2020 Kowainik+copyright: 2018-2021 Kowainik category: TOML, Text, Configuration build-type: Simple extra-doc-files: README.md@@ -37,7 +37,7 @@ tested-with: GHC == 8.4.4 GHC == 8.6.5 GHC == 8.8.4- GHC == 8.10.2+ GHC == 8.10.4 source-repository head type: git@@ -121,7 +121,7 @@ , hashable >= 1.2 && < 1.4 , megaparsec >= 7.0.5 && < 9.1 , mtl ^>= 2.2- , parser-combinators >= 1.1.0 && < 1.3+ , parser-combinators >= 1.1.0 && < 1.4 , text ^>= 1.2 , time >= 1.8 && < 1.11 , transformers ^>= 0.5