tomland 0.1.0 → 0.2.0
raw patch · 6 files changed
+184/−60 lines, 6 filessetup-changedPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Toml.Bi.Combinators: data EncodeException
- Toml.Bi.Combinators: instance GHC.Classes.Eq Toml.Bi.Combinators.EncodeException
- Toml.Bi.Combinators: instance GHC.Show.Show Toml.Bi.Combinators.EncodeException
- Toml.Bi.Combinators: unsafeDecode :: BiToml a -> a -> Text
- Toml.Bi.Monad: dimapBijection :: (Functor r, Functor w) => (c -> d) -> (a -> b) -> Bijection r w d a -> Bijection r w c b
+ Toml.Bi.Combinators: mdimap :: (Monad r, Monad w, MonadError DecodeException r) => (c -> d) -> (a -> Maybe b) -> Bijection r w d a -> Bijection r w c b
+ Toml.Bi.Combinators: table :: forall a. BiToml a -> Key -> BiToml a
+ Toml.Bi.Monad: dimap :: (Functor r, Functor w) => (c -> d) -> (a -> b) -> Bijection r w d a -> Bijection r w c b
- Toml.Bi.Combinators: decode :: BiToml a -> a -> Either DecodeException Text
+ Toml.Bi.Combinators: decode :: BiToml a -> Text -> Either DecodeException a
- Toml.Bi.Combinators: encode :: BiToml a -> Text -> Either EncodeException a
+ Toml.Bi.Combinators: encode :: BiToml a -> a -> Text
- Toml.Bi.Combinators: type Env = ExceptT EncodeException (Reader TOML)
+ Toml.Bi.Combinators: type Env = ExceptT DecodeException (Reader TOML)
- Toml.Bi.Combinators: type St = ExceptT DecodeException (State TOML)
+ Toml.Bi.Combinators: type St = State TOML
Files
- CHANGELOG.md +40/−0
- Setup.hs +2/−0
- examples/Playground.hs +11/−2
- src/Toml/Bi/Combinators.hs +83/−50
- src/Toml/Bi/Monad.hs +46/−7
- tomland.cabal +2/−1
+ CHANGELOG.md view
@@ -0,0 +1,40 @@+Change log+==========++tomland uses [PVP Versioning][1].+The change log is available [on GitHub][2].++0.2.0+=====+* Switch names for `decode` and `encode` functions.+* [#47](https://github.com/kowainik/tomland/issues/47):+ Rename `dimapBijection` to `dimap`. Introduce `mdimap` combinator.+* [#37](https://github.com/kowainik/tomland/issues/37):+ Add tables support for bidirectional conversion.++0.1.0+=====+* [#16](https://github.com/kowainik/tomland/issues/16):+ Add parser for literal strings.+* Add `IsString` instance for `Key` data type.+* [#38](https://github.com/kowainik/tomland/issues/38):+ Add bidirectional converter for array.+* [#21](https://github.com/kowainik/tomland/issues/21):+ Report expected vs. actual type error in parsing.+* [#44](https://github.com/kowainik/tomland/issues/44):+ Add bidirectional converter for `Maybe`.++0.0.0+=====+* [#3](https://github.com/kowainik/tomland/issues/3):+ Implement basic TOML parser with `megaparsec`.+* [#7](https://github.com/kowainik/tomland/issues/7):+ Implement type safe version of `Value` type as GADT.+* [#4](https://github.com/kowainik/tomland/issues/4):+ Implement basic pretty-printer.+* [#1](https://github.com/kowainik/tomland/issues/1):+ Implement types representing TOML configuration.+* Initially created.++[1]: https://pvp.haskell.org+[2]: https://github.com/kowainik/tomland/releases
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
examples/Playground.hs view
@@ -20,8 +20,15 @@ , testS :: Text , testA :: [Text] , testM :: Maybe Bool+ , testX :: TestInside+ , testY :: Maybe TestInside } +newtype TestInside = TestInside { unInside :: Text }++insideT :: BiToml TestInside+insideT = Toml.dimap unInside TestInside $ Toml.str "inside"+ testT :: BiToml Test testT = Test <$> Toml.bool "testB" .= testB@@ -30,6 +37,8 @@ <*> Toml.str "testS" .= testS <*> Toml.arrayOf Toml.strV "testA" .= testA <*> Toml.maybeP Toml.bool "testM" .= testM+ <*> Toml.table insideT "testX" .= testX+ <*> Toml.maybeP (Toml.table insideT) "testY" .= testY main :: IO () main = do@@ -44,9 +53,9 @@ TIO.putStrLn "=== Testing bidirectional conversion ===" biFile <- TIO.readFile "examples/biTest.toml"- case Toml.encode testT biFile of+ case Toml.decode testT biFile of Left msg -> print msg- Right test -> TIO.putStrLn $ Toml.unsafeDecode testT test+ Right test -> TIO.putStrLn $ Toml.encode testT test myToml :: TOML myToml = TOML (HashMap.fromList
src/Toml/Bi/Combinators.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE Rank2Types #-}@@ -13,17 +14,16 @@ , St -- * Exceptions- , EncodeException , DecodeException -- * Encode/Decode- , encode , decode- , unsafeDecode+ , encode -- * Converters , bijectionMaker , dimapNum+ , mdimap -- * Toml parsers , bool@@ -33,6 +33,7 @@ , str , arrayOf , maybeP+ , table -- * Value parsers , Valuer (..)@@ -43,70 +44,52 @@ , arrV ) where -import Control.Monad.Except (ExceptT, catchError, runExceptT, throwError)-import Control.Monad.Reader (Reader, asks, runReader)-import Control.Monad.State (State, gets, modify, runState)+import Control.Monad.Except (ExceptT, MonadError, catchError, runExceptT, throwError)+import Control.Monad.Reader (Reader, asks, local, runReader)+import Control.Monad.State (State, execState, gets, modify) import Data.Bifunctor (first)+import Data.Maybe (fromMaybe) import Data.Text (Text) -import Toml.Bi.Monad (Bi, Bijection (..), dimapBijection)-import Toml.Parser (ParseException, parse)+import Toml.Bi.Monad (Bi, Bijection (..), dimap)+import Toml.Parser (ParseException (..), parse) import Toml.PrefixTree (Key) import Toml.Printer (prettyToml) import Toml.Type (AnyValue (..), TOML (..), Value (..), ValueType (..), matchArray, matchBool, matchDouble, matchInteger, matchText) import qualified Data.HashMap.Strict as HashMap+import qualified Toml.PrefixTree as Prefix -- | Type of exception for converting from 'Toml' to user custom data type.-data EncodeException- = KeyNotFound Key -- ^ such key is not present in 'Toml'- | TypeMismatch Text -- ^ Expected type; TODO: add actual type+data DecodeException+ = KeyNotFound Key -- ^ No such key+ | TableNotFound Key -- ^ No such table+ | TypeMismatch Text -- ^ Expected type; TODO: add actual type | ParseError ParseException -- ^ Exception during parsing deriving (Eq, Show) -- TODO: manual pretty show instances -- | Immutable environment for 'Toml' conversion. -- This is @r@ type variable in 'Bijection' data type.-type Env = ExceptT EncodeException (Reader TOML)---- | Write exception for convertion to 'Toml' from user custom data type.-data DecodeException- = DuplicateKey Key AnyValue -- ^ Key is already in table for some value- deriving (Eq, Show) -- TODO: manual pretty show instances+type Env = ExceptT DecodeException (Reader TOML) -- | Mutable context for 'Toml' conversion. -- This is @w@ type variable in 'Bijection' data type.-type St = ExceptT DecodeException (State TOML)+type St = State TOML -- | Specialied 'Bi' type alias for 'Toml' monad. Keeps 'TOML' object either as -- environment or state. type BiToml a = Bi Env St a -- | Convert textual representation of toml into user data type.-encode :: BiToml a -> Text -> Either EncodeException a-encode biToml text = do+decode :: BiToml a -> Text -> Either DecodeException a+decode biToml text = do toml <- first ParseError (parse text) runReader (runExceptT $ biRead biToml) toml -- | Convert object to textual representation.-decode :: BiToml a -> a -> Either DecodeException Text-decode biToml obj = do- -- this pair has type (TOML, Either DecodeException a)- let (result, toml) = runState (runExceptT $ biWrite biToml obj) (TOML mempty mempty)-- -- just to trigger error if Left- _ <- result-- pure $ prettyToml toml--fromRight :: b -> Either a b -> b-fromRight b (Left _) = b-fromRight _ (Right b) = b---- | Unsafe version of 'decode' function if you're sure that you decoding--- of structure is correct.-unsafeDecode :: BiToml a -> a -> Text-unsafeDecode biToml text = fromRight (error "Unsafe decode") $ decode biToml text+encode :: BiToml a -> a -> Text+encode bi obj = prettyToml $ execState (biWrite bi obj) (TOML mempty mempty) ---------------------------------------------------------------------------- -- Generalized versions of parsers@@ -133,17 +116,51 @@ 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+ a <$ modify (\(TOML vals nested) -> TOML (HashMap.insert key val vals) nested) -- | Helper dimapper to turn 'integer' parser into parser for 'Int', 'Natural', 'Word', etc. dimapNum :: forall n r w . (Integral n, Functor r, Functor w) => Bi r w Integer -> Bi r w n-dimapNum = dimapBijection toInteger fromIntegral+dimapNum = dimap toInteger fromIntegral +{- | Almost same as 'dimap'. Useful when you want to have fields like this+inside your configuration:++@+data GhcVer = Ghc7103 | Ghc802 | Ghc822 | Ghc842++showGhcVer :: GhcVer -> Text+parseGhcVer :: Text -> Maybe GhcVer+@++When you specify couple of functions of the following types:++@+show :: a -> Text+parse :: Text -> Maybe a+@++they should satisfy property @parse . show == Just@ if you want to use your+converter for pretty-printing.+-}+mdimap :: (Monad r, Monad w, MonadError DecodeException r)+ => (c -> d) -- ^ Convert from safe to unsafe value+ -> (a -> Maybe b) -- ^ Parser for more type safe value+ -> Bijection r w d a -- ^ Source 'Bijection' object+ -> Bijection r w c b+mdimap toString toMaybe bi = Bijection+ { biRead = (toMaybe <$> biRead bi) >>= \case+ Nothing -> throwError $ ParseError $ ParseException "Can't parse" -- TODO+ Just b -> pure b++ , biWrite = \s -> do+ retS <- biWrite bi $ toString s+ case toMaybe retS of+ Nothing -> error $ "Given pair of functions for 'mdimap' doesn't satisfy roundtrip property"+ Just b -> pure b+ }+ ---------------------------------------------------------------------------- -- Value parsers ----------------------------------------------------------------------------@@ -219,10 +236,7 @@ output :: [a] -> St [a] output a = do let val = AnyValue $ Array $ map (valTo valuer) 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+ a <$ modify (\(TOML vals tables) -> TOML (HashMap.insert key val vals) tables) -- TODO: maybe conflicts from maybe in Prelude, maybe we should add C or P suffix or something else?... -- | Bidirectional converter for @Maybe smth@ values.@@ -234,6 +248,25 @@ Just v -> biWrite bi v >> pure (Just v) } where- handleNotFound :: EncodeException -> Env (Maybe a)- handleNotFound (KeyNotFound _) = pure Nothing- handleNotFound e = throwError e+ handleNotFound :: DecodeException -> Env (Maybe a)+ handleNotFound (KeyNotFound _) = pure Nothing+ handleNotFound (TableNotFound _) = pure Nothing+ handleNotFound e = throwError e++-- | Parser for tables. Use it when when you have nested objects.+table :: forall a . BiToml a -> Key -> BiToml a+table bi key = Bijection input output+ where+ input :: Env a+ input = do+ mTable <- asks $ Prefix.lookup key . tomlTables+ case mTable of+ Nothing -> throwError $ TableNotFound key+ Just toml -> local (const toml) (biRead bi)++ output :: a -> St a+ output a = do+ mTable <- gets $ Prefix.lookup key . tomlTables+ let toml = fromMaybe (TOML mempty mempty) mTable+ let newToml = execState (biWrite bi a) toml+ a <$ modify (\(TOML vals tables) -> TOML vals (Prefix.insert key newToml tables))
src/Toml/Bi/Monad.hs view
@@ -3,7 +3,7 @@ module Toml.Bi.Monad ( Bijection (..) , Bi- , dimapBijection+ , dimap , (.=) ) where @@ -71,16 +71,55 @@ @Profunctor@ type class in @base@ or package with no dependencies (and we don't want to bring extra dependencies) this instance is implemented as a single top-level function.++Useful when you want to parse @newtype@s. For example, if you had data type like this:++@+data Example = Example+ { foo :: Bool+ , bar :: Text+ }+@++toml bidirectional converter for this type will look like this:++@+exampleT :: BiToml Example+exampleT = Example+ <$> bool "foo" .= foo+ <*> str "bar" .= bar+@++Now if you change your time in the following way:++@+newtype Email = Email { unEmail :: Text }++data Example = Example+ { foo :: Bool+ , bar :: Email+ }+@++you need to patch your toml parser like this:++@+exampleT :: BiToml Example+exampleT = Example+ <$> bool "foo" .= foo+ <*> dimap unEmail Email (str "bar") .= bar+@ -}-dimapBijection :: (Functor r, Functor w)- => (c -> d) -- ^ Mapper for consumer- -> (a -> b) -- ^ Mapper for producer- -> Bijection r w d a -- ^ Source 'Bijection' object- -> Bijection r w c b-dimapBijection f g bi = Bijection+dimap :: (Functor r, Functor w)+ => (c -> d) -- ^ Mapper for consumer+ -> (a -> b) -- ^ Mapper for producer+ -> Bijection r w d a -- ^ Source 'Bijection' object+ -> Bijection r w c b+dimap f g bi = Bijection { biRead = g <$> biRead bi , biWrite = fmap g . biWrite bi . f }+ {- | Operator to connect two operations:
tomland.cabal view
@@ -1,5 +1,5 @@ name: tomland-version: 0.1.0+version: 0.2.0 description: TOML parser synopsis: TOML parser homepage: https://github.com/kowainik/tomland@@ -12,6 +12,7 @@ category: Data, Text, Configuration, TOML build-type: Simple extra-doc-files: README.md+ , CHANGELOG.md cabal-version: 1.24 tested-with: GHC == 8.2.2 , GHC == 8.0.2