typed-encoding 0.2.2.0 → 0.3.0.0
raw patch · 83 files changed
+3653/−2838 lines, 83 files
Files
- ChangeLog.md +31/−9
- README.md +4/−4
- src/Data/TypedEncoding.hs +83/−63
- src/Data/TypedEncoding/Combinators/Common.hs +52/−0
- src/Data/TypedEncoding/Combinators/Decode.hs +74/−0
- src/Data/TypedEncoding/Combinators/Encode.hs +68/−0
- src/Data/TypedEncoding/Combinators/Encode/Experimental.hs +45/−0
- src/Data/TypedEncoding/Combinators/Promotion.hs +101/−0
- src/Data/TypedEncoding/Combinators/Restriction/Bool.hs +0/−395
- src/Data/TypedEncoding/Combinators/Restriction/BoundedAlphaNums.hs +0/−112
- src/Data/TypedEncoding/Combinators/Restriction/Common.hs +0/−64
- src/Data/TypedEncoding/Combinators/ToEncStr.hs +50/−0
- src/Data/TypedEncoding/Combinators/Unsafe.hs +32/−0
- src/Data/TypedEncoding/Combinators/Validate.hs +88/−0
- src/Data/TypedEncoding/Common/Class.hs +63/−0
- src/Data/TypedEncoding/Common/Class/Decode.hs +58/−0
- src/Data/TypedEncoding/Common/Class/Encode.hs +44/−0
- src/Data/TypedEncoding/Common/Class/IsStringR.hs +72/−0
- src/Data/TypedEncoding/Common/Class/Superset.hs +89/−0
- src/Data/TypedEncoding/Common/Class/Util.hs +89/−0
- src/Data/TypedEncoding/Common/Class/Util/StringConstraints.hs +136/−0
- src/Data/TypedEncoding/Common/Class/Validate.hs +50/−0
- src/Data/TypedEncoding/Common/Types.hs +23/−0
- src/Data/TypedEncoding/Common/Types/CheckedEnc.hs +86/−0
- src/Data/TypedEncoding/Common/Types/Common.hs +68/−0
- src/Data/TypedEncoding/Common/Types/Decoding.hs +76/−0
- src/Data/TypedEncoding/Common/Types/Enc.hs +225/−0
- src/Data/TypedEncoding/Common/Types/Exceptions.hs +81/−0
- src/Data/TypedEncoding/Common/Types/SomeAnnotation.hs +34/−0
- src/Data/TypedEncoding/Common/Types/SomeEnc.hs +62/−0
- src/Data/TypedEncoding/Common/Types/UncheckedEnc.hs +53/−0
- src/Data/TypedEncoding/Common/Types/Unsafe.hs +29/−0
- src/Data/TypedEncoding/Common/Types/Validation.hs +81/−0
- src/Data/TypedEncoding/Common/Util/TypeLits.hs +145/−0
- src/Data/TypedEncoding/Conv/ByteString/Char8.hs +3/−4
- src/Data/TypedEncoding/Conv/ByteString/Lazy/Char8.hs +2/−3
- src/Data/TypedEncoding/Conv/Text.hs +11/−4
- src/Data/TypedEncoding/Conv/Text/Encoding.hs +5/−4
- src/Data/TypedEncoding/Instances/Do/Sample.hs +31/−23
- src/Data/TypedEncoding/Instances/Enc/Base64.hs +87/−112
- src/Data/TypedEncoding/Instances/Restriction/ASCII.hs +27/−136
- src/Data/TypedEncoding/Instances/Restriction/Bool.hs +429/−0
- src/Data/TypedEncoding/Instances/Restriction/BoundedAlphaNums.hs +122/−0
- src/Data/TypedEncoding/Instances/Restriction/Common.hs +0/−45
- src/Data/TypedEncoding/Instances/Restriction/Misc.hs +61/−0
- src/Data/TypedEncoding/Instances/Restriction/UTF8.hs +29/−77
- src/Data/TypedEncoding/Instances/Support.hs +21/−8
- src/Data/TypedEncoding/Instances/Support/Common.hs +36/−0
- src/Data/TypedEncoding/Instances/Support/Decode.hs +39/−0
- src/Data/TypedEncoding/Instances/Support/Encode.hs +63/−0
- src/Data/TypedEncoding/Instances/Support/Helpers.hs +112/−0
- src/Data/TypedEncoding/Instances/Support/Unsafe.hs +35/−0
- src/Data/TypedEncoding/Instances/Support/Validate.hs +60/−0
- src/Data/TypedEncoding/Instances/ToEncString/Common.hs +0/−44
- src/Data/TypedEncoding/Internal/Class.hs +0/−72
- src/Data/TypedEncoding/Internal/Class/Decode.hs +0/−81
- src/Data/TypedEncoding/Internal/Class/Encode.hs +0/−71
- src/Data/TypedEncoding/Internal/Class/Encoder.hs +0/−49
- src/Data/TypedEncoding/Internal/Class/IsStringR.hs +0/−73
- src/Data/TypedEncoding/Internal/Class/Recreate.hs +0/−90
- src/Data/TypedEncoding/Internal/Class/Superset.hs +0/−131
- src/Data/TypedEncoding/Internal/Class/Util.hs +0/−94
- src/Data/TypedEncoding/Internal/Class/Util/StringConstraints.hs +0/−134
- src/Data/TypedEncoding/Internal/Combinators.hs +0/−54
- src/Data/TypedEncoding/Internal/Instances/Combinators.hs +0/−107
- src/Data/TypedEncoding/Internal/Types.hs +0/−109
- src/Data/TypedEncoding/Internal/Types/CheckedEnc.hs +0/−85
- src/Data/TypedEncoding/Internal/Types/Common.hs +0/−4
- src/Data/TypedEncoding/Internal/Types/Enc.hs +0/−97
- src/Data/TypedEncoding/Internal/Types/SomeAnnotation.hs +0/−34
- src/Data/TypedEncoding/Internal/Types/SomeEnc.hs +0/−61
- src/Data/TypedEncoding/Internal/Types/UncheckedEnc.hs +0/−52
- src/Data/TypedEncoding/Internal/Types/Unsafe.hs +0/−29
- src/Data/TypedEncoding/Internal/Util/TypeLits.hs +0/−146
- src/Data/TypedEncoding/Unsafe.hs +2/−2
- src/Examples/TypedEncoding/Conversions.hs +71/−48
- src/Examples/TypedEncoding/DiySignEncoding.hs +15/−11
- src/Examples/TypedEncoding/Overview.hs +50/−34
- src/Examples/TypedEncoding/ToEncString.hs +24/−25
- src/Examples/TypedEncoding/Unsafe.hs +4/−4
- test/Test/Bc/ASCIISpec.hs +76/−0
- test/Test/Bc/IsStringRSpec.hs +6/−7
- typed-encoding.cabal +40/−27
ChangeLog.md view
@@ -2,17 +2,39 @@ ## Anticipated future breaking changes -- ByteString / Text conversion functions in `Data.TypedEncoding.Instances.Restriction.ASCII`, - `Data.TypedEncoding.Instances.Restriction.ASCII` and `Data.TypedEncoding.Instances.Enc.Base64`- are now deprecated and will be removed.-- `EncodeFAll`, `DecodeFAll`, `RecreateFAll`, `EncodeF`, etc do not work well with more open - encoding annotation such as `"r-ban:soething"` they will be either changed or deprecated / replaced with constructions similar to `Encoder` in `Data.TypedEncoding.Internal.Class.Encoder`. - `Data.TypedEncoding.Internal.Class.IsStringR` expected to be be changed / replaced-- functions used to create encoding instances or encoding combinators (e.g. `implEncodeP`) will get more constraints. -- (never ending) rework of internal module stucture to make it easier to navigate -- Instance and Combinator modules will be merged.-- Displ String instance (used in examples, will be made consistent with Text and ByteString) - (post 0.3) "enc-B64" will be moved to a different package (more distant goal)++## 0.3++- Breaking: Numerous changes on the implementation side, new version should be largely compatible on the call site except+ for small differences in constraints and order for type variables (if `-XTypeApplications` is used).+ See [v3 migration guide](https://github.com/rpeszek/typed-encoding/blob/master/doc/v3ConversionGuide.md).+ - `EncodeFAll`, `DecodeFAll`, `RecreateFAll`, `EncodeF`, `DecodeF`, `RecreateF` replaced with+ `EncodeAll`, `DecodeAll`, `ValidateAll`, `Encode`, `Decode`, `Validate`.+ - functions used to create encoding instances or encoding combinators (e.g. `implEncodeP`) are now more precisely typed+ - `Displ String` instance (used in examples, has been made consistent with Text and ByteString)+ - Modules under `Data.TypedEncoding.Combinators` merged into `Data.TypedEncoding.Instances`.+ - Modules under `Data.TypedEncoding.Internal` have been reorganized and moved outside of `Internal`. Various changes that make the library easier to navigate. + for better navigation and discovery.+ - some previously exported combinators (e.g. `implTranF`) have moved to `Data.TypedEncoding.Instances.Support.Unsafe` + - `ToEncString`, `FromEncString` have more type variables and function name but backward compatible functions+ have been provided. + - `Superset` typeclass removed, replaced with `IsSuperset` type family.+ - Minor changes in `forall` variable order in combinators for `"r-bool:"` encodings.+ - `Encoder` type removed, replaced by `Encodings`.+ - `checkWithValidationsEnc` combinator renamed to `check`+ - (Considered private) `MkCheckedEnc` constructor became `UnsafeMkCheckedEnc`+ - (Considered private) `MkEnc` constructor became `UnsafeMkEnc`+ +- new functionality+ - new types and typeclasses are based on both encoding name and algorithm name allowing+ typeclass definitions for open encodings like `"r-ban:"` that can contain arbitrary symbol literals. + - new set of combinators grouped into `_` (compiler decided algorithm), `'` (program specifies algorithm), and+ `algorithm name ~ encoding name` categories + - `above` combinator subsumes partial encoding / decoding combinators + - `EncodingSuperset` class added+ ## 0.2.2
README.md view
@@ -63,7 +63,6 @@ Please see `Examples.TypedEncoding` it the module list. - ## Other encoding packages My approach will be to write specific encodings (e.g. _HTTP_) or wrap encodings from other packages using separate "bridge" projects.@@ -76,15 +75,16 @@ - [typed-encoding-encoding](https://github.com/rpeszek/typed-encoding-encoding) bridges [encoding](https://github.com/dmwit/encoding) package -## Plans, some TODOs+## News -- lensifying conversions -- better implementation type safety+- v0.3 has numerous changes and improvements. ## Tested with+ - stack (1.9.3) lts-14.27 (ghc-8.6.5) - needs ghc >= 8.2.2, base >=4.10 for GHC.TypeLits support ## Known issues+ - running test suite: cabal has problems with doctest, use stack https://github.com/haskell/cabal/issues/6087
src/Data/TypedEncoding.hs view
@@ -1,5 +1,3 @@-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE DataKinds #-} -- | -- = Overview@@ -35,7 +33,7 @@ -- -- * /encoding/ -- * /decoding/--- * /recreation/ (verification of existing payload)+-- * /validation (recreation)/ (verification of existing payload) -- * type conversions between encoded types -- -- of string-like data (@ByteString@, @Text@) that is subject of some@@ -55,24 +53,24 @@ -- == "r-" restriction / predicate -- -- * /encoding/ is a partial identity--- * /recreation/ is a partial identity (matching encoding)+-- * /validation/ is a partial identity (matching encoding) -- * /decoding/ is identity ----- Examples: @"r-UTF8"@, @"r-ASCII"@, upper alpha-numeric bound /r-ban/ restrictions like @"r-999-999-9999"@+-- Examples: @"r-UTF8"@, @"r-ASCII"@, upper alpha-numeric bound /r-ban/ restrictions like @"r-ban:999-999-9999"@ -- -- == "do-" transformations -- -- * /encoding/ applies transformation to the string (could be partial) -- * /decoding/ - typically none--- * /recreation/ - typically none but, if present, verifies the payload has expected data (e.g. only uppercase chars for "do-UPPER")+-- * /validation/ - typically none but, if present, verifies the payload has expected data (e.g. only uppercase chars for "do-UPPER") -- -- Examples: @"do-UPPER"@, @"do-lower"@, @"do-reverse"@ -- -- == "enc-" data encoding that is not "r-" -- -- * /encoding/ applies encoding transformation to the string (could be partial)--- * /decoding/ reverses the transformation (can be used as pure function)--- * /recreation/ verifies that the payload has correctly encoded data+-- * /decoding/ reverses the transformation (can be now be used as pure function)+-- * /validation/ verifies that the payload has correctly encoded data -- -- Examples: @"enc-B64"@ -- @@ -84,39 +82,26 @@ -- -- Examples: ----- @"boolOr:(r-ban:FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF)(r-ban:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)"@ +-- @"boolOr:(r-ban:999-999-9999)(r-ban:(999) 999-9999)"@ -- -- "@boolNot:(r-ASCII)" -- ----- = Usage+-- = Call Site Usage -- -- To use this library import this module and one or more /instance/ or /combinator/ module. -- -- Here is list of instance modules available in typed-encoding library itself -- -- * "Data.TypedEncoding.Instances.Enc.Base64"--- * "Data.TypedEncoding.Instances.Restriction.Common" +-- * "Data.TypedEncoding.Instances.Restriction.Misc" (replaces @Common@ from v0.2) -- * "Data.TypedEncoding.Instances.Restriction.ASCII" -- * "Data.TypedEncoding.Instances.Restriction.UTF8" +-- * "Data.TypedEncoding.Instances.Restriction.Bool" (experimental / early alpha version, moved from @Combinators@ to @Instances@ in v0.3)+-- * "Data.TypedEncoding.Instances.Restriction.BoundedAlphaNums" (moved from @Combinators@ to @Instances@ in v0.3) -- * "Data.TypedEncoding.Instances.Do.Sample" --- * "Data.TypedEncoding.Instances.ToEncString.Common" -- --- This list is not intended to be exhaustive, rather separate libraries--- can provide instances for other encodings and transformations.------ To implement a new encoding import this module and------ * "Data.TypedEncoding.Instances.Support"------ Defining annotations with combinators is an alternative to using typeclass instances. ------ Combinator modules with be merged with Instances modules in the future.------ Included combinator modules:------ * "Data.TypedEncoding.Combinators.Restriction.Bool"--- * "Data.TypedEncoding.Combinators.Restriction.BoundedAlphaNums"+-- ... and needed conversions. -- -- Conversion combinator module structure is similar to one found in @text@ and @bytestring@ packages -- And can be found (since 0.2.2) in@@ -128,49 +113,84 @@ -- * "Data.TypedEncoding.Conv.ByteString.Char8" -- * "Data.TypedEncoding.Conv.ByteString.Lazy.Char8" --+-- This list is not intended to be exhaustive, rather separate libraries+-- can provide instances for other encodings and transformations. --+-- = New encoding instance creation+--+-- To implement a new encoding import+--+-- * "Data.TypedEncoding.Instances.Support"+-- -- = Examples -- -- Examples of how to use this library are included in -- -- * "Examples.TypedEncoding" module Data.TypedEncoding (- module Data.TypedEncoding- -- * Classes- , module Data.TypedEncoding.Internal.Class- -- * Encoding class and Encoder (replaces EncodeFAll)- , module Data.TypedEncoding.Internal.Class.Encoder- -- * Combinators- , module Data.TypedEncoding.Internal.Combinators- -- * Types- , Enc- , CheckedEnc- , EncodeEx(..)- , RecreateEx(..)- , UnexpectedDecodeEx(..)- , EncAnn - -- * Existentially quantified version of @Enc@ and basic combinators- , module Data.TypedEncoding.Internal.Types.SomeEnc- -- * Types and combinators for not verfied encoding - , module Data.TypedEncoding.Internal.Types.UncheckedEnc- -- * Basic @Enc@ Combinators- , getPayload - , unsafeSetPayload- , fromEncoding+ + -- * @Enc@ and basic combinators+ Enc , toEncoding- -- * Basic @CheckedEnc@ Combinators - , unsafeCheckedEnc- , getCheckedPayload- , getCheckedEncPayload- , toCheckedEnc- , fromCheckedEnc- -- * Other Basic Combinators - , recreateErrUnknown+ , fromEncoding+ , getPayload++ -- * Existentially quantified and untyped versions of @Enc@+ , module Data.TypedEncoding.Common.Types.SomeEnc+ , module Data.TypedEncoding.Common.Types.CheckedEnc++ -- * @Encoding@ and basic combinators+ , Encoding (..)+ , _mkEncoding+ , runEncoding'+ , _runEncoding + + -- * List of encodings+ , Encodings (..)+ , runEncodings'+ , _runEncodings++ -- * Similar to @Encoding@ and @Encodings@ but cover /Decoding/ and /Validation/+ , module Data.TypedEncoding.Common.Types.Decoding+ , module Data.TypedEncoding.Common.Types.Validation++ -- * @UncheckedEnc@ is an /untyped/ version of Enc that represents not validated encoding + , module Data.TypedEncoding.Common.Types.UncheckedEnc+ + -- * Classes+ , module Data.TypedEncoding.Common.Class+ + -- * Combinators+ , module Data.TypedEncoding.Combinators.Common+ , module Data.TypedEncoding.Combinators.Encode+ , module Data.TypedEncoding.Combinators.Decode+ , module Data.TypedEncoding.Combinators.Validate+ , module Data.TypedEncoding.Combinators.Unsafe+ , module Data.TypedEncoding.Combinators.ToEncStr+ , module Data.TypedEncoding.Combinators.Promotion++ -- * Exceptions + , module Data.TypedEncoding.Common.Types.Exceptions++ -- * Other+ , module Data.TypedEncoding.Common.Types.Common+ ) where -import Data.TypedEncoding.Internal.Types-import Data.TypedEncoding.Internal.Types.SomeEnc-import Data.TypedEncoding.Internal.Types.UncheckedEnc-import Data.TypedEncoding.Internal.Class-import Data.TypedEncoding.Internal.Combinators-import Data.TypedEncoding.Internal.Class.Encoder+import Data.TypedEncoding.Common.Types.Enc+import Data.TypedEncoding.Common.Types.Decoding+import Data.TypedEncoding.Common.Types.Validation++import Data.TypedEncoding.Common.Types.Common+import Data.TypedEncoding.Common.Types.CheckedEnc+import Data.TypedEncoding.Common.Types.SomeEnc+import Data.TypedEncoding.Common.Types.UncheckedEnc+import Data.TypedEncoding.Common.Types.Exceptions+import Data.TypedEncoding.Common.Class+import Data.TypedEncoding.Combinators.Common+import Data.TypedEncoding.Combinators.Encode+import Data.TypedEncoding.Combinators.Decode+import Data.TypedEncoding.Combinators.Validate+import Data.TypedEncoding.Combinators.Unsafe+import Data.TypedEncoding.Combinators.ToEncStr+import Data.TypedEncoding.Combinators.Promotion
+ src/Data/TypedEncoding/Combinators/Common.hs view
@@ -0,0 +1,52 @@++{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE DataKinds #-}+-- {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE FlexibleContexts #-}++-- | Combinators reexported in Data.TypedEncoding+module Data.TypedEncoding.Combinators.Common where++import Data.TypedEncoding.Common.Types+import Data.TypedEncoding.Combinators.Unsafe+import Data.TypedEncoding.Common.Class.Util (Append)+import GHC.TypeLits+import Data.Proxy++-- $setup+-- >>> :set -XTypeApplications+-- >>> import qualified Data.Text as T+-- >>> import Data.Word+++-- * Partial application of encoding / decoding / recreation++-- | Any valid transformation of encodings (encoding / decoding / recreation) can be +-- replayed on top of another encoding stack. +--+-- This subsumes various /encodePart, decodePart, recreatePart/ combinators.+aboveF :: forall (ts :: [Symbol]) xs ys f c str . (Functor f) =>+ (Enc xs c str -> f (Enc ys c str)) + -> Enc (Append xs ts) c str -> f (Enc (Append ys ts) c str)+aboveF fn (UnsafeMkEnc _ conf str) = + let re :: f (Enc ys c str) = fn $ UnsafeMkEnc Proxy conf str+ in UnsafeMkEnc Proxy conf . getPayload <$> re+++above :: forall (ts :: [Symbol]) xs ys c str . + (Enc xs c str -> Enc ys c str) + -> Enc (Append xs ts) c str -> Enc (Append ys ts) c str+above fn (UnsafeMkEnc _ conf str) = + let re ::Enc ys c str = fn $ UnsafeMkEnc Proxy conf str+ in UnsafeMkEnc Proxy conf . getPayload $ re+++-- * Other ++getTransformF :: forall e1 e2 f c s1 s2 . Functor f => (Enc e1 c s1 -> f (Enc e2 c s2)) -> c -> s1 -> f s2+getTransformF fn c str = getPayload <$> fn (unsafeSetPayload c str)++
+ src/Data/TypedEncoding/Combinators/Decode.hs view
@@ -0,0 +1,74 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE TypeApplications #-}++-- | Combinators reexported in Data.TypedEncoding+module Data.TypedEncoding.Combinators.Decode where++import Data.TypedEncoding.Common.Types.Enc+import Data.TypedEncoding.Common.Types.Decoding+import Data.TypedEncoding.Combinators.Common++import Data.TypedEncoding.Common.Class.Util+import Data.TypedEncoding.Common.Class.Decode+import Data.Functor.Identity+import GHC.TypeLits+++-- * Convenience combinators which mimic pre-v0.3 type signatures. These assume that @algs@ are the same as @nms@++decodeF :: forall nm xs f c str . (Decode f nm nm c str) => Enc (nm ': xs) c str -> f (Enc xs c str)+decodeF = decodeF' @nm @nm++decodeFAll :: forall nms f c str . (Monad f, DecodeAll f nms nms c str) => + Enc nms c str+ -> f (Enc ('[]::[Symbol]) c str) +decodeFAll = decodeFAll' @nms @nms ++-- | +-- +decodeAll :: forall nms c str . (DecodeAll Identity nms nms c str) =>+ Enc nms c str + -> Enc ('[]::[Symbol]) c str +decodeAll = decodeAll' @nms @nms ++decodeFPart :: forall xs xsf f c str . (Monad f, DecodeAll f xs xs c str) => Enc (Append xs xsf) c str -> f (Enc xsf c str)+decodeFPart = decodeFPart' @xs @xs ++decodePart :: forall xs xsf c str . (DecodeAll Identity xs xs c str) => Enc (Append xs xsf) c str -> Enc xsf c str +decodePart = decodePart' @xs @xs ++++-- * Convenience combinators which mimic pre-v0.3 type signatures. These do not try to figure out @algs@ or assume much about them++decodeF' :: forall alg nm xs f c str . (Decode f nm alg c str) => Enc (nm ': xs) c str -> f (Enc xs c str)+decodeF' = runDecoding (decoding @f @nm @alg)++decodeFAll' :: forall algs nms f c str . (Monad f, DecodeAll f nms algs c str) => + Enc nms c str+ -> f (Enc ('[]::[Symbol]) c str) +decodeFAll' = runDecodings @algs @nms @f decodings ++-- | +-- +decodeAll' :: forall algs nms c str . (DecodeAll Identity nms algs c str) =>+ Enc nms c str + -> Enc ('[]::[Symbol]) c str +decodeAll' = runIdentity . decodeFAll' @algs ++decodeFPart' :: forall algs xs xsf f c str . (Monad f, DecodeAll f xs algs c str) => Enc (Append xs xsf) c str -> f (Enc xsf c str)+decodeFPart' = aboveF @xsf @xs @'[] (decodeFAll' @algs) +-- decodeFPart' (UnsafeMkEnc _ conf str) = +-- let re :: f (Enc '[] c str) = decodeFAll' @algs @xs $ UnsafeMkEnc Proxy conf str+-- in UnsafeMkEnc Proxy conf . getPayload <$> re++decodePart' :: forall algs xs xsf c str . (DecodeAll Identity xs algs c str) => Enc (Append xs xsf) c str -> Enc xsf c str +decodePart' = runIdentity . decodeFPart' @algs @xs++
+ src/Data/TypedEncoding/Combinators/Encode.hs view
@@ -0,0 +1,68 @@++{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE TypeApplications #-}++module Data.TypedEncoding.Combinators.Encode where++import Data.TypedEncoding.Common.Types.Enc+import Data.TypedEncoding.Common.Class.Util -- Append+import Data.TypedEncoding.Combinators.Common+import Data.TypedEncoding.Common.Class.Encode+import GHC.TypeLits+import Data.Functor.Identity+++-- * Convenience combinators which mimic pre-v0.3 type signatures. These assume that @algs@ are the same as @nms@++encodeF :: forall nm xs f c str . Encode f nm nm c str => Enc xs c str -> f (Enc (nm ': xs) c str)+encodeF = encodeF' @nm @nm++encodeFAll :: forall nms f c str . (Monad f, EncodeAll f nms nms c str) => + Enc ('[]::[Symbol]) c str + -> f (Enc nms c str) +encodeFAll = encodeFAll' @nms @nms++encodeAll :: forall nms c str . (EncodeAll Identity nms nms c str) =>+ Enc ('[]::[Symbol]) c str + -> Enc nms c str +encodeAll = encodeAll' @nms @nms ++encodeFPart :: forall xs xsf f c str . (Monad f, EncodeAll f xs xs c str) => Enc xsf c str -> f (Enc (Append xs xsf) c str)+encodeFPart = encodeFPart' @xs @xs++encodePart :: forall xs xsf c str . (EncodeAll Identity xs xs c str) => Enc xsf c str -> Enc (Append xs xsf) c str +encodePart = encodePart' @xs @xs+++-- * Convenience combinators which mimic pre-v0.3 type signatures. These do not try to figure out @algs@ or assume much about them++encodeF' :: forall alg nm xs f c str . (Encode f nm alg c str) => Enc xs c str -> f (Enc (nm ': xs) c str)+encodeF' = runEncoding' (encoding @f @nm @alg)++encodeFAll' :: forall algs nms f c str . (Monad f, EncodeAll f nms algs c str) => + Enc ('[]::[Symbol]) c str + -> f (Enc nms c str) +encodeFAll' = runEncodings' @algs @nms @f encodings ++-- | +-- +encodeAll' :: forall algs nms c str . (EncodeAll Identity nms algs c str) =>+ Enc ('[]::[Symbol]) c str + -> Enc nms c str +encodeAll' = runIdentity . encodeFAll' @algs ++encodeFPart' :: forall algs xs xsf f c str . (Monad f, EncodeAll f xs algs c str) => Enc xsf c str -> f (Enc (Append xs xsf) c str)+encodeFPart' = aboveF @xsf @'[] @xs (encodeFAll' @algs) +-- encodeFPart' (UnsafeMkEnc _ conf str) = +-- let re :: f (Enc xs c str) = encodeFAll' @algs $ UnsafeMkEnc Proxy conf str+-- in UnsafeMkEnc Proxy conf . getPayload <$> re++encodePart' :: forall algs xs xsf c str . (EncodeAll Identity xs algs c str) => Enc xsf c str -> Enc (Append xs xsf) c str +encodePart' = runIdentity . encodeFPart' @algs @xs
+ src/Data/TypedEncoding/Combinators/Encode/Experimental.hs view
@@ -0,0 +1,45 @@+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}++-- |+-- Experimental features, slow to compile when used.+module Data.TypedEncoding.Combinators.Encode.Experimental where++import Data.TypedEncoding.Combinators.Encode+import Data.TypedEncoding.Common.Types.Enc+import Data.TypedEncoding.Common.Types.Common+import Data.TypedEncoding.Common.Class.Util -- Append+import Data.TypedEncoding.Common.Class.Encode +import Data.Functor.Identity+import GHC.TypeLits++-- * Combinators equivalent to "Data.TypedEncoding.Common.Class.Encode" that automatically figure out algorithm name.+-- Cause slow compilation when used++_encodeF :: forall nm xs f c str alg . (Encode f nm alg c str, alg ~ AlgNm nm) => Enc xs c str -> f (Enc (nm ': xs) c str)+_encodeF = encodeF' @(AlgNm nm) @nm++_encodeFAll :: forall nms f c str algs . (Monad f, EncodeAll f nms algs c str, algs ~ AlgNmMap nms) => + Enc ('[]::[Symbol]) c str + -> f (Enc nms c str) +_encodeFAll = encodeFAll' @(AlgNmMap nms) @nms++_encodeAll :: forall nms c str algs . (EncodeAll Identity nms algs c str, algs ~ AlgNmMap nms) =>+ Enc ('[]::[Symbol]) c str + -> Enc nms c str +_encodeAll = encodeAll' @(AlgNmMap nms) @nms ++_encodeFPart :: forall xs xsf f c str algs . (Monad f, EncodeAll f xs algs c str, algs ~ AlgNmMap xs) => Enc xsf c str -> f (Enc (Append xs xsf) c str)+_encodeFPart = encodeFPart' @(AlgNmMap xs) @xs++_encodePart :: forall xs xsf c str algs . (EncodeAll Identity xs algs c str, algs ~ AlgNmMap xs) => Enc xsf c str -> Enc (Append xs xsf) c str +_encodePart = encodePart' @(AlgNmMap xs) @xs+
+ src/Data/TypedEncoding/Combinators/Promotion.hs view
@@ -0,0 +1,101 @@+++{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+-- {-# LANGUAGE TypeApplications #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE AllowAmbiguousTypes #-}++-- | Promote and demote combinators.+module Data.TypedEncoding.Combinators.Promotion where++import Data.TypedEncoding.Common.Class+import Data.TypedEncoding.Common.Util.TypeLits++import Data.TypedEncoding.Common.Types (Enc(..) )+import Data.TypedEncoding.Combinators.Unsafe (withUnsafeCoerce)+++-- $setup+-- >>> :set -XOverloadedStrings -XMultiParamTypeClasses -XDataKinds -XTypeApplications+-- >>> import Data.TypedEncoding.Common.Class.Util (displ)+-- >>> import Data.TypedEncoding.Combinators.Unsafe (unsafeSetPayload)+-- >>> import Data.TypedEncoding.Instances.Restriction.UTF8 ()+-- >>> import Data.TypedEncoding.Instances.Restriction.ASCII ()+-- >>> import Data.Text as T++-- | Remove redundant superset right after the top (at second last encoding position)+--+-- >>> displ $ demoteFlattenTop (unsafeSetPayload () "" :: Enc '["r-ASCII", "r-UTF8", "r-boo"] () T.Text)+-- "Enc '[r-ASCII,r-boo] () (Text )"+--+-- @since 0.2.2.0 (moved)+demoteFlattenTop :: forall y x xs c str . (IsSuperset y x ~ 'True) => Enc (x ': y ': xs) c str -> Enc (x ': xs) c str+demoteFlattenTop = withUnsafeCoerce id++-- | add redundant superset right after+--+-- >>> displ $ promoteUnFlattenTop @"r-UTF8" (unsafeSetPayload () "" :: Enc '["r-ASCII", "r-boo"] () T.Text)+-- "Enc '[r-ASCII,r-UTF8,r-boo] () (Text )"+--+-- @since 0.2.2.0 (moved)+promoteUnFlattenTop :: forall y x xs c str . (IsSuperset y x ~ 'True) => Enc (x ': xs) c str -> Enc (x ': y ': xs) c str+promoteUnFlattenTop = withUnsafeCoerce id++-- | remove redunant superset from the top (at last applied encoding position)+--+-- >>> displ $ demoteRemoveTop (unsafeSetPayload () "" :: Enc '["r-UTF8", "r-ASCII", "r-boo"] () T.Text)+-- "Enc '[r-ASCII,r-boo] () (Text )"+--+-- @since 0.2.2.0 (moved)+demoteRemoveTop :: forall y x xs c str . (IsSuperset y x ~ 'True) => Enc (y ': x ' : xs) c str -> Enc (x ': xs) c str+demoteRemoveTop = withUnsafeCoerce id++-- | add redundant superset at the top+--+-- >>> displ $ promoteAddTop @"r-UTF8" (unsafeSetPayload () "" :: Enc '["r-ASCII", "r-boo"] () T.Text)+-- "Enc '[r-UTF8,r-ASCII,r-boo] () (Text )"+--+-- @since 0.2.2.0 (moved)+promoteAddTop :: forall y x xs c str . (IsSuperset y x ~ 'True) => Enc (x ': xs) c str -> Enc (y ': x ' : xs) c str +promoteAddTop = withUnsafeCoerce id++-- | remove redundant superset at bottom (first encoding) position+--+-- >>> displ $ demoteRemoveBot (unsafeSetPayload () "" :: Enc '["r-boo", "r-ASCII", "r-UTF8"] () T.Text)+-- "Enc '[r-boo,r-ASCII] () (Text )"+--+-- @since 0.2.2.0 (moved)+demoteRemoveBot :: (UnSnoc xs ~ '(,) ys y, UnSnoc ys ~ '(,) zs x, IsSuperset y x ~ 'True) => Enc xs c str -> Enc ys c str+demoteRemoveBot = withUnsafeCoerce id++-- | add redundant superset at bottom (first encoding) position+--+-- >>> displ $ promoteAddBot @"r-UTF8" (unsafeSetPayload () "" :: Enc '["r-boo", "r-ASCII"] () T.Text)+-- "Enc '[r-boo,r-ASCII,r-UTF8] () (Text )"+promoteAddBot :: forall y x xs c str ys . (UnSnoc xs ~ '(,) ys x, IsSuperset y x ~ 'True) => Enc xs c str -> Enc (Snoc xs y) c str+promoteAddBot = withUnsafeCoerce id++-- | remove redundant superset at second bottom (second encoding) position+--+-- >>> displ $ demoteFlattenBot (unsafeSetPayload () "" :: Enc '["r-boo", "r-UTF8", "r-ASCII"] () T.Text)+-- "Enc '[r-boo,r-ASCII] () (Text )"+--+-- @since 0.2.2.0 (moved)+demoteFlattenBot :: (UnSnoc xs ~ '(,) ys x, UnSnoc ys ~ '(,) zs y, IsSuperset y x ~ 'True) => Enc xs c str -> Enc (Snoc zs x) c str+demoteFlattenBot = withUnsafeCoerce id++-- | add redundant superset at second bottom (second encoding) position+--+-- >>> displ $ promoteUnFlattenBot @"r-UTF8" (unsafeSetPayload () "" :: Enc '["r-boo", "r-ASCII"] () T.Text)+-- "Enc '[r-boo,r-UTF8,r-ASCII] () (Text )"+--+-- @since 0.2.2.0 (moved)+promoteUnFlattenBot :: forall y x xs c str ys . (UnSnoc xs ~ '(,) ys x, IsSuperset y x ~ 'True) => Enc xs c str -> Enc (Snoc (Snoc ys y) x) c str+promoteUnFlattenBot = withUnsafeCoerce id
− src/Data/TypedEncoding/Combinators/Restriction/Bool.hs
@@ -1,395 +0,0 @@--{-# LANGUAGE DataKinds #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE UndecidableInstances #-}---- |--- Boolean algebra on encodings------ @since 0.2.1.0--- --- == Grammar--- --- Simple grammar requires boolean terms to be included in parentheses------ @--- bool[BinaryOp]:(leftTerm)(rightTerm)--- bool[UnaryOp]:(term)--- @------ Expected behavior is described next to corresponding combinator.------ Typeclass encoding is not used to avoid instance overlapping.--- --- Use 'Data.TypedEncoding.Combinators.Restriction.Common.recWithEncR' --- to create manual recovery step that can be combined with 'recreateFPart'.---- This is very much in beta state.----module Data.TypedEncoding.Combinators.Restriction.Bool where ---import GHC.TypeLits-import Data.Proxy-import Data.Symbol.Ascii--import Data.TypedEncoding-import Data.TypedEncoding.Instances.Support--- import Data.TypedEncoding.Internal.Util.TypeLits-import Data.TypedEncoding.Combinators.Restriction.Common---- import qualified Data.Text as T--- import Data.TypedEncoding.Instances.Restriction.Common()----- $setup--- >>> :set -XOverloadedStrings -XMultiParamTypeClasses -XDataKinds -XTypeApplications--- >>> import qualified Data.Text as T--- >>> import Data.TypedEncoding.Instances.Restriction.Common()----- |--- See examples in 'encBoolOrRight''-encBoolOrLeft :: forall f s t xs c str . (- BoolOpIs s "or" ~ 'True- -- IsBoolOr s ~ 'True- , Functor f- , LeftTerm s ~ t- ) => (Enc xs c str -> f (Enc (t ': xs) c str)) -> Enc xs c str -> f (Enc (s ': xs) c str) -encBoolOrLeft = implChangeAnn ---- |--- See examples in 'encBoolOrRight''-encBoolOrLeft' :: forall f s t xs c str . (- BoolOpIs s "or" ~ 'True- -- IsBoolOr s ~ 'True- , Functor f- , LeftTerm s ~ t- , EncodeF f (Enc xs c str) (Enc (t ': xs) c str) - ) => Enc xs c str -> f (Enc (s ': xs) c str) -encBoolOrLeft' = encBoolOrLeft (encodeF @f @(Enc xs c str) @(Enc (t ': xs) c str)) ---- |--- -encBoolOrRight :: forall f s t xs c str . (- BoolOpIs s "or" ~ 'True- -- IsBoolOr s ~ 'True- , Functor f- , RightTerm s ~ t- ) => (Enc xs c str -> f (Enc (t ': xs) c str)) -> Enc xs c str -> f (Enc (s ': xs) c str) -encBoolOrRight = implChangeAnn ---- |--- >>> :{ --- let tst1, tst2, tst3 :: Either EncodeEx (Enc '["boolOr:(r-Word8-decimal)(r-Int-decimal)"] () T.Text)--- tst1 = encBoolOrLeft' . toEncoding () $ "212" --- tst2 = encBoolOrRight' . toEncoding () $ "1000000" --- tst3 = encBoolOrLeft' . toEncoding () $ "1000000"--- :}--- --- >>> tst1 --- Right (MkEnc Proxy () "212")------ >>> tst2--- Right (MkEnc Proxy () "1000000")------ >>> tst3--- Left (EncodeEx "r-Word8-decimal" ("Payload does not satisfy format Word8-decimal: 1000000"))-encBoolOrRight' :: forall f s t xs c str . (- BoolOpIs s "or" ~ 'True- -- IsBoolOr s ~ 'True- , Functor f- , RightTerm s ~ t- , EncodeF f (Enc xs c str) (Enc (t ': xs) c str) - ) => Enc xs c str -> f (Enc (s ': xs) c str) -encBoolOrRight' = encBoolOrRight (encodeF @f @(Enc xs c str) @(Enc (t ': xs) c str)) --encBoolAnd :: forall f s t1 t2 xs c str . (- BoolOpIs s "and" ~ 'True - , KnownSymbol s- -- IsBoolAnd s ~ 'True- , f ~ Either EncodeEx- , Eq str- , LeftTerm s ~ t1- , RightTerm s ~ t2- ) => - (Enc xs c str -> f (Enc (t1 ': xs) c str)) - -> (Enc xs c str -> f (Enc (t2 : xs) c str)) - -> Enc xs c str -> f (Enc (s ': xs) c str) -encBoolAnd fnl fnr en0 = - let - eent1 = fnl en0- eent2 = fnr en0- p = (Proxy :: Proxy s)- in - case (eent1, eent2) of- (Right ent1, Right ent2) -> - if getPayload ent1 == getPayload ent2- then Right . withUnsafeCoerce id $ ent1 - else Left $ EncodeEx p "Left - right encoding do not match" - (_, _) -> mergeErrs (emptyEncErr p) (mergeEncodeEx p) eent1 eent2---- |--- @"boolOr:(enc1)(enc2)"@ contains strings that encode the same way under both encodings.--- for example @"boolOr:(r-UPPER)(r-lower)"@ valid elements would include @"123-34"@ but not @"abc"@------ >>> :{--- let tst1, tst2 :: Either EncodeEx (Enc '["boolAnd:(r-Word8-decimal)(r-Int-decimal)"] () T.Text)--- tst1 = encBoolAnd' . toEncoding () $ "234"--- tst2 = encBoolAnd' . toEncoding () $ "100000"--- :}--- --- >>> tst1--- Right (MkEnc Proxy () "234")--- >>> tst2--- Left (EncodeEx "r-Word8-decimal" ("Payload does not satisfy format Word8-decimal: 100000"))-encBoolAnd' :: forall f s t1 t2 xs c str . (- BoolOpIs s "and" ~ 'True - , KnownSymbol s- -- IsBoolAnd s ~ 'True- , f ~ Either EncodeEx- , Eq str- , LeftTerm s ~ t1- , RightTerm s ~ t2- , EncodeF f (Enc xs c str) (Enc (t1 ': xs) c str) - , EncodeF f (Enc xs c str) (Enc (t2 ': xs) c str) - ) => Enc xs c str -> f (Enc (s ': xs) c str) -encBoolAnd' = encBoolAnd (encodeF @f @(Enc xs c str) @(Enc (t1 ': xs) c str)) (encodeF @f @(Enc xs c str) @(Enc (t2 ': xs) c str)) ----- tst1, tst2 :: Either EncodeEx (Enc '["boolNot:(r-Word8-decimal)"] () T.Text)--- tst1 = encBoolNot' . toEncoding () $ "334"--- tst2 = encBoolNot' . toEncoding () $ "127"--encBoolNot :: forall f s t xs c str . (- BoolOpIs s "not" ~ 'True- , KnownSymbol s- , f ~ Either EncodeEx- , FirstTerm s ~ t- , KnownSymbol t- , IsR t ~ 'True- ) => (Enc xs c str -> f (Enc (t ': xs) c str)) -> Enc xs c str -> f (Enc (s ': xs) c str) -encBoolNot fn en0 = - let - een = fn en0- p = (Proxy :: Proxy s)- pt = (Proxy :: Proxy t)- in - case een of- Left _ -> Right . withUnsafeCoerce id $ en0- Right _ -> Left $ EncodeEx p $ "Encoding " ++ symbolVal pt ++ " succeeded" - ---- |--- >>> :{--- let tst1, tst2 :: Either EncodeEx (Enc '["boolNot:(r-Word8-decimal)"] () T.Text)--- tst1 = encBoolNot' . toEncoding () $ "334"--- tst2 = encBoolNot' . toEncoding () $ "127"--- :}------ >>> tst1--- Right (MkEnc Proxy () "334")--- >>> tst2--- Left (EncodeEx "boolNot:(r-Word8-decimal)" ("Encoding r-Word8-decimal succeeded"))-encBoolNot' :: forall f s t xs c str . (- BoolOpIs s "not" ~ 'True- , KnownSymbol s- , f ~ Either EncodeEx- , FirstTerm s ~ t- , KnownSymbol t- , IsR t ~ 'True- , EncodeF f (Enc xs c str) (Enc (t ': xs) c str) - ) => Enc xs c str -> f (Enc (s ': xs) c str) -encBoolNot' = encBoolNot (encodeF :: Enc xs c str -> f (Enc (t ': xs) c str))---- | --- Decodes boolean expression if all leaves are @"r-"@-decBoolR :: forall f xs t s c str . (- NestedR s ~ 'True- , Applicative f- ) => Enc (s ': xs) c str -> f (Enc xs c str) --decBoolR = implTranP id --recWithEncBoolR :: forall (s :: Symbol) xs c str . (NestedR s ~ 'True) - => (Enc xs c str -> Either EncodeEx (Enc (s ': xs) c str)) - -> Enc xs c str -> Either RecreateEx (Enc (s ': xs) c str)-recWithEncBoolR = unsafeRecWithEncR---- * Type family based parser ---- |--- >>> :kind! BoolOpIs "boolAnd:(someenc)(otherenc)" "and"--- ...--- = 'True-type family BoolOpIs (s :: Symbol) (op :: Symbol) :: Bool where- BoolOpIs s op = AcceptEq ('Text "Invalid bool encoding " ':<>: ShowType s ) (CmpSymbol (BoolOp s) op)---- | --- This works fast with @!kind@ but is much slower in declaration --- :kind! BoolOp "boolOr:()()"-type family BoolOp (s :: Symbol) :: Symbol where- BoolOp s = Fst (BoolOpHelper (Dupl s))- -- ToLower (TakeUntil (Drop 4 s) ":")--type family BoolOpHelper (x :: (Symbol, Symbol)) :: (Symbol, Bool) where- BoolOpHelper ('(,) s1 s2) = '(,) (ToLower (TakeUntil (Drop 4 s1) ":")) ( AcceptEq ('Text "Invalid bool encoding " ':<>: ShowType s2 ) (CmpSymbol "bool" (Take 4 s2)))---type family IsBool (s :: Symbol) :: Bool where- IsBool s = AcceptEq ('Text "Not boolean encoding " ':<>: ShowType s ) (CmpSymbol "bool" (Take 4 s))---- |--- --- >>> :kind! NestedR "boolOr:(r-abc)(r-cd)"--- ...--- = 'True------ >>> :kind! NestedR "boolOr:(boolAnd:(r-ab)(r-ac))(boolNot:(r-cd))"--- ...--- = 'True------ >>> :kind! NestedR "boolOr:(boolAnd:(r-ab)(ac))(boolNot:(r-cd))"--- ...--- ... (TypeError ...)--- ...-type family NestedR (s :: Symbol) :: Bool where- NestedR "" = 'True -- RightTerm, LeftTerm return "" on "r-"- NestedR s = Or (IsROrEmpty s) - (And (IsBool s) (And (NestedR (LeftTerm s)) (NestedR (RightTerm s)))) ---- Value level check------ nestedR :: String -> Bool--- nestedR s = isROrEmpty s || isBool s && (nestedR (leftTerm s) && nestedR (rightTerm s))---- isBool ('b' :'o' : 'o' : _) = True--- isBool _ = False---- isROrEmpty "" = True--- isROrEmpty ('r' : '-' : _) = True--- isROrEmpty _ = False---type family FirstTerm (s :: Symbol) :: Symbol where- FirstTerm s = LeftTerm s---- | --- returns "" for unary operator-type family SecondTerm (s :: Symbol) :: Symbol where- SecondTerm s = RightTerm s----- |--- >>> :kind! LeftTerm "boolSomeOp:(agag)(222)"--- ...--- = "agag"------ >>> :kind! LeftTerm "r-Int-decimal"--- ...--- = ""-type family LeftTerm (s :: Symbol) :: Symbol where- LeftTerm s = Concat (LDropLast( LTakeFstParen (LParenCnt (ToList s))))---- |--- >>> :kind! RightTerm "boolSomeOp:(agag)(222)"--- ...--- = "222"------ >>> :kind! RightTerm "r-Int-decimal"--- ...--- = ""-type family RightTerm (s :: Symbol) :: Symbol where- RightTerm s = Concat (LDropLast (LTakeSndParen 0 (LParenCnt (ToList s))))---type family LDropLast (s :: [Symbol]) :: [Symbol] where- LDropLast '[] = '[]- LDropLast '[x] = '[]- LDropLast (x ': xs) = x ': LDropLast xs---type family LParenCnt (s :: [Symbol]) :: [(Symbol, Nat)] where- LParenCnt '[] = '[]- LParenCnt ("(" ': xs) = LParenCntHelper ('(,) "(" 'Decr) (LParenCnt xs) -- '(,) "(" (LParenCntFstCnt (LParenCnt xs) - 1) ': LParenCnt xs- LParenCnt (")" ': xs) = LParenCntHelper ('(,) ")" 'Incr) (LParenCnt xs)- LParenCnt (x ': xs) = LParenCntHelper ('(,) x 'NoChng) (LParenCnt xs)--data Adjust = Incr | Decr | NoChng--type family AdjHelper (a :: Adjust) (n :: Nat) :: Nat where- AdjHelper 'Incr n = n + 1- AdjHelper 'Decr 0 = 0- AdjHelper 'Decr n = n - 1- AdjHelper 'NoChng n = n--type family LParenCntHelper (s :: (Symbol, Adjust)) (sx :: [(Symbol, Nat)]) :: [(Symbol, Nat)] where- LParenCntHelper ('(,) x k) '[] = '(,) x (AdjHelper k 0) ': '[]- LParenCntHelper ('(,) x k) ('(,) c i : xs) = '(,) x (AdjHelper k i) ': '(,) c i ': xs---type family LTakeFstParen (si :: [(Symbol, Nat)]) :: [Symbol] where - LTakeFstParen '[] = '[]- LTakeFstParen ('(,) _ 0 ': xs) = LTakeFstParen xs- LTakeFstParen ('(,) ")" 1 ': _) = '[")"]- LTakeFstParen ('(,) a p ': xs) = a ': LTakeFstParen xs---type family LTakeSndParen (n :: Nat) (si :: [(Symbol, Nat)]) :: [Symbol] where- LTakeSndParen _ '[] = '[]- LTakeSndParen 0 ('(,) ")" 1 ': xs) = LTakeSndParen 1 xs- LTakeSndParen 1 ('(,) _ 0 : xs) = LTakeSndParen 1 xs- LTakeSndParen 0 ('(,) _ _ ': xs) = LTakeSndParen 0 xs- LTakeSndParen 1 ('(,) a _ ': xs) = a : LTakeSndParen 1 xs- LTakeSndParen n _ = '[]---- -- * (Example) Value level equivalend of type family parsers----- -- map snd . parenCnt $ "((AGA)(bcd))(123) "--- -- dropLast . takeFstParen . parenCnt $ "((AGA)(bcd))(123)" --- -- dropLast . takeSndParen 0 . parenCnt $ "((AGA)(bcd))(123)" ---- leftTerm :: String -> String--- leftTerm = dropLast . takeFstParen . parenCnt---- rightTerm :: String -> String--- rightTerm = dropLast . takeSndParen 0 . parenCnt---- parenCnt :: String -> [(Char, Int)]--- parenCnt [] = []--- parenCnt ('(' : xs) = parenCntHelper ('(', -1) (parenCnt xs) --('(', parenCntFstCnt (parenCnt xs) - 1) : parenCnt xs--- parenCnt (')' : xs) = parenCntHelper (')', 1) (parenCnt xs) -- (')', parenCntFstCnt (parenCnt xs) + 1) : parenCnt xs--- parenCnt (x : xs) = parenCntHelper (x, 0) (parenCnt xs) -- (x, parenCntFstCnt (parenCnt xs) ) : parenCnt xs----- parenCntHelper :: (Char, Int) -> [(Char, Int)] -> [(Char, Int)]--- parenCntHelper (x, k) [] = [(x,k)]--- parenCntHelper (x, k) ((c,i) : xs) = (x, i + k): (c,i) : xs----- takeFstParen :: [(Char, Int)] -> [Char]--- takeFstParen [] = []--- takeFstParen ((_, 0) : xs) = takeFstParen xs--- takeFstParen ((')', 1) : _) = [')']--- takeFstParen ((a, p) : xs) = a : takeFstParen xs---- takeSndParen :: Int -> [(Char, Int)] -> [Char]--- takeSndParen _ [] = []--- takeSndParen 0 ((')', 1) : xs) = takeSndParen 1 xs--- takeSndParen 1 ((_, 0) : xs) = takeSndParen 1 xs--- takeSndParen 0 ((_, _) : xs) = takeSndParen 0 xs--- takeSndParen 1 ((a, _) : xs) = a : takeSndParen 1 xs--- takeSndParen _ _ = []---- dropLast :: [Char] -> [Char]--- dropLast [] = []--- dropLast [x] = []--- dropLast (x:xs) = x : dropLast xs-
− src/Data/TypedEncoding/Combinators/Restriction/BoundedAlphaNums.hs
@@ -1,112 +0,0 @@--{-# LANGUAGE MultiParamTypeClasses #-}--- {-# LANGUAGE PolyKinds #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeFamilies #-}--- {-# LANGUAGE KindSignatures #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE UndecidableInstances #-}---- | --- Restrictions @"r-ban:"@ cover commonly used fixed (short) size strings with restricted--- characters such as GUID, credit card numbers, etc. --- --- Alphanumeric chars are ordered: @0-9@ followed by @A-Z@,--- followed by @a-z@. Annotation specifies upper character bound. --- Any non alpha numeric characters are considered fixed delimiters--- and need to be present exactly as specified.--- For example @"r-ban:999-99-9999"@ could be used to describe SSN numbers,--- @"r-ban:FFFF" would describe strings consisting of 4 hex digits.------ This is a simple implementation that converts to @String@, should be used--- only with short length data.------ This module does not create instances of @EncodeF@ typeclass to avoid duplicate instance issues.------ Decoding function @decFR@ is located in--- "Data.TypedEncoding.Combinators.Restriction.Common"------ Use 'Data.TypedEncoding.Combinators.Restriction.Common.recWithEncR' --- to create manual recovery step that can be combined with 'recreateFPart'.------ @since 0.2.1.0-module Data.TypedEncoding.Combinators.Restriction.BoundedAlphaNums where ---import GHC.TypeLits-import qualified Data.List as L-import Data.Char-import Data.Proxy-import Data.Either--import Data.TypedEncoding.Internal.Util.TypeLits-import Data.TypedEncoding.Internal.Class.IsStringR-import Data.TypedEncoding.Instances.Support---- $setup--- >>> :set -XOverloadedStrings -XMultiParamTypeClasses -XDataKinds -XTypeApplications--- >>> import qualified Data.Text as T--- >>> import Data.TypedEncoding.Combinators.Restriction.Common---- better compilation errors?-type family IsBan (s :: Symbol) :: Bool where- IsBan s = AcceptEq ('Text "Not ban restriction encoding " ':<>: ShowType s ) (CmpSymbol (TakeUntil s ":") "r-ban")--type instance IsSupersetOpen "r-ASCII" "r-ban" xs = 'True--instance (KnownSymbol s, "r-ban" ~ TakeUntil s ":" , IsStringR str, Encodings (Either EncodeEx) xs grps c str) => Encodings (Either EncodeEx) (s ': xs) ("r-ban" ': grps) c str where- encodings = AppendEnc encFBan encodings---- |--- >>> encFBan . toEncoding () $ "C59F9FB7-4621-44D9-9020-CE37BF6E2BD1" :: Either EncodeEx (Enc '["r-ban:FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF"] () T.Text)--- Right (MkEnc Proxy () "C59F9FB7-4621-44D9-9020-CE37BF6E2BD1")--- --- >>> recWithEncR encFBan . toEncoding () $ "211-22-9934" :: Either RecreateEx (Enc '["r-ban:999-99-9999"] () T.Text)--- Right (MkEnc Proxy () "211-22-9934")-encFBan :: forall f s t xs c str .- (- IsStringR str- , KnownSymbol s- , IsBan s ~ 'True- , f ~ Either EncodeEx- ) => - Enc xs c str -> f (Enc (s ': xs) c str) -encFBan = implEncodeF @s (verifyBoundedAlphaNum (Proxy :: Proxy s)) ---- TODO v0.3 remove f from forall in encFBan (slightly breaking chanage)----- |--- >>> verifyBoundedAlphaNum (Proxy :: Proxy "r-ban:FF-FF") (T.pack "12-3E")--- Right "12-3E"--- >>> verifyBoundedAlphaNum (Proxy :: Proxy "r-ban:FF-FF") (T.pack "1G-3E")--- Left "'G' not boulded by 'F'"--- >>> verifyBoundedAlphaNum (Proxy :: Proxy "r-ban:FF-FF") (T.pack "13G3E")--- Left "'G' not matching '-'"--- >>> verifyBoundedAlphaNum (Proxy :: Proxy "r-ban:FF-FF") (T.pack "13-234")--- Left "Input list has wrong size expecting 5 but length \"13-234\" == 6"-verifyBoundedAlphaNum :: forall s a str . (KnownSymbol s, IsStringR str) => Proxy s -> str -> Either String str-verifyBoundedAlphaNum p str = - if pattl == inpl - then case lefts match of- (e: _) -> Left e- _ -> Right str- else Left $ "Input list has wrong size expecting " ++ show pattl ++ " but length " ++ show input ++ " == " ++ show inpl - where - patt = L.drop (L.length ("r-ban:" :: String)) . symbolVal $ p- input = toString str- pattl = L.length patt- inpl = L.length input - - match = L.zipWith fn input patt--- fn ci cp = case (isAlphaNum ci, isAlphaNum cp, ci <= cp, ci == cp) of- (True, True, True, _) -> Right ()- (_, _, _, True) -> Right ()- (_, True, _, False) -> Left $ show ci ++ " not boulded by " ++ show cp- (_, False, _, False) -> Left $ show ci ++ " not matching " ++ show cp
− src/Data/TypedEncoding/Combinators/Restriction/Common.hs
@@ -1,64 +0,0 @@---- {-# LANGUAGE PolyKinds #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ScopedTypeVariables #-}--- {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TypeFamilies #-}--- {-# LANGUAGE KindSignatures #-}s-{-# LANGUAGE UndecidableInstances #-}---- |--- Common combinators used across encodings.------ @since 0.2.1.0-module Data.TypedEncoding.Combinators.Restriction.Common where --import GHC.TypeLits-import Data.TypedEncoding.Internal.Util.TypeLits-import Data.TypedEncoding.Instances.Support---- $setup--- >>> :set -XOverloadedStrings -XMultiParamTypeClasses -XDataKinds -XTypeApplications----- | Universal decode for all "r-" types-decFR :: (IsR s ~ 'True, Applicative f) => - Enc (s ': xs) c str -> f (Enc xs c str) -decFR = implTranP id ----- | --- Manual recreate step combinator converting @"r-"@ encode function to a recreate step.------ For "r-" encoding recreate and encode are the same other than the exception type used. ------ The convention in @typed-encoding@ is to implement encode and convert it to recreate.-recWithEncR :: forall (s :: Symbol) xs c str . (IsR s ~ 'True) - => (Enc xs c str -> Either EncodeEx (Enc (s ': xs) c str)) - -> Enc xs c str -> Either RecreateEx (Enc (s ': xs) c str)-recWithEncR = unsafeRecWithEncR---unsafeRecWithEncR :: forall (s :: Symbol) xs c str .- (Enc xs c str -> Either EncodeEx (Enc (s ': xs) c str)) - -> Enc xs c str -> Either RecreateEx (Enc (s ': xs) c str)-unsafeRecWithEncR fn = either (Left . encToRecrEx) Right . fn---- |--- >>> :kind! IsR "r-UPPER"--- ...--- ... 'True------ >>> :kind! IsR "do-UPPER"--- ...--- = (TypeError ... -type family IsR (s :: Symbol) :: Bool where- IsR s = AcceptEq ('Text "Not restriction encoding " ':<>: ShowType s ) (CmpSymbol "r-" (Take 2 s))---type family IsROrEmpty (s :: Symbol) :: Bool where- IsROrEmpty "" = True- IsROrEmpty x = IsR x
+ src/Data/TypedEncoding/Combinators/ToEncStr.hs view
@@ -0,0 +1,50 @@++{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE AllowAmbiguousTypes #-}++-- | v0.2 like (backward compatible) combinators for covering to and from encoded string.+--+-- @since 0.3.0.0++module Data.TypedEncoding.Combinators.ToEncStr where++import Data.TypedEncoding.Common.Class+import Data.TypedEncoding.Common.Types (Enc(..) )++import Data.Functor.Identity++toEncStringF :: forall nm f a str . (ToEncString f nm nm a str) => a -> f (Enc '[nm] () str)+toEncStringF = toEncStringF' @nm @nm++toEncStringF' :: forall alg nm f a str . (ToEncString f nm alg a str) => a -> f (Enc '[nm] () str)+toEncStringF' = toEncF @f @nm @alg++toEncString :: forall nm a str . (ToEncString Identity nm nm a str) => a -> Enc '[nm] () str+toEncString = toEncString' @nm @nm++toEncString' :: forall alg nm a str . (ToEncString Identity nm alg a str) => a -> Enc '[nm] () str+toEncString' = runIdentity . toEncF @Identity @nm @alg++++-- backward compatible v0.2 like combinators+fromEncStringF :: forall nm f a str . (FromEncString f nm nm a str) => Enc '[nm] () str -> f a+fromEncStringF = fromEncStringF' @nm @nm++fromEncStringF' :: forall alg nm f a str . (FromEncString f nm alg a str) => Enc '[nm] () str -> f a+fromEncStringF' = fromEncF @f @nm @alg++fromEncString :: forall nm a str . (FromEncString Identity nm nm a str) => Enc '[nm] () str -> a+fromEncString = fromEncString' @nm @nm++fromEncString' :: forall alg nm a str . (FromEncString Identity nm alg a str) => Enc '[nm] () str -> a+fromEncString' = runIdentity . fromEncF @Identity @nm @alg
+ src/Data/TypedEncoding/Combinators/Unsafe.hs view
@@ -0,0 +1,32 @@+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE ScopedTypeVariables #-}++-- | Basic unsafe operations on 'Enc'+module Data.TypedEncoding.Combinators.Unsafe where++import Data.TypedEncoding.Common.Types.Enc+import Data.Proxy+++-- |+-- Currently recommended way of recreating encoding from trusted input,+-- if avoiding cost of "Data.TypedEncoding.Common.Types.Validation" is important+-- +-- @since 0.1.0.0 (moved)+unsafeSetPayload :: conf -> str -> Enc enc conf str +unsafeSetPayload = UnsafeMkEnc Proxy ++-- |+-- @since 0.1.0.0 (moved)+withUnsafeCoerce :: (s1 -> s2) -> Enc e1 c s1 -> Enc e2 c s2+withUnsafeCoerce f (UnsafeMkEnc _ conf str) = UnsafeMkEnc Proxy conf (f str)++-- |+-- @since 0.3.0.0+withUnsafeCoerceF :: forall e1 e2 f c s1 s2 . Functor f => (s1 -> f s2) -> Enc e1 c s1 -> f (Enc e2 c s2)+withUnsafeCoerceF f (UnsafeMkEnc _ conf str) = UnsafeMkEnc Proxy conf <$> f str++-- |+-- @since 0.1.0.0 (moved)+unsafeChangePayload :: (s1 -> s2) -> Enc e c s1 -> Enc e c s2+unsafeChangePayload f (UnsafeMkEnc p conf str) = UnsafeMkEnc p conf (f str)
+ src/Data/TypedEncoding/Combinators/Validate.hs view
@@ -0,0 +1,88 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE TypeApplications #-}++-- | Combinators reexported in Data.TypedEncoding+module Data.TypedEncoding.Combinators.Validate where++import Data.TypedEncoding.Combinators.Common+import Data.TypedEncoding.Combinators.Unsafe+import Data.TypedEncoding.Common.Types+import Data.TypedEncoding.Common.Class.Validate+import Data.TypedEncoding.Common.Class.Util (SymbolList, Append)+import GHC.TypeLits+import Data.Functor.Identity+++-- * Validation++-- | Maybe signals annotation mismatch, effect @f@ is not evaluated unless there is match+checkWithValidations :: forall algs (nms :: [Symbol]) f c str . (+ Monad f+ , SymbolList nms+ ) => Validations f nms algs c str -> UncheckedEnc c str -> Maybe (f (Enc nms c str))+checkWithValidations vers x = + case verifyAnn @nms x of+ Left err -> Nothing -- asRecreateErr_ perr $ Left err+ Right (MkUncheckedEnc _ c str) -> Just $ recreateWithValidations vers . toEncoding c $ str++check :: forall (nms :: [Symbol]) f c str . (+ Monad f+ , ValidateAll f nms nms c str+ , SymbolList nms+ ) => UncheckedEnc c str -> Maybe (f (Enc nms c str)) +check = checkWithValidations @nms @nms @f validations ++check' :: forall algs (nms :: [Symbol]) f c str . (+ Monad f+ , ValidateAll f nms algs c str+ , SymbolList nms+ ) => UncheckedEnc c str -> Maybe (f (Enc nms c str)) +check' = checkWithValidations @algs @nms @f validations +++recreateWithValidations :: forall algs nms f c str . (Monad f) => Validations f nms algs c str -> Enc ('[]::[Symbol]) c str -> f (Enc nms c str)+recreateWithValidations vers str@(UnsafeMkEnc _ _ pay) = + let str0 :: Enc nms c str = withUnsafeCoerce id str+ in withUnsafeCoerce (const pay) <$> runValidationChecks vers str0 ++-- * v0.2 style recreate functions++recreateFAll :: forall nms f c str . (Monad f, ValidateAll f nms nms c str) => + Enc ('[]::[Symbol]) c str + -> f (Enc nms c str) +recreateFAll = recreateFAll' @nms @nms ++recreateAll :: forall nms c str . (ValidateAll Identity nms nms c str) =>+ Enc ('[]::[Symbol]) c str + -> Enc nms c str +recreateAll = recreateAll' @nms @nms ++recreateFPart :: forall xs xsf f c str . (Monad f, ValidateAll f xs xs c str) => Enc xsf c str -> f (Enc (Append xs xsf) c str)+recreateFPart = recreateFPart' @xs @xs ++-- * Convenience combinators which mimic pre-v0.3 type signatures. These do not try to figure out @algs@ or assume much about them++recreateFAll' :: forall algs nms f c str . (Monad f, ValidateAll f nms algs c str) => + Enc ('[]::[Symbol]) c str + -> f (Enc nms c str) +recreateFAll' = recreateWithValidations @algs @nms @f validations ++recreateAll' :: forall algs nms c str . (ValidateAll Identity nms algs c str) =>+ Enc ('[]::[Symbol]) c str + -> Enc nms c str +recreateAll' = runIdentity . recreateFAll' @algs ++recreateFPart' :: forall algs xs xsf f c str . (Monad f, ValidateAll f xs algs c str) => Enc xsf c str -> f (Enc (Append xs xsf) c str)+recreateFPart' = aboveF @xsf @'[] @xs (recreateFAll' @algs) ++recreatePart' :: forall algs xs xsf c str . (ValidateAll Identity xs algs c str) => Enc xsf c str -> Enc (Append xs xsf) c str +recreatePart' = runIdentity . recreateFPart' @algs @xs+++--------------------------------------------
+ src/Data/TypedEncoding/Common/Class.hs view
@@ -0,0 +1,63 @@++{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE AllowAmbiguousTypes #-}++module Data.TypedEncoding.Common.Class (+ module Data.TypedEncoding.Common.Class+ , module Data.TypedEncoding.Common.Class.Util+ , module Data.TypedEncoding.Common.Class.Encode+ , module Data.TypedEncoding.Common.Class.Decode+ , module Data.TypedEncoding.Common.Class.Validate + , module Data.TypedEncoding.Common.Class.Superset + ) where++import Data.TypedEncoding.Common.Class.Util+import Data.TypedEncoding.Common.Class.Encode+import Data.TypedEncoding.Common.Class.Decode+import Data.TypedEncoding.Common.Class.Validate+import Data.TypedEncoding.Common.Class.Superset++import Data.TypedEncoding.Common.Types (Enc(..) )+import Data.TypedEncoding.Combinators.Unsafe (withUnsafeCoerce)++import GHC.TypeLits++++-- | +-- Generalized Java @toString@ or a type safe version of Haskell's 'Show'.+--+-- Encodes @a@ as @Enc '[xs]@ specifying algorithm @alg@ and using effect @f@ +--+class (KnownSymbol nm, KnownSymbol ann) => ToEncString f nm ann a str where+ toEncF :: a -> f (Enc '[nm] () str)++-- | +-- Reverse of 'ToEncString' decodes encoded string back to @a@+class (KnownSymbol nm, KnownSymbol ann) => FromEncString f nm ann a str where+ fromEncF :: Enc '[nm] () str -> f a++++-- Other classes --++-- | Flatten is more permissive 'IsSuperset'+-- @+-- instance FlattenAs "r-ASCII" "enc-B64" where -- OK+-- @+-- +-- Now encoded data has form @Enc '["r-ASCII"] c str@ +-- and there is no danger of it begin incorrectly decoded.++class FlattenAs (y :: Symbol) (x :: Symbol) where+ flattenAs :: Enc (x ': xs) c str -> Enc '[y] c str+ flattenAs = withUnsafeCoerce id
+ src/Data/TypedEncoding/Common/Class/Decode.hs view
@@ -0,0 +1,58 @@+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE TypeApplications #-}++module Data.TypedEncoding.Common.Class.Decode where++import Data.TypedEncoding.Common.Types (UnexpectedDecodeEx(..))+import Data.TypedEncoding.Common.Types.Decoding+import Data.Proxy+import Data.Functor.Identity+import GHC.TypeLits++-- |+-- @since 0.3.0.0+class Decode f nm alg conf str where+ decoding :: Decoding f nm alg conf str++-- |+-- @since 0.3.0.0+class DecodeAll f nms algs conf str where+ decodings :: Decodings f nms algs conf str++instance DecodeAll f '[] '[] conf str where + decodings = ZeroD ++instance (DecodeAll f nms algs conf str, Decode f nm alg conf str) => DecodeAll f (nm ': nms) (alg ': algs) conf str where + decodings = ConsD decoding decodings +++-- | With type safety in place decoding errors should be unexpected.+-- This class can be used to provide extra info if decoding could fail+--+-- @since 0.1.0.0+class UnexpectedDecodeErr f where + unexpectedDecodeErr :: UnexpectedDecodeEx -> f a++instance UnexpectedDecodeErr Identity where+ unexpectedDecodeErr x = fail $ show x++instance UnexpectedDecodeErr (Either UnexpectedDecodeEx) where+ unexpectedDecodeErr = Left ++-- |+-- @since 0.1.0.0+asUnexpected_ :: (KnownSymbol x, UnexpectedDecodeErr f, Applicative f, Show err) => Proxy x -> Either err a -> f a+asUnexpected_ p (Left err) = unexpectedDecodeErr $ UnexpectedDecodeEx p err+asUnexpected_ _ (Right r) = pure r++-- |+-- @since 0.1.0.0+asUnexpected :: forall x f err a . (KnownSymbol x, UnexpectedDecodeErr f, Applicative f, Show err) => Either err a -> f a+asUnexpected = asUnexpected_ (Proxy :: Proxy x)
+ src/Data/TypedEncoding/Common/Class/Encode.hs view
@@ -0,0 +1,44 @@+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE TypeApplications #-}++module Data.TypedEncoding.Common.Class.Encode where++import Data.TypedEncoding.Common.Types.Enc+++-- | +-- Using 2 Symbol type variables (@nm@ and @alg@) creates what seems like redundant typing+-- in statically defined instances such as @"r-ASCII"@, however it +-- provides future flexibility to +-- constrain @nm@ in some interesting way, different than @AlgNm nm ~ alg@. +--+-- It also seems to be easier to understand as type variables used in the definition of 'Encoding'+-- match with what is on the typeclass.+--+-- @alg@ is expected to be very statically defined and is needed to support more open instances such as @"r-ban"@.+--+-- @since 0.3.0.0+class Encode f nm alg conf str where+ encoding :: Encoding f nm alg conf str++-- |+-- @since 0.3.0.0+class EncodeAll f nms algs conf str where+ encodings :: Encodings f nms algs conf str++instance EncodeAll f '[] '[] conf str where + encodings = ZeroE ++instance (EncodeAll f nms algs conf str, Encode f nm alg conf str) => EncodeAll f (nm ': nms) (alg ': algs) conf str where + encodings = ConsE encoding encodings ++++
+ src/Data/TypedEncoding/Common/Class/IsStringR.hs view
@@ -0,0 +1,72 @@+++{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE FlexibleInstances #-}++-- | This Module will be removed in the future in favor of classes defined in+-- "Data.TypedEncoding.Common.Class.Util.StringConstraints"+module Data.TypedEncoding.Common.Class.IsStringR where++import Data.Proxy++import Data.String+import qualified Data.Text as T+import qualified Data.Text.Lazy as TL++++-- $setup+-- >>> :set -XScopedTypeVariables -XTypeApplications -XAllowAmbiguousTypes+-- >>> import Test.QuickCheck+-- >>> import Test.QuickCheck.Instances.Text()+-- >>> import Test.QuickCheck.Instances.ByteString()++-- | This class will be removed in 0.3.x.x in favor of classes definined in +-- "Data.TypedEncoding.Common.Class.Util.StringConstraints"+--+-- Reverses 'Data.String.IsString'+--+-- laws:+--+-- @+-- toString . fromString == id+-- fromString . toString == id+-- @+--+-- Note: ByteString is not a valid instance, ByteString "r-ASCII", or "r-UTF8" would+-- be needed.+-- @B.unpack $ B.pack "\160688" == "\176"@+class IsStringR a where+ toString :: a -> String++prop_fromStringToString :: forall s . (IsString s, IsStringR s, Eq s) => s -> Bool+prop_fromStringToString x = x == (fromString @s . toString $ x)++prop_toStringFromString :: forall s . (IsString s, IsStringR s) => Proxy s -> String -> Bool+prop_toStringFromString _ x = x == (toString @s . fromString $ x)++-- This would not work+-- @B.unpack $ B.pack "\160688" == "\176"@+--+-- prop> prop_toStringFromString (Proxy :: Proxy B.ByteString) +-- prop> prop_fromStringToString @B.ByteString+-- instance IsStringR B.ByteString where+-- toString = B.unpack+ ++-- |+-- prop> prop_toStringFromString (Proxy :: Proxy T.Text) +-- prop> prop_fromStringToString @T.Text+instance IsStringR T.Text where+ toString = T.unpack ++-- |+-- prop> prop_toStringFromString (Proxy :: Proxy TL.Text) +-- prop> prop_fromStringToString @TL.Text+instance IsStringR TL.Text where+ toString = TL.unpack ++instance IsStringR [Char] where+ toString = id
+ src/Data/TypedEncoding/Common/Class/Superset.hs view
@@ -0,0 +1,89 @@++{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+-- {-# LANGUAGE TypeApplications #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE AllowAmbiguousTypes #-}++module Data.TypedEncoding.Common.Class.Superset where++import Data.TypedEncoding.Common.Util.TypeLits++import Data.TypedEncoding.Common.Types (Enc(..) )+import Data.TypedEncoding.Combinators.Unsafe (withUnsafeCoerce)+import GHC.TypeLits+import Data.Symbol.Ascii+++-- $setup+-- >>> :set -XOverloadedStrings -XMultiParamTypeClasses -XDataKinds -XTypeApplications+-- >>> import Data.TypedEncoding+-- >>> import Data.TypedEncoding.Instances.Restriction.UTF8 ()+-- >>> import Data.TypedEncoding.Instances.Restriction.ASCII ()+-- >>> import Data.Text as T+++-- |+-- Replaces previous @Superset@ typeclass.+--+-- Subsets are useful for restriction encodings+-- like r-UFT8 but should not be used for other encodings as+-- this would be dangerous. For example, considering "enc-" encoding as a superset of "r-" encoding would+-- permit converting encoded binary +-- @"Enc '["enc-"] c ByteString@ to @"Enc '["r-ASCII"] c ByteString@ and then to @"Enc '["r-ASCII"] c Text@, +-- which could result in runtime errors.+--+-- The requirement is that that the decoding in the superset+-- can replace the decoding from injected subset.+--+-- @IsSuperset bigger smaller@ reads as @bigger@ is a superset of @smaller@+--+-- @since 0.2.2.0+type family IsSuperset (y :: Symbol) (x :: Symbol) :: Bool where+ IsSuperset "r-ASCII" "r-ASCII" = 'True+ IsSuperset "r-UTF8" "r-ASCII" = 'True+ IsSuperset "r-UTF8" "r-UTF8" = 'True+ IsSuperset y x = IsSupersetOpen y (TakeUntil x ":") (ToList x)++type family IsSupersetOpen (y :: Symbol) (x :: Symbol) (xs :: [Symbol]) :: Bool++-- |+-- >>> let Right tstAscii = encodeFAll . toEncoding () $ "Hello World" :: Either EncodeEx (Enc '["r-ASCII"] () T.Text)+-- >>> displ (injectInto @ "r-UTF8" tstAscii)+-- "Enc '[r-UTF8] () (Text Hello World)"+--+-- @since 0.2.2.0+injectInto :: forall y x xs c str . (IsSuperset y x ~ 'True) => Enc (x ': xs) c str -> Enc (y ': xs) c str+injectInto = withUnsafeCoerce id++-- TODO consider expanding to +-- _injectInto ::forall y x xs c str . (IsSuperset y x ~ 'True) => Enc (x ': xs) c str -> Enc (Replace x y xs) c str++-- |+-- IsSuperset is not intended for @"enc-"@ encodings. This class is.+-- +-- It allows to specify constraints that say, for example, that /Base 64/ encodes into +-- a subset of /ASCII/.+--+-- @since 0.3.0.0+class EncodingSuperset (enc :: Symbol) where+ type EncSuperset enc :: Symbol++ implEncInto :: forall xs c str . Enc (enc ': xs) c str -> Enc (EncSuperset enc ': enc ': xs) c str+ implEncInto = withUnsafeCoerce id+ +{-# WARNING implEncInto "Using this method at the call site may not be backward compatible between minor version upgrades, use _encodesInto instead." #-}++_encodesInto :: forall y enc xs c str r . (IsSuperset y r ~ 'True, EncodingSuperset enc, r ~ EncSuperset enc) => Enc (enc ': xs) c str -> Enc (y ': enc ': xs) c str+_encodesInto = injectInto . implEncInto++-- prop_Superset :: forall y x xs c str . (Superset y x, Eq str) => Enc (x ': xs) c str -> Bool+-- prop_Superset x = getPayload x == (getPayload . inject @y @x $ x)+
+ src/Data/TypedEncoding/Common/Class/Util.hs view
@@ -0,0 +1,89 @@++{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE AllowAmbiguousTypes #-}++module Data.TypedEncoding.Common.Class.Util where++import Data.TypedEncoding.Common.Types.Common++import qualified Data.ByteString.Char8 as B+import qualified Data.ByteString.Lazy.Char8 as BL+import qualified Data.Text as T+import qualified Data.Text.Lazy as TL++import Data.Proxy+import qualified Data.List as L+import GHC.TypeLits++-- $setup+-- >>> :set -XScopedTypeVariables -XTypeApplications -XAllowAmbiguousTypes -XDataKinds++-- * Symbol List++class SymbolList (xs::[Symbol]) where + symbolVals :: [String]++instance SymbolList '[] where+ symbolVals = []++-- |+-- >>> symbolVals @ '["FIRST", "SECOND"]+-- ["FIRST","SECOND"]+instance (SymbolList xs, KnownSymbol x) => SymbolList (x ': xs) where+ symbolVals = symbolVal (Proxy :: Proxy x) : symbolVals @xs+ +symbolVals_ :: forall xs . SymbolList xs => Proxy xs -> [String]+symbolVals_ _ = symbolVals @xs++-- * Display ++-- | Human friendly version of Show+class Displ x where + displ :: x -> String+++instance Displ [EncAnn] where + displ x = "[" ++ L.intercalate "," x ++ "]"+instance Displ T.Text where+ displ x = "(Text " ++ T.unpack x ++ ")"+instance Displ TL.Text where+ displ x = "(TL.Text " ++ TL.unpack x ++ ")"+instance Displ B.ByteString where+ displ x = "(ByteString " ++ B.unpack x ++ ")" +instance Displ BL.ByteString where+ displ x = "(ByteString " ++ BL.unpack x ++ ")" +instance Displ String where+ displ x = "(String " ++ x ++ ")" ++++-- |+-- >>> displ (Proxy :: Proxy ["FIRST", "SECOND"])+-- "[FIRST,SECOND]"+instance (SymbolList xs) => Displ (Proxy xs) where+ displ _ = displ $ symbolVals @ xs+ -- "[" ++ (L.intercalate "," $ map displ $ symbolVals @ xs) ++ "]"+++-- * Other++-- | TODO should this be imported from somewhere?+type family Append (xs :: [k]) (ys :: [k]) :: [k] where+ Append '[] xs = xs+ Append (y ': ys) xs = y ': Append ys xs++-- | Polymorphic data payloads used to encode/decode+class HasA a c where+ has :: c -> a++instance HasA () c where+ has = const ()
+ src/Data/TypedEncoding/Common/Class/Util/StringConstraints.hs view
@@ -0,0 +1,136 @@+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE AllowAmbiguousTypes #-}++-- | Future replacement for "Data.TypedEncoding.Common.Class.IsStringR"+module Data.TypedEncoding.Common.Class.Util.StringConstraints where++import qualified Data.List as L+import qualified Data.Text as T+import qualified Data.Text.Lazy as TL+import Data.String+import Data.Proxy+import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as BL+import qualified Data.ByteString.Char8 as B8+import qualified Data.ByteString.Lazy.Char8 as BL8++-- $setup+-- >>> :set -XScopedTypeVariables -XTypeApplications -XAllowAmbiguousTypes+-- >>> import Test.QuickCheck+-- >>> import Test.QuickCheck.Instances.Text()+-- >>> import Test.QuickCheck.Instances.ByteString()+++-- * IsString reversal++-- | Reverses 'Data.String.IsString'+--+-- law for types that are also @IsString@:+-- +-- @+-- toString . fromString == id+-- @+--+-- Note: ByteString is not a valid instance, ByteString "r-ASCII", or "r-UTF8" would+-- be needed.+-- @B8.unpack $ B8.pack "\160688" == "\176"@+--+-- This class is separated from @ToStrIso@ to allow instances from /smaller/ types+-- the can inject into the 'String' type.+class ToStrInj str from where+ toString :: from -> str++prop_toStringFromString :: forall s . (IsString s, ToStrInj String s) => Proxy s -> String -> Bool+prop_toStringFromString _ x = x == (toString @String @s . fromString $ x)+++-- |+-- prop> prop_toStringFromString (Proxy :: Proxy T.Text) +instance ToStrInj String T.Text where+ toString = T.unpack ++-- |+-- prop> prop_toStringFromString (Proxy :: Proxy TL.Text) +instance ToStrInj String TL.Text where+ toString = TL.unpack +++instance ToStrInj String String where+ toString = id++-- will not work!+-- prop> prop_toStringFromString (Proxy :: Proxy B.ByteString) +-- instance ToStrInj String B.ByteString where+-- toString = B8.unpack++-- will not work!+-- prop> prop_toStringFromString (Proxy :: Proxy BL.ByteString) +-- instance ToStrInj String BL.ByteString where+-- toString = BL8.unpack+++-- | Same as @ToStrInj@ but with additional+--+-- law for types that are also @IsString@:+-- @+-- fromString . toString == id+-- @+class ToStrInj str from => ToStrIso str from where++prop_fromStringToString :: forall s . (IsString s, ToStrIso String s, Eq s) => s -> Bool+prop_fromStringToString x = x == (fromString @s . toString $ x)++-- |+-- prop> prop_fromStringToString @T.Text+instance ToStrIso String T.Text where++-- |+-- prop> prop_fromStringToString @TL.Text+instance ToStrIso String TL.Text where ++instance ToStrIso String String where++-- |+-- Used to find exceptions that violated "r-" encoding+-- Expected to be used to check encoding of ASCII-7 so Text and ByteString are compatible.+class Char8Find str where+ find :: (Char -> Bool) -> str -> Maybe Char++instance Char8Find String where+ find = L.find++instance Char8Find T.Text where+ find = T.find++instance Char8Find TL.Text where+ find = TL.find ++-- |+-- B8.pack is basically a convenient way to get Word8 elements into ByteString.+--+-- During B8.pack conversion charters are downsized to the 0-255 range (become a Word8).+-- The length is preserved. +-- +-- >>> B8.pack "\160582"+-- "F"+--+-- >>> B.length $ B8.pack "\160582"+-- 1+--+-- This instance allows to check elements of ByteString interpreting them as Char.+-- +-- Safe if restricting to 7bit code points.+instance Char8Find B.ByteString where+ find = B8.find ++instance Char8Find BL.ByteString where+ find = BL8.find
+ src/Data/TypedEncoding/Common/Class/Validate.hs view
@@ -0,0 +1,50 @@++{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+-- {-# LANGUAGE TypeApplications #-}+{-# LANGUAGE AllowAmbiguousTypes #-}++module Data.TypedEncoding.Common.Class.Validate where++import Data.TypedEncoding.Common.Types (RecreateEx(..))+import Data.TypedEncoding.Common.Types.Validation++import Data.Proxy+import GHC.TypeLits+++class Validate f nm alg conf str where+ validation :: Validation f nm alg conf str++class ValidateAll f nms algs conf str where+ validations :: Validations f nms algs conf str++instance ValidateAll f '[] '[] conf str where + validations = ZeroV++instance (ValidateAll f nms algs conf str, Validate f nm alg conf str) => ValidateAll f (nm ': nms) (alg ': algs) conf str where + validations = ConsV validation validations ++++-- | Recovery errors are expected unless Recovery allows Identity instance+class RecreateErr f where + recoveryErr :: RecreateEx -> f a++instance RecreateErr (Either RecreateEx) where+ recoveryErr = Left ++asRecreateErr_ :: (RecreateErr f, Applicative f, Show err, KnownSymbol x) => Proxy x -> Either err a -> f a+asRecreateErr_ p (Left err) = recoveryErr $ RecreateEx p err+asRecreateErr_ _ (Right r) = pure r+++asRecreateErr :: forall x f err a . (RecreateErr f, Applicative f, Show err, KnownSymbol x) => Either err a -> f a+asRecreateErr = asRecreateErr_ (Proxy :: Proxy x)
+ src/Data/TypedEncoding/Common/Types.hs view
@@ -0,0 +1,23 @@++-- |+-- Internal definition of types++module Data.TypedEncoding.Common.Types (+ module Data.TypedEncoding.Common.Types+ , module Data.TypedEncoding.Common.Types.Enc+ , module Data.TypedEncoding.Common.Types.Decoding+ , module Data.TypedEncoding.Common.Types.Validation+ , module Data.TypedEncoding.Common.Types.CheckedEnc+ , module Data.TypedEncoding.Common.Types.UncheckedEnc+ , module Data.TypedEncoding.Common.Types.Common+ , module Data.TypedEncoding.Common.Types.Exceptions+ ) where++import Data.TypedEncoding.Common.Types.Enc+import Data.TypedEncoding.Common.Types.Decoding+import Data.TypedEncoding.Common.Types.Validation+import Data.TypedEncoding.Common.Types.CheckedEnc+import Data.TypedEncoding.Common.Types.UncheckedEnc+import Data.TypedEncoding.Common.Types.Common+import Data.TypedEncoding.Common.Types.Exceptions+
+ src/Data/TypedEncoding/Common/Types/CheckedEnc.hs view
@@ -0,0 +1,86 @@+{-# LANGUAGE GADTs #-}+{-# LANGUAGE ScopedTypeVariables #-}+-- {-# LANGUAGE PolyKinds #-}+-- {-# LANGUAGE DataKinds #-}+-- {-# LANGUAGE TypeOperators #-}+-- {-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE RankNTypes #-}++-- |+-- Module defines 'CheckedEnc' - untyped ADT version of 'Enc' ++module Data.TypedEncoding.Common.Types.CheckedEnc where++import Data.TypedEncoding.Common.Types.Enc+import Data.TypedEncoding.Common.Types.Common+import Data.TypedEncoding.Common.Class.Util+import Data.Proxy++-- $setup+-- >>> :set -XOverloadedStrings -XMultiParamTypeClasses -XDataKinds -XAllowAmbiguousTypes+-- >>> import qualified Data.Text as T+-- >>> import Data.TypedEncoding.Combinators.Unsafe (unsafeSetPayload)+++-- * Untyped Enc++-- constructor is to be treated as Unsafe to Encode and Decode instance implementations+-- particular encoding instances may expose smart constructors for limited data types++-- | Represents some validated encoded string. +--+-- @CheckedEnc@ is untyped version of 'Data.TypedEncoding.Common.Types.Enc.Enc'. +-- @CheckedEnc@ contains verified encoded data, encoding is visible+-- at the value level only.+data CheckedEnc conf str = UnsafeMkCheckedEnc [EncAnn] conf str+ deriving (Show, Eq) ++unsafeCheckedEnc:: [EncAnn] -> c -> s -> CheckedEnc c s+unsafeCheckedEnc = UnsafeMkCheckedEnc++getCheckedPayload :: CheckedEnc conf str -> str+getCheckedPayload = snd . getCheckedEncPayload++getCheckedEncPayload :: CheckedEnc conf str -> ([EncAnn], str) +getCheckedEncPayload (UnsafeMkCheckedEnc t _ s) = (t,s)++toCheckedEnc :: forall xs c str . (SymbolList xs) => Enc xs c str -> CheckedEnc c str +toCheckedEnc (UnsafeMkEnc p c s) = + UnsafeMkCheckedEnc (symbolVals @ xs) c s +++fromCheckedEnc :: forall xs c str . SymbolList xs => CheckedEnc c str -> Maybe (Enc xs c str)+fromCheckedEnc (UnsafeMkCheckedEnc xs c s) = + let p = Proxy :: Proxy xs+ in if symbolVals @ xs == xs+ then Just $ UnsafeMkEnc p c s+ else Nothing++------------------------++-- |+-- >>> let encsometest = UnsafeMkCheckedEnc ["TEST"] () $ T.pack "hello"+-- >>> proc_toCheckedEncFromCheckedEnc @'["TEST"] encsometest+-- True+-- >>> proc_toCheckedEncFromCheckedEnc @'["TEST1"] encsometest+-- False+proc_toCheckedEncFromCheckedEnc :: forall xs c str . (SymbolList xs, Eq c, Eq str) => CheckedEnc c str -> Bool+proc_toCheckedEncFromCheckedEnc x = (== Just x) . fmap (toCheckedEnc @ xs) . fromCheckedEnc $ x++-- |+-- >>> let enctest = unsafeSetPayload () "hello" :: Enc '["TEST"] () T.Text+-- >>> proc_fromCheckedEncToCheckedEnc enctest+-- True+proc_fromCheckedEncToCheckedEnc :: forall xs c str . (SymbolList xs, Eq c, Eq str) => Enc xs c str -> Bool+proc_fromCheckedEncToCheckedEnc x = (== Just x) . fromCheckedEnc . toCheckedEnc $ x++-- |+-- >>> displ $ unsafeCheckedEnc ["TEST"] () ("hello" :: T.Text)+-- "UnsafeMkCheckedEnc [TEST] () (Text hello)"+instance (Show c, Displ str) => Displ (CheckedEnc c str) where+ displ (UnsafeMkCheckedEnc xs c s) = + "UnsafeMkCheckedEnc " ++ displ xs ++ " " ++ show c ++ " " ++ displ s+
+ src/Data/TypedEncoding/Common/Types/Common.hs view
@@ -0,0 +1,68 @@+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeOperators #-}+-- {-# LANGUAGE FlexibleInstances #-}+-- {-# LANGUAGE FlexibleContexts #-}+-- {-# LANGUAGE UndecidableInstances #-}+-- {-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE ConstraintKinds #-}++module Data.TypedEncoding.Common.Types.Common where++import Data.TypedEncoding.Common.Util.TypeLits+import GHC.TypeLits+++-- $setup+-- >>> :set -XScopedTypeVariables -XTypeFamilies -XKindSignatures -XDataKinds+++-- | Represents value level (single) annotation.+type EncAnn = String ++type Restriction s = (KnownSymbol s, IsR s ~ 'True)++type Algorithm nm alg = AlgNm nm ~ alg+++-- |+-- Converts encoding name to algorithm name, this assumes the ":" delimiter expected by this library. +--+-- This allows working with open encoding definitions such as "r-ban" or "r-bool"+-- +-- >>> :kind! AlgNm "enc-B64"+-- ...+-- = "enc-B64"+--+-- >>> :kind! AlgNm "r-ban:999-99-9999"+-- ...+-- = "r-ban"+--+type family AlgNm (encnm :: Symbol) :: Symbol where+ AlgNm encnm = TakeUntil encnm ":"+++type family AlgNmMap (nms :: [Symbol]) :: [Symbol] where+ AlgNmMap '[] = '[]+ AlgNmMap (x ': xs) = AlgNm x ': AlgNmMap xs++-- |+-- >>> :kind! IsR "r-UPPER"+-- ...+-- ... 'True+--+-- >>> :kind! IsR "do-UPPER"+-- ...+-- = (TypeError ... +type family IsR (s :: Symbol) :: Bool where+ IsR s = AcceptEq ('Text "Not restriction encoding " ':<>: ShowType s ) (CmpSymbol "r-" (Take 2 s))+++type family IsROrEmpty (s :: Symbol) :: Bool where+ IsROrEmpty "" = True+ IsROrEmpty x = IsR x+
+ src/Data/TypedEncoding/Common/Types/Decoding.hs view
@@ -0,0 +1,76 @@+{-# LANGUAGE GADTs #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE PartialTypeSignatures #-}+{-# OPTIONS_GHC -Wno-partial-type-signatures #-}++-- |+-- Internal definition of types+--+-- Decoding types for @Enc@+module Data.TypedEncoding.Common.Types.Decoding where++import Data.Proxy+import GHC.TypeLits++import Data.TypedEncoding.Common.Types.Enc+import Data.TypedEncoding.Common.Types.Common++-- |+-- Similar to 'Data.TypedEncoding.Common.Types.Enc.Encoding'+--+-- Used to create instances of decoding.++data Decoding f (nm :: Symbol) (alg :: Symbol) conf str where+ -- | Consider this constructor as private or use it with care+ --+ -- Using this constructor:+ -- @+ -- MkDecoding :: Proxy nm -> (forall (xs :: [Symbol]) . Enc (nm ': xs) conf str -> f (Enc xs conf str)) -> Decoding f nm (AlgNm nm) conf str+ -- @+ -- + -- would make compilation much slower+ UnsafeMkDecoding :: Proxy nm -> (forall (xs :: [Symbol]) . Enc (nm ': xs) conf str -> f (Enc xs conf str)) -> Decoding f nm alg conf str++-- | Type safe smart constructor+-- (See also 'Data.TypedEncoding.Common.Types.Enc._mkEncoding') +mkDecoding :: forall f (nm :: Symbol) conf str . (forall (xs :: [Symbol]) . Enc (nm ': xs) conf str -> f (Enc xs conf str)) -> Decoding f nm (AlgNm nm) conf str+mkDecoding = UnsafeMkDecoding Proxy++runDecoding :: forall alg nm f xs conf str . Decoding f nm alg conf str -> Enc (nm ': xs) conf str -> f (Enc xs conf str)+runDecoding (UnsafeMkDecoding _ fn) = fn++-- | Same as 'runDecoding" but compiler figures out algorithm name+--+-- Using it can slowdown compilation+_runDecoding :: forall nm f xs conf str alg . (AlgNm nm ~ alg) => Decoding f nm alg conf str -> Enc (nm ': xs) conf str -> f (Enc xs conf str)+_runDecoding = runDecoding @(AlgNm nm)++-- |+-- Wraps a list of @Decoding@ elements.+--+-- Similarly to 'Data.TypedEncoding.Common.Types.Enc.Encodings' can be used with a typeclass+-- 'Data.TypedDecoding.Internal.Class.Decode.DecodeAll'+data Decodings f (nms :: [Symbol]) (algs :: [Symbol]) conf str where+ -- | constructor is to be treated as Unsafe to Encode and Decode instance implementations+ -- particular encoding instances may expose smart constructors for limited data types+ ZeroD :: Decodings f '[] '[] conf str+ ConsD :: Decoding f nm alg conf str -> Decodings f nms algs conf str -> Decodings f (nm ': nms) (alg ': algs) conf str++runDecodings :: forall algs nms f c str . (Monad f) => Decodings f nms algs c str -> Enc nms c str -> f (Enc ('[]::[Symbol]) c str)+runDecodings ZeroD enc0 = pure enc0+runDecodings (ConsD fn xs) enc = + let re :: f (Enc _ c str) = runDecoding fn enc+ in re >>= runDecodings xs+++-- | At possibly big compilation cost, have compiler figure out algorithm names.+_runDecodings :: forall nms f c str algs . (Monad f, algs ~ AlgNmMap nms) => Decodings f nms algs c str -> Enc nms c str -> f (Enc ('[]::[Symbol]) c str)+_runDecodings = runDecodings @(AlgNmMap nms)
+ src/Data/TypedEncoding/Common/Types/Enc.hs view
@@ -0,0 +1,225 @@+{-# LANGUAGE GADTs #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE PartialTypeSignatures #-}+{-# OPTIONS_GHC -Wno-partial-type-signatures #-}+-- |+-- Internal definition of types++module Data.TypedEncoding.Common.Types.Enc where++import Data.Proxy+import GHC.TypeLits++import Data.TypedEncoding.Common.Class.Util+import Data.TypedEncoding.Common.Types.Common++-- $setup+-- >>> :set -XOverloadedStrings -XMultiParamTypeClasses -XDataKinds -XAllowAmbiguousTypes+-- >>> import qualified Data.ByteString as B+-- >>> import qualified Data.Text as T+-- >>> import Data.Functor.Identity+-- >>> import Data.TypedEncoding+-- >>> import Data.TypedEncoding.Instances.Enc.Base64 ()+-- >>> import Data.TypedEncoding.Instances.Restriction.BoundedAlphaNums ()++-- |+-- Contains encoded data annotated by +--+-- * @nms@ list of @Symbol@s with encoding names (encoding stack)+-- * @conf@ that can contain configuration / encoding information such as digest.+-- * @str@ the encoded data+--+-- Example: +--+-- @+-- Enc '["r-ASCII"] () ByteString+-- @+--+-- @since 0.1.0.0+data Enc nms conf str where+ -- | + -- @since 0.3.0.0 renamed from MkEnc + --+ -- Use of this constructor should be kept to a minimum. + --+ -- Use of 'Data.TypedEncoding.Combinators.Unsafe.unsafeSetPayload' currently recommended+ -- for recovering 'Enc' from trusted input sources (if avoiding cost of "Data.TypedEncoding.Common.Types.Validation" is important).+ UnsafeMkEnc :: Proxy nms -> conf -> str -> Enc nms conf str+ deriving (Show, Eq) ++-- |+-- >>> let disptest = UnsafeMkEnc Proxy () "hello" :: Enc '["TEST"] () T.Text+-- >>> displ disptest+-- "Enc '[TEST] () (Text hello)"+instance (SymbolList xs, Show c, Displ str) => Displ ( Enc xs c str) where+ displ (UnsafeMkEnc p c s) = + "Enc '" ++ displ (Proxy :: Proxy xs) ++ " " ++ show c ++ " " ++ displ s++-- |+-- @since 0.1.0.0+toEncoding :: conf -> str -> Enc ('[] :: [Symbol]) conf str+toEncoding = UnsafeMkEnc Proxy ++-- |+-- @since 0.1.0.0+fromEncoding :: Enc '[] conf str -> str+fromEncoding = getPayload++-- |+-- @since 0.1.0.0+getPayload :: Enc enc conf str -> str +getPayload (UnsafeMkEnc _ _ str) = str++ ++-- |+-- Wraps the encoding function.+-- Contains type level information about the encoding name and the algorithm used. +--+-- This type is used by programs implementing encoding instance.+-- Such program needs to define a value of this type. +-- It also implements 'Data.TypedEncoding.Common.Class.Encode.Encode' instance that simply returns that value.+--+-- Programs using encoding can access this type using 'Data.TypedEncoding.Common.Class.Encode.Encode.encoding'+-- (from the @Encode@ typeclass) but a better (and recommended) approach is to use its plural sibling 'Encodings' +-- defined below.+--+-- This type has 2 symbol type variables:+--+-- * @nm@ defines the encoding+-- * @alg@ defines algorithm +--+-- These two are related, currently this library only supports +--+-- * Names @nm@ containing ":" using format "alg:...", for example name "r-ban:999" has "r-ban" algorithm +-- * Names without ":" require that @nm ~ alg@+--+-- Future version are likely to relax this, possibly introducing ability do define more than one algorithm+-- for given encoding. +--+-- Using 2 variables allows us to define typeclass constraints that work+-- with definitions like @"r-ban"@ where @"r-ban:@" can be followed by arbitrary+-- string literal.+--+-- Examples: +--+-- @+-- Encoding (Either EncodeEx) "r-ban:9" "r-ban" () String+-- @+--+-- encodes a single character @ <= 9'@+--+-- @+-- Encoding Identity "enc-B64" "enc-B64" () ByteString+-- @+--+-- Represents a /Byte 64/ encoder that can operate on any stack of previous encodings.+-- (encoding name and algorithm name are "enc-B64", there is no +-- additional configuration @()@ needed and it runs in the @Identity@ Functor.+--+-- Similar boilerplate for /Decoding/ and /Validation/ is specified in separate modules.+--+-- @since 0.3.0.0+data Encoding f (nm :: Symbol) (alg :: Symbol) conf str where+ -- | Consider this constructor as private or use it with care+ --+ -- Defining constructor like this:+ -- @+ -- MkEncoding :: Proxy nm -> (forall (xs :: [Symbol]) . Enc xs conf str -> f (Enc (nm ': xs) conf str)) -> Encoding f nm (AlgNm nm) conf str+ -- @+ -- + -- would make compilation much slower+ UnsafeMkEncoding :: Proxy nm -> (forall (xs :: [Symbol]) . Enc xs conf str -> f (Enc (nm ': xs) conf str)) -> Encoding f nm alg conf str++-- | Type safe smart constructor+--+-- Adding the type family @(AlgNm nm)@ mapping to @Encoding@ constructor slows down the compilation. +-- Using smart constructor does not have that issue.+--+-- This approach also provides more future flexibility with possibility of future overloads relaxing current +-- limitations on @alg@ names. +-- +-- /Notice underscore @_@ convention, it indicates a use of @Algorithm@ @AlgNm@: compiler figures out @alg@ value. These can be slower to compile when used. /+--+-- Here are other conventions that relate to the existence of @alg@+--+-- * functions ending with: @'@, for example 'Data.TypedEncoding.Combinators.Encode.encodeF'' have @alg@+-- as first type variable in the @forall@ list.+--+-- * functions without tick tend to assume @nm ~ alg@+-- +-- This particular function appears to not increase compilation time. +--+-- @since 0.3.0.0+_mkEncoding :: forall f (nm :: Symbol) conf str . (forall (xs :: [Symbol]) . Enc xs conf str -> f (Enc (nm ': xs) conf str)) -> Encoding f nm (AlgNm nm) conf str+_mkEncoding = UnsafeMkEncoding Proxy++-- |+-- @since 0.3.0.0+runEncoding' :: forall alg nm f xs conf str . Encoding f nm alg conf str -> Enc xs conf str -> f (Enc (nm ': xs) conf str)+runEncoding' (UnsafeMkEncoding _ fn) = fn++-- | Same as 'runEncoding'' but compiler figures out algorithm name+--+-- Using it can slowdown compilation+--+-- This combinator has @Algorithm nm alg@ constraint (which stands for @TakeUntil ":" nm ~ alg@.+-- If rules on @alg@ are relaxed this will just return the /default/ algorithm.+--+-- If that happens @-XTypeApplications@ annotations will be needed and @_@ methods will simply +-- use default algorithm name.+--+-- @since 0.3.0.0+_runEncoding :: forall nm f xs conf str alg . (Algorithm nm alg) => Encoding f nm alg conf str -> Enc xs conf str -> f (Enc (nm ': xs) conf str)+_runEncoding = runEncoding' @(AlgNm nm)++-- |+-- HList like construction that defines a list of @Encoding@ elements.+--+-- This type is used by programs using / manipulating encodings.+--+-- Can be easily accessed with 'Data.TypedEncoding.Common.Class.Encode.EncodeAll' constraint using+-- 'Data.TypedEncoding.Common.Class.Encode.EncodeAll.encodings'. But could also be used by creating+-- @Encodings@ list by hand.+--+-- @since 0.3.0.0+data Encodings f (nms :: [Symbol]) (algs :: [Symbol]) conf str where+ -- | constructor is to be treated as Unsafe to Encode and Decode instance implementations+ -- particular encoding instances may expose smart constructors for limited data types+ ZeroE :: Encodings f '[] '[] conf str+ ConsE :: Encoding f nm alg conf str -> Encodings f nms algs conf str -> Encodings f (nm ': nms) (alg ': algs) conf str++-- |+-- Runs encodings, requires -XTypeApplication annotation specifying the algorithm(s)+--+-- >>> runEncodings' @'["r-ban"] encodings . toEncoding () $ ("22") :: Either EncodeEx (Enc '["r-ban:111"] () T.Text)+-- Left (EncodeEx "r-ban:111" ("Input list has wrong size expecting 3 but length \"22\" == 2"))+--+-- @since 0.3.0.0+runEncodings' :: forall algs nms f c str . (Monad f) => Encodings f nms algs c str -> Enc ('[]::[Symbol]) c str -> f (Enc nms c str)+runEncodings' ZeroE enc0 = pure enc0+runEncodings' (ConsE fn enc) enc0 = + let re :: f (Enc _ c str) = runEncodings' enc enc0+ in re >>= runEncoding' fn+++-- | At a possibly some compilation cost, have compiler figure out algorithm names.+--+-- >>> _runEncodings encodings . toEncoding () $ ("Hello World") :: Identity (Enc '["enc-B64","enc-B64"] () B.ByteString)+-- Identity (UnsafeMkEnc Proxy () "U0dWc2JHOGdWMjl5YkdRPQ==")+-- +-- >>> _runEncodings encodings . toEncoding () $ ("22") :: Either EncodeEx (Enc '["r-ban:111"] () T.Text)+-- Left (EncodeEx "r-ban:111" ("Input list has wrong size expecting 3 but length \"22\" == 2"))+--+-- (see also '_runEncoding')+-- @since 0.3.0.0+_runEncodings :: forall nms f c str algs . (Monad f, algs ~ AlgNmMap nms) => Encodings f nms algs c str -> Enc ('[]::[Symbol]) c str -> f (Enc nms c str)+_runEncodings = runEncodings' @(AlgNmMap nms)
+ src/Data/TypedEncoding/Common/Types/Exceptions.hs view
@@ -0,0 +1,81 @@+{-# LANGUAGE GADTs #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+-- {-# LANGUAGE RankNTypes #-}++-- |+-- Internal definition of types++module Data.TypedEncoding.Common.Types.Exceptions where+++import Data.Proxy+import GHC.TypeLits+++-- $setup+-- >>> :set -XOverloadedStrings -XMultiParamTypeClasses -XDataKinds -XAllowAmbiguousTypes+-- >>> import qualified Data.Text as T+++-- | Represents errors in recovery (recreation of encoded types).+data RecreateEx where+ RecreateEx:: (Show e, KnownSymbol x) => Proxy x -> e -> RecreateEx+ RecreateExUnkStep:: (Show e) => e -> RecreateEx++instance Show RecreateEx where+ show (RecreateEx prxy a) = "(RecreateEx \"" ++ symbolVal prxy ++ "\" (" ++ show a ++ "))"+ show (RecreateExUnkStep a) = "(UnknownDecodeStep (" ++ show a ++ "))"+++recreateErrUnknown :: (Show e) => e -> RecreateEx+recreateErrUnknown = RecreateExUnkStep++-- instance Eq RecreateEx where+-- (RecreateEx prxy1 a1) == RecreateEx prxy2 a2 = (symbolVal prxy1) == (symbolVal prxy2)+++-- | Represents errors in encoding+data EncodeEx where+ EncodeEx:: (Show a, KnownSymbol x) => Proxy x -> a -> EncodeEx ++instance Show EncodeEx where+ show (EncodeEx prxy a) = "(EncodeEx \"" ++ symbolVal prxy ++ "\" (" ++ show a ++ "))"++asEncodeEx :: (Show a, KnownSymbol x) => Proxy x -> Either a b -> Either EncodeEx b+asEncodeEx p = either (Left . EncodeEx p) Right +++-- | Useful when manually recreating using recovery+encToRecrEx :: EncodeEx -> RecreateEx+encToRecrEx (EncodeEx p a) = RecreateEx p a++mergeEncodeEx :: KnownSymbol x => Proxy x -> EncodeEx -> Maybe EncodeEx -> EncodeEx+mergeEncodeEx _ ex Nothing = ex+mergeEncodeEx p (EncodeEx _ a) (Just (EncodeEx _ b)) = EncodeEx p $ "Errors: " ++ show (a,b)++emptyEncErr :: KnownSymbol x => Proxy x -> EncodeEx +emptyEncErr p = EncodeEx p ("unexpected" :: String)++-- | Type safety over encodings makes decoding process safe.+-- However failures are still possible due to bugs or unsafe payload modifications.+-- UnexpectedDecodeEx represents such errors.+data UnexpectedDecodeEx where+ UnexpectedDecodeEx :: (Show a, KnownSymbol x) => Proxy x -> a -> UnexpectedDecodeEx++instance Show UnexpectedDecodeEx where+ show (UnexpectedDecodeEx prxy a) = "(UnexpectedDecodeEx \"" ++ symbolVal prxy ++ "\" (" ++ show a ++ "))"+++-- * Base combinators that rely on types defined here++mergeErrs :: err -> (err -> Maybe err -> err) -> Either err a -> Either err b -> Either err c+mergeErrs _ fn (Left er1) (Left er2) = Left (fn er1 $ Just er2)+mergeErrs _ fn (Left er1) _ = Left (fn er1 Nothing)+mergeErrs _ fn _ (Left er2) = Left (fn er2 Nothing) +mergeErrs de fn (Right r) (Right y) = Left de
+ src/Data/TypedEncoding/Common/Types/SomeAnnotation.hs view
@@ -0,0 +1,34 @@+{-# LANGUAGE GADTs #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE RankNTypes #-}++-- | internally used existential type for taking track of annotations+module Data.TypedEncoding.Common.Types.SomeAnnotation where++import Data.TypedEncoding.Common.Types.Common+import Data.TypedEncoding.Common.Class.Util+import Data.TypedEncoding.Internal.Util+import Data.Proxy+import GHC.TypeLits+++data SomeAnnotation where+ MkSomeAnnotation :: SymbolList xs => Proxy xs -> SomeAnnotation++withSomeAnnotation :: SomeAnnotation -> (forall xs . SymbolList xs => Proxy xs -> r) -> r+withSomeAnnotation (MkSomeAnnotation p) fn = fn p++-- folds over SomeSymbol list using withSomeSymbol and proxyCons+someAnnValue :: [EncAnn] -> SomeAnnotation+someAnnValue xs = + foldr (fn . someSymbolVal) (MkSomeAnnotation (Proxy :: Proxy '[])) xs+ where + somesymbs = map someSymbolVal xs+ fn ss (MkSomeAnnotation pxs) = withSomeSymbol ss (\px -> MkSomeAnnotation (px `proxyCons` pxs)) ++proxyCons :: forall (x :: Symbol) (xs :: [Symbol]) . Proxy x -> Proxy xs -> Proxy (x ': xs)+proxyCons _ _ = Proxy
+ src/Data/TypedEncoding/Common/Types/SomeEnc.hs view
@@ -0,0 +1,62 @@+{-# LANGUAGE GADTs #-}+{-# LANGUAGE ScopedTypeVariables #-}+-- {-# LANGUAGE PolyKinds #-}+-- {-# LANGUAGE DataKinds #-}+-- {-# LANGUAGE TypeOperators #-}+-- {-# LANGUAGE FlexibleInstances #-}+-- {-# LANGUAGE StandaloneDeriving #-}+-- {-# LANGUAGE TypeApplications #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE RankNTypes #-}++-- |+-- Module defines 'SomeEnc' - existentially quantified version of @Enc@+-- and basic combinators++module Data.TypedEncoding.Common.Types.SomeEnc where++import Data.TypedEncoding.Common.Types.Enc+import Data.TypedEncoding.Common.Class.Util+import Data.TypedEncoding.Common.Types.SomeAnnotation+import Data.TypedEncoding.Common.Types.CheckedEnc++-- $setup+-- >>> :set -XOverloadedStrings -XMultiParamTypeClasses -XDataKinds -XAllowAmbiguousTypes+-- >>> import qualified Data.Text as T+-- >>> import Data.TypedEncoding.Combinators.Unsafe++++-- | Existentially quantified quantified @Enc@+-- effectively isomorphic to 'CheckedEnc'+data SomeEnc conf str where+ MkSomeEnc :: SymbolList xs => Enc xs conf str -> SomeEnc conf str+ +withSomeEnc :: SomeEnc conf str -> (forall xs . SymbolList xs => Enc xs conf str -> r) -> r+withSomeEnc (MkSomeEnc enc) f = f enc++toSome :: SymbolList xs => Enc xs conf str -> SomeEnc conf str+toSome = MkSomeEnc++-- | +-- >>> let enctest = unsafeSetPayload () "hello" :: Enc '["TEST"] () T.Text+-- >>> someToChecked . MkSomeEnc $ enctest+-- UnsafeMkCheckedEnc ["TEST"] () "hello"+someToChecked :: SomeEnc conf str -> CheckedEnc conf str+someToChecked se = withSomeEnc se toCheckedEnc++-- | +-- >>> let tst = unsafeCheckedEnc ["TEST"] () "test"+-- >>> displ $ checkedToSome tst+-- "Some (Enc '[TEST] () (String test))"+checkedToSome :: CheckedEnc conf str -> SomeEnc conf str+checkedToSome (UnsafeMkCheckedEnc xs c s) = withSomeAnnotation (someAnnValue xs) (\p -> MkSomeEnc (UnsafeMkEnc p c s))+++-- |+-- >>> let enctest = unsafeSetPayload () "hello" :: Enc '["TEST"] () T.Text+-- >>> displ $ MkSomeEnc enctest+-- "Some (Enc '[TEST] () (Text hello))"+instance (Show c, Displ str) => Displ (SomeEnc c str) where+ displ (MkSomeEnc en) = + "Some (" ++ displ en ++ ")"
+ src/Data/TypedEncoding/Common/Types/UncheckedEnc.hs view
@@ -0,0 +1,53 @@+{-# LANGUAGE GADTs #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE AllowAmbiguousTypes #-}++-- |+-- Internal definition of types++module Data.TypedEncoding.Common.Types.UncheckedEnc where++import Data.Proxy+import Data.TypedEncoding.Common.Class.Util+import Data.TypedEncoding.Common.Types.Common++-- $setup+-- >>> :set -XOverloadedStrings -XMultiParamTypeClasses -XDataKinds -XAllowAmbiguousTypes+-- >>> import qualified Data.Text as T+++-- * UncheckedEnc for validation, similar to CheckedEnc but not verified++-- | Represents some encoded string where encoding was not validated.+--+-- Similar to 'Data.TypedEncoding.Common.Types.CheckedEnc' but unlike+-- @CheckedEnc@ it can contain payloads that have invalid encoding.+-- +-- See 'Data.TypedEncoding.Combinators.Validate.check'+data UncheckedEnc c str = MkUncheckedEnc [EncAnn] c str deriving (Show, Eq)++toUncheckedEnc :: [EncAnn] -> c -> str -> UncheckedEnc c str+toUncheckedEnc = MkUncheckedEnc++getUncheckedEncAnn :: UncheckedEnc c str -> [EncAnn]+getUncheckedEncAnn (MkUncheckedEnc ann _ _) = ann++verifyAnn :: forall xs c str . SymbolList xs => UncheckedEnc c str -> Either String (UncheckedEnc c str)+verifyAnn x@(MkUncheckedEnc xs _ _) = + let p = Proxy :: Proxy xs+ in if symbolVals @ xs == xs+ then Right x+ else Left $ "UncheckedEnc has not matching annotation " ++ displ xs++-- |+-- >>> displ $ MkUncheckedEnc ["TEST"] () ("hello" :: T.Text)+-- "MkUncheckedEnc [TEST] () (Text hello)"+instance (Show c, Displ str) => Displ (UncheckedEnc c str) where+ displ (MkUncheckedEnc xs c s) = + "MkUncheckedEnc " ++ displ xs ++ " " ++ show c ++ " " ++ displ s
+ src/Data/TypedEncoding/Common/Types/Unsafe.hs view
@@ -0,0 +1,29 @@+{-# LANGUAGE GADTs #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE FlexibleInstances #-}++module Data.TypedEncoding.Common.Types.Unsafe where++import Data.Proxy+import Data.TypedEncoding.Common.Types++ +-- | Allows to operate within Enc. These are considered unsafe.+-- keeping the same list of encodings +newtype Unsafe enc conf str = Unsafe {runUnsafe :: Enc enc conf str} deriving (Show)++withUnsafe :: (Unsafe e c s1 -> Unsafe e c s2) -> Enc e c s1 -> Enc e c s2+withUnsafe f enc = runUnsafe . f $ Unsafe enc++instance Functor (Unsafe enc conf) where+ fmap f (Unsafe (UnsafeMkEnc p c x)) = Unsafe (UnsafeMkEnc p c (f x))+ +instance Applicative (Unsafe enc ()) where+ pure = Unsafe . UnsafeMkEnc Proxy () + Unsafe (UnsafeMkEnc p c1 f) <*> Unsafe (UnsafeMkEnc _ c2 x) = Unsafe (UnsafeMkEnc p () (f x)) ++instance Monad (Unsafe enc ()) where + Unsafe (UnsafeMkEnc _ _ x) >>= f = f x +
+ src/Data/TypedEncoding/Common/Types/Validation.hs view
@@ -0,0 +1,81 @@+{-# LANGUAGE GADTs #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE PartialTypeSignatures #-}+{-# OPTIONS_GHC -Wno-partial-type-signatures #-}+-- |+-- Internal definition of types+--+-- Validation types for @Enc@+--+-- See also+--+-- * "Data.TypedEncoding.Combinators.Validate"+-- * "Data.TypedEncoding.Common.Class.Validate"+--+-- Use of 'Data.TypedEncoding.Combinators.Unsafe.unsafeSetPayload' currently recommended+-- for recovering 'Enc' from trusted input sources (if avoiding cost of Validation is important).++module Data.TypedEncoding.Common.Types.Validation where++import Data.Proxy+import GHC.TypeLits++import Data.TypedEncoding.Common.Types.Enc+import Data.TypedEncoding.Common.Types.Common+++-- |+-- Validation unwraps a layer of encoding and offers payload data down the encoding stack+-- for further verification. +--+-- For "enc-" encodings this will typically be decoding step.+-- +-- For "r-" encodings this will typically be encoding step.+data Validation f (nm :: Symbol) (alg :: Symbol) conf str where+ UnsafeMkValidation :: Proxy nm -> (forall (xs :: [Symbol]) . Enc (nm ': xs) conf str -> f (Enc xs conf str)) -> Validation f nm alg conf str++-- | Type safe smart constructor+-- adding the type family @(AlgNm nm)@ restriction to UnsafeMkValidation slows down compilation, especially in tests. +mkValidation :: forall f (nm :: Symbol) conf str . (forall (xs :: [Symbol]) . Enc (nm ': xs) conf str -> f (Enc xs conf str)) -> Validation f nm (AlgNm nm) conf str+mkValidation = UnsafeMkValidation Proxy++runValidation :: forall alg nm f xs conf str . Validation f nm alg conf str -> Enc (nm ': xs) conf str -> f (Enc xs conf str)+runValidation (UnsafeMkValidation _ fn) = fn++-- | Same as 'runValidation" but compiler figures out algorithm name+--+-- Using it can slowdown compilation+_runValidation :: forall nm f xs conf str alg . (AlgNm nm ~ alg) => Validation f nm alg conf str -> Enc (nm ': xs) conf str -> f (Enc xs conf str)+_runValidation = runValidation @(AlgNm nm)++-- |+-- Wraps a list of @Validation@ elements.+--+-- Similarly to 'Validation' it can be used with a typeclass+-- 'Data.TypedValidation.Internal.Class.Encode.EncodeAll'+data Validations f (nms :: [Symbol]) (algs :: [Symbol]) conf str where+ -- | constructor is to be treated as Unsafe to Encode and Decode instance implementations+ -- particular encoding instances may expose smart constructors for limited data types+ ZeroV :: Validations f '[] '[] conf str+ ConsV :: Validation f nm alg conf str -> Validations f nms algs conf str -> Validations f (nm ': nms) (alg ': algs) conf str++-- | This basically puts payload in decoded state.+-- More useful combinators are in "Data.TypedEncoding.Combinators.Validate"+runValidationChecks :: forall algs nms f c str . (Monad f) => Validations f nms algs c str -> Enc nms c str -> f (Enc ('[]::[Symbol]) c str)+runValidationChecks ZeroV enc0 = pure enc0+runValidationChecks (ConsV fn xs) enc = + let re :: f (Enc _ c str) = runValidation fn enc+ in re >>= runValidationChecks xs+++-- -- | At possibly big compilation cost, have compiler figure out algorithm names.+-- _runValidations :: forall nms f c str algs . (Monad f, algs ~ AlgNmMap nms) => Validations f nms algs c str -> Enc nms c str -> f (Enc ('[]::[Symbol]) c str)+-- _runValidations = runValidations @(AlgNmMap nms)
+ src/Data/TypedEncoding/Common/Util/TypeLits.hs view
@@ -0,0 +1,145 @@+++{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+++-- | TypeLits related utilities.+--+-- Lots of this could be avoided by adding @singletons@ as dependency.+--+-- Uses @symbols@ library for its ToList type family.+--+-- Currently this is spread out in different modules+--+-- * "Data.TypedEncoding.Common.Class.Util"+-- * "Data.TypedEncoding.Common.Types.SomeAnnotation"+--+-- (TODO) these will need to get consolidated.+module Data.TypedEncoding.Common.Util.TypeLits where++import GHC.TypeLits+import Data.Symbol.Ascii+++-- $setup+-- >>> :set -XScopedTypeVariables -XTypeFamilies -XKindSignatures -XDataKinds++type family AcceptEq (msg :: ErrorMessage) (c :: Ordering) :: Bool where+ AcceptEq _ EQ = True+ AcceptEq msg _ = TypeError msg++type family And (b1 :: Bool) (b2 :: Bool) :: Bool where+ And 'True 'True = 'True+ And _ _ = 'False++type family Or (b1 :: Bool) (b2 :: Bool) :: Bool where+ Or 'False 'False = 'False+ Or _ _ = 'True++type family Repeat (n :: Nat) (s :: Symbol) :: Symbol where+ Repeat 0 s = ""+ Repeat n s = AppendSymbol s (Repeat (n - 1) s)++type family Fst (s :: (k,h)) :: k where+ Fst ('(,) a _) = a++type family Dupl (s :: k) :: (k,k) where+ Dupl a = '(,) a a++-- | :kind! Concat (LDrop 6 (ToList "bool: \"r-ban:ff-ff\" | \"r-ban:ffff\""))+type family Concat (s :: [Symbol]) :: Symbol where+ Concat '[] = ""+ Concat (x ': xs) = AppendSymbol x (Concat xs)+++type family Drop (n :: Nat) (s :: Symbol) :: Symbol where+ Drop n s = Concat (LDrop n (ToList s))++-- TODO create TypeList.List module ?++-- | :kind! LDrop 6 (ToList "bool: \"r-ban:ff-ff\" | \"r-ban:ffff\"")+type family LDrop (n :: Nat) (s :: [k]) :: [k] where+ LDrop 0 s = s+ LDrop n '[] = '[]+ LDrop n (x ': xs) = LDrop (n - 1) xs +++-- type family DropVerify (n :: Nat) (s :: Symbol) (v :: Symbol) :: Symbol where+-- Drop n s v = Concat (LDrop n (ToList s))++-- type family DropVerify (n :: Nat) (s :: Symbol) (v :: Symbol) :: Symbol where+-- Drop n s v = Concat (LDrop n (ToList s))++-- | +-- :kind! Take 3 "123456"+type family Take (n :: Nat) (s :: Symbol) :: Symbol where+ Take n s = Concat (LTake n (ToList s))++-- | :kind! LTake 3 (ToList "123456")+type family LTake (n :: Nat) (s :: [k]) :: [k] where+ LTake 0 s = '[]+ LTake n '[] = '[]+ LTake n (x ': xs) = x ': LTake (n - 1) xs ++-- +-- :kind! TakeUntil "findme:blah" ":"+type family TakeUntil (s :: Symbol) (stop :: Symbol) :: Symbol where+ TakeUntil s stop = Concat (LTakeUntil (ToList s) stop)++type family LTakeUntil (s :: [Symbol]) (stop :: Symbol) :: [Symbol] where+ LTakeUntil '[] _ = '[]+ LTakeUntil (x ': xs) stop = LTakeUntilHelper (x ': LTakeUntil xs stop) (CmpSymbol x stop)++type family LTakeUntilHelper (s :: [Symbol]) (o :: Ordering) :: [Symbol] where+ LTakeUntilHelper '[] _ = '[]+ LTakeUntilHelper (x ': xs) 'EQ = '[]+ LTakeUntilHelper (x ': xs) _ = (x ': xs)+++type family Length (s :: Symbol) :: Nat where + Length x = LLengh (ToList x)++type family LLengh (s :: [k]) :: Nat where+ LLengh '[] = 0+ LLengh (x ': xs) = 1 + LLengh xs+++-- |+-- >>> :kind! LLast '["1","2","3"]+-- ...+-- = "3"+type family LLast (s :: [Symbol]) :: Symbol where+ LLast '[] = TypeError ('Text "Empty Symbol list not allowed")+ LLast '[x] = x+ LLast (_ ': xs) = LLast xs+++-- |+-- Concat (Snoc '["1","2","3"] "4") +-- ...+-- = "1234"+type family Snoc (s :: [k]) (t :: k) :: [k] where+ Snoc '[] x = '[x]+ Snoc (x ': xs) y = x ': Snoc xs y+++-- |+-- :kind! UnSnoc '["1","2","3"] +type family UnSnoc (s :: [k]) :: ([k], k) where+ UnSnoc '[] = TypeError ('Text "Empty list, no last element") + UnSnoc '[x] = '(,) '[] x+ UnSnoc (x ': xs) = UnSnocHelper x (UnSnoc xs)+++type family UnSnocHelper (s :: k) (t :: ([k], k)) :: ([k], k) where + UnSnocHelper y ('(,) xs x) = '(,) (y ': xs) x
src/Data/TypedEncoding/Conv/ByteString/Char8.hs view
@@ -9,9 +9,8 @@ module Data.TypedEncoding.Conv.ByteString.Char8 where import qualified Data.ByteString.Char8 as B8-import Data.TypedEncoding.Internal.Types.Enc (Enc, unsafeChangePayload)-import qualified Data.TypedEncoding.Internal.Util.TypeLits as Knds-import Data.TypedEncoding+import qualified Data.TypedEncoding.Common.Util.TypeLits as Knds+import Data.TypedEncoding.Instances.Support -- $setup -- >>> :set -XDataKinds -XTypeApplications -XOverloadedStrings@@ -31,7 +30,7 @@ -- ... -- -- >>> displ $ pack (unsafeSetPayload () "Hello" :: Enc '["r-bar", "r-ASCII"] () String)--- "MkEnc '[r-bar,r-ASCII] () (ByteString Hello)"+-- "Enc '[r-bar,r-ASCII] () (ByteString Hello)" pack :: (Knds.LLast xs ~ t, IsSuperset "r-ASCII" t ~ 'True) => Enc xs c String -> Enc xs c B8.ByteString pack = unsafeChangePayload B8.pack
src/Data/TypedEncoding/Conv/ByteString/Lazy/Char8.hs view
@@ -9,9 +9,8 @@ module Data.TypedEncoding.Conv.ByteString.Lazy.Char8 where import qualified Data.ByteString.Lazy.Char8 as BL8-import Data.TypedEncoding.Internal.Types.Enc (Enc, unsafeChangePayload)-import qualified Data.TypedEncoding.Internal.Util.TypeLits as Knds-import Data.TypedEncoding+import qualified Data.TypedEncoding.Common.Util.TypeLits as Knds+import Data.TypedEncoding.Instances.Support -- $setup -- >>> :set -XDataKinds -XTypeApplications -XOverloadedStrings
src/Data/TypedEncoding/Conv/Text.hs view
@@ -22,16 +22,23 @@ unpack :: Enc xs c T.Text -> Enc xs c String unpack = unsafeChangePayload T.unpack --- | Text is automatically @"r-UTF8"@ encoded+-- | +-- Text is automatically @"r-UTF8"@ encoded -- +-- Adding @"r-UTF8"@ annotation simply adds type level interpretion requirement that 'T.Text' is treated+-- as /UTF8/. The internals of 'T.Text' (currently /UTF-16/) are not relevant and @utf8Promote@ is implemented+-- as 'id'. This is not the same as encoding @Word8@ layouts into 'Char'-s.+-- This, in /typed-encoding/ terminology, would be @"enc-UTF8"@, not @"r-UTF8".+-- -- >>> displ $ utf8Promote $ toEncoding () ("text" :: T.Text)--- "MkEnc '[r-UTF8] () (Text text)"+-- "Enc '[r-UTF8] () (Text text)" utf8Promote :: Enc xs c T.Text -> Enc (Snoc xs "r-UTF8") c T.Text utf8Promote = withUnsafeCoerce id --- | For 'T.Text' @"r-UTF8"@ is redundant+-- | +-- For 'T.Text' @"r-UTF8"@ is redundant -- -- >>> displ . utf8Demote $ (unsafeSetPayload () "Hello" :: Enc '["r-UTF8"] () T.Text)--- "MkEnc '[] () (Text Hello)"+-- "Enc '[] () (Text Hello)" utf8Demote :: (UnSnoc xs ~ '(,) ys "r-UTF8") => Enc xs c T.Text -> Enc ys c T.Text utf8Demote = withUnsafeCoerce id
src/Data/TypedEncoding/Conv/Text/Encoding.hs view
@@ -27,13 +27,14 @@ -- >>> import qualified Data.ByteString.Char8 as B8 -- >>> import Data.Char -- >>> import Data.Either+-- >>> import Data.TypedEncoding -- >>> import Data.TypedEncoding.Conv.Text -- >>> let emptyUTF8B = unsafeSetPayload () "" :: Enc '["r-UTF8"] () B.ByteString -- >>> :{ -- instance Arbitrary (Enc '["r-UTF8"] () B.ByteString) where -- arbitrary = fmap (fromRight emptyUTF8B) -- . flip suchThat isRight --- . fmap (encodeFAll @(Either EncodeEx) @'["r-UTF8"] @(). toEncoding ()) $ arbitrary +-- . fmap (encodeFAll @'["r-UTF8"] @(Either EncodeEx) @(). toEncoding ()) $ arbitrary -- instance Arbitrary (Enc '["r-UTF8"] () T.Text) where -- arbitrary = fmap (unsafeSetPayload ()) -- arbitrary @@ -52,12 +53,12 @@ -- With given constraints 'decodeUtf8' and 'encodeUtf8' can be used on subsets of @"r-UTF8"@ -- -- >>> displ . decodeUtf8 $ (unsafeSetPayload () "Hello" :: Enc '["r-ASCII"] () B.ByteString)--- "MkEnc '[r-ASCII] () (Text Hello)"+-- "Enc '[r-ASCII] () (Text Hello)" -- -- "r-UTF8" is redundant: -- -- >>> displ . utf8Demote . decodeUtf8 $ (unsafeSetPayload () "Hello" :: Enc '["r-UTF8"] () B.ByteString)--- "MkEnc '[] () (Text Hello)"+-- "Enc '[] () (Text Hello)" -- -- @decodeUtf8@ and @encodeUtf8@ form isomorphism -- @@ -74,6 +75,6 @@ -- | -- >>> displ $ encodeUtf8 $ utf8Promote $ toEncoding () ("text" :: T.Text)--- "MkEnc '[r-UTF8] () (ByteString text)"+-- "Enc '[r-UTF8] () (ByteString text)" encodeUtf8 :: forall xs c t. (LLast xs ~ t, IsSuperset "r-UTF8" t ~ 'True) => Enc xs c T.Text -> Enc xs c B.ByteString encodeUtf8 = withUnsafe (fmap TE.encodeUtf8)
src/Data/TypedEncoding/Instances/Do/Sample.hs view
@@ -16,43 +16,51 @@ import qualified Data.Text as T import qualified Data.Text.Lazy as TL import qualified Data.ByteString as B--- import qualified Data.ByteString.Lazy as BL+ import Data.Char import Data.TypedEncoding.Instances.Support+import Data.TypedEncoding.Instances.Support.Unsafe +instance Applicative f => Encode f "do-UPPER" "do-UPPER" c T.Text where+ encoding = _implEncodingP T.toUpper -instance Applicative f => EncodeF f (Enc xs c T.Text) (Enc ("do-UPPER" ': xs) c T.Text) where- encodeF = implEncodeP T.toUpper-instance (RecreateErr f, Applicative f) => RecreateF f (Enc xs c T.Text) (Enc ("do-UPPER" ': xs) c T.Text) where- checkPrevF = implCheckPrevF (asRecreateErr @"do-UPPER" . (\t -> +instance (RecreateErr f, Applicative f) => Validate f "do-UPPER" "do-UPPER" c T.Text where+ validation = mkValidation $+ implTranF (asRecreateErr @"do-UPPER" . (\t -> let (g,b) = T.partition isUpper t in if T.null b then Right t else Left $ "Found not upper case chars " ++ T.unpack b) )-instance Applicative f => EncodeF f (Enc xs c TL.Text) (Enc ("do-UPPER" ': xs) c TL.Text) where- encodeF = implEncodeP TL.toUpper -instance Applicative f => EncodeF f (Enc xs c T.Text) (Enc ("do-lower" ': xs) c T.Text) where- encodeF = implEncodeP T.toLower -instance Applicative f => EncodeF f (Enc xs c TL.Text) (Enc ("do-lower" ': xs) c TL.Text) where- encodeF = implEncodeP TL.toLower -instance Applicative f => EncodeF f (Enc xs c T.Text) (Enc ("do-Title" ': xs) c T.Text) where- encodeF = implEncodeP T.toTitle -instance Applicative f => EncodeF f (Enc xs c TL.Text) (Enc ("do-Title" ': xs) c TL.Text) where- encodeF = implEncodeP TL.toTitle +instance Applicative f => Encode f "do-UPPER" "do-UPPER" c TL.Text where+ encoding = _implEncodingP TL.toUpper -instance Applicative f => EncodeF f (Enc xs c T.Text) (Enc ("do-reverse" ': xs) c T.Text) where- encodeF = implEncodeP T.reverse -instance Applicative f => EncodeF f (Enc xs c TL.Text) (Enc ("do-reverse" ': xs) c TL.Text) where- encodeF = implEncodeP TL.reverse ++instance Applicative f => Encode f "do-lower" "do-lower" c T.Text where+ encoding = _implEncodingP T.toLower ++instance Applicative f => Encode f "do-lower" "do-lower" c TL.Text where+ encoding = _implEncodingP TL.toLower ++instance Applicative f => Encode f "do-Title" "do-Title" c T.Text where+ encoding = _implEncodingP T.toTitle ++instance Applicative f => Encode f "do-Title" "do-Title" c TL.Text where+ encoding = _implEncodingP TL.toTitle ++instance Applicative f => Encode f "do-reverse" "do-reverse" c T.Text where+ encoding = _implEncodingP T.reverse +instance Applicative f => Encode f "do-reverse" "do-reverse" c TL.Text where+ encoding = _implEncodingP TL.reverse + newtype SizeLimit = SizeLimit {unSizeLimit :: Int} deriving (Eq, Show)-instance (HasA SizeLimit c, Applicative f) => EncodeF f (Enc xs c T.Text) (Enc ("do-size-limit" ': xs) c T.Text) where- encodeF = implEncodeP' (T.take . unSizeLimit . has @ SizeLimit) -instance (HasA SizeLimit c, Applicative f) => EncodeF f (Enc xs c B.ByteString) (Enc ("do-size-limit" ': xs) c B.ByteString) where- encodeF = implEncodeP' (B.take . unSizeLimit . has @ SizeLimit) +instance (HasA SizeLimit c, Applicative f) => Encode f "do-size-limit" "do-size-limit" c T.Text where+ encoding = _implEncodingConfP (T.take . unSizeLimit . has @ SizeLimit) +instance (HasA SizeLimit c, Applicative f) => Encode f "do-size-limit" "do-size-limit" c B.ByteString where+ encoding = _implEncodingConfP (B.take . unSizeLimit . has @ SizeLimit)
src/Data/TypedEncoding/Instances/Enc/Base64.hs view
@@ -4,12 +4,14 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-} -- | Defines /Base64/ encoding module Data.TypedEncoding.Instances.Enc.Base64 where import Data.TypedEncoding import Data.TypedEncoding.Instances.Support+import Data.TypedEncoding.Instances.Support.Unsafe import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL@@ -21,73 +23,18 @@ import qualified Data.ByteString.Base64 as B64 import qualified Data.ByteString.Base64.Lazy as BL64 --- import qualified Data.ByteString.Base64.URL as B64URL--- import qualified Data.ByteString.Base64.URL.Lazy as BL64URL + -- $setup--- >>> :set -XScopedTypeVariables -XKindSignatures -XMultiParamTypeClasses -XDataKinds -XPolyKinds -XPartialTypeSignatures -XFlexibleInstances+-- >>> :set -XOverloadedStrings -XScopedTypeVariables -XKindSignatures -XMultiParamTypeClasses -XDataKinds -XPolyKinds -XPartialTypeSignatures -XFlexibleInstances -XTypeApplications -- >>> import Test.QuickCheck -- >>> import Test.QuickCheck.Instances.Text() -- >>> import Test.QuickCheck.Instances.ByteString() -------------------- Conversions --+-- * Conversions ----------------- --- | --- DEPRECATED use 'Data.TypedEncoding.Conv.Text.Encoding.decodeUtf8'--- and 'Data.TypedEncoding.Conv.Text.utf8Demote'--- --- Will be removed in 0.3.x.x------ See warning in 'Data.TypedEncoding.Instances.Restriction.ASCII.byteString2TextS'-byteString2TextS :: Enc ("enc-B64" ': "r-UTF8" ': ys) c B.ByteString -> Enc ("enc-B64" ': ys) c T.Text -byteString2TextS = withUnsafeCoerce TE.decodeUtf8---- | --- DEPRECATED use 'Data.TypedEncoding.Conv.Text.Lazy.Encoding.decodeUtf8'--- and 'Data.TypedEncoding.Conv.Text.utf8Demote'------ Will be removed in 0.3.x.x------ See warning in 'Data.TypedEncoding.Instances.Restriction.ASCII.byteString2TextS'-byteString2TextL :: Enc ("enc-B64" ': "r-UTF8" ': ys) c BL.ByteString -> Enc ("enc-B64" ': ys) c TL.Text -byteString2TextL = withUnsafeCoerce TEL.decodeUtf8---- DEPRECATED use 'Data.TypedEncoding.Conv.Text.Encoding.encodeUtf8'--- and 'Data.TypedEncoding.Conv.Text.utf8Promote'--- --- Will be removed in 0.3.x.x-text2ByteStringS :: Enc ("enc-B64" ': ys) c T.Text -> Enc ("enc-B64" ': "r-UTF8" ': ys) c B.ByteString -text2ByteStringS = withUnsafeCoerce TE.encodeUtf8---- DEPRECATED use 'Data.TypedEncoding.Conv.Text.Lazy.Encoding.encodeUtf8'--- and 'Data.TypedEncoding.Conv.Text.utf8Promote'--- --- Will be removed in 0.3.x.x-text2ByteStringL :: Enc ("enc-B64" ': ys) c TL.Text -> Enc ("enc-B64" ': "r-UTF8" ': ys) c BL.ByteString -text2ByteStringL = withUnsafeCoerce TEL.encodeUtf8----- | B64 encoded bytestring can be converted to Text as "enc-B64-nontext" preventing it from --- being B64-decoded directly to Text-byteString2TextS' :: Enc ("enc-B64" ': ys) c B.ByteString -> Enc ("enc-B64-nontext" ': ys) c T.Text -byteString2TextS' = withUnsafeCoerce TE.decodeUtf8---- DEPRECATED -byteString2TextL' :: Enc ("enc-B64" ': ys) c BL.ByteString -> Enc ("enc-B64-nontext" ': ys) c TL.Text -byteString2TextL' = withUnsafeCoerce TEL.decodeUtf8---- DEPRECATED -text2ByteStringS' :: Enc ("enc-B64-nontext" ': ys) c T.Text -> Enc ("enc-B64" ': ys) c B.ByteString -text2ByteStringS' = withUnsafeCoerce TE.encodeUtf8---- DEPRECATED -text2ByteStringL' :: Enc ("enc-B64-nontext" ': ys) c TL.Text -> Enc ("enc-B64" ': ys) c BL.ByteString -text2ByteStringL' = withUnsafeCoerce TEL.encodeUtf8--- acceptLenientS :: Enc ("enc-B64-len" ': ys) c B.ByteString -> Enc ("enc-B64" ': ys) c B.ByteString acceptLenientS = withUnsafeCoerce (B64.encode . B64.decodeLenient) @@ -99,82 +46,110 @@ -- -- >>> let tstB64 = encodeAll . toEncoding () $ "Hello World" :: Enc '["enc-B64"] () B.ByteString -- >>> displ (flattenAs tstB64 :: Enc '["r-ASCII"] () B.ByteString)--- "MkEnc '[r-ASCII] () (ByteString SGVsbG8gV29ybGQ=)"+-- "Enc '[r-ASCII] () (ByteString SGVsbG8gV29ybGQ=)" instance FlattenAs "r-ASCII" "enc-B64-nontext" where instance FlattenAs "r-ASCII" "enc-B64" where --- DEPRECATED will be removed+-- |+-- This is not precise, actually /Base 64/ uses a subset of ASCII+-- and that would require a new definition @"r-B64"@. ----- dangerous, with new approach.--- Supersets are for "r-" types only-instance Superset "r-ASCII" "enc-B64-nontext" where-instance Superset "r-ASCII" "enc-B64" where+-- This instance likely to be changed / corrected in the future if @"r-B64"@ is defined.+--+-- >>> let tstB64 = encodeAll . toEncoding () $ "Hello World" :: Enc '["enc-B64"] () B.ByteString+-- >>> displ (_encodesInto @"r-ASCII" $ tstB64)+-- "Enc '[r-ASCII,enc-B64] () (ByteString SGVsbG8gV29ybGQ=)"+--+-- >>> displ (_encodesInto @"r-UTF8" $ tstB64)+-- "Enc '[r-UTF8,enc-B64] () (ByteString SGVsbG8gV29ybGQ=)"+--+-- @since 0.3.0.0+instance EncodingSuperset "enc-B64" where+ type EncSuperset "enc-B64" = "r-ASCII" --------------------- Encodings --------------------+-- * Encoders -instance Encodings (Either EncodeEx) xs grps c B.ByteString => Encodings (Either EncodeEx) ("enc-B64" ': xs) ("enc-B64" ': grps) c B.ByteString where- encodings = encodeFEncoder @(Either EncodeEx) @"enc-B64" @"enc-B64"+instance Applicative f => Encode f "enc-B64" "enc-B64" c B.ByteString where+ encoding = encB64B -instance Applicative f => EncodeF f (Enc xs c B.ByteString) (Enc ("enc-B64" ': xs) c B.ByteString) where- encodeF = implEncodeP B64.encode - --- | Effectful instance for corruption detection.+encB64B :: Applicative f => Encoding f "enc-B64" "enc-B64" c B.ByteString+encB64B = _implEncodingP B64.encode++instance Applicative f => Encode f "enc-B64" "enc-B64" c BL.ByteString where+ encoding = encB64BL++encB64BL :: Applicative f => Encoding f "enc-B64" "enc-B64" c BL.ByteString+encB64BL = _implEncodingP BL64.encode+++-- | This instance will likely be removed in future versions (performance concerns)+instance Applicative f => Encode f "enc-B64" "enc-B64" c T.Text where+ encoding = endB64T++-- | This function will likely be removed in future versions (performance concerns)+endB64T :: Applicative f => Encoding f "enc-B64" "enc-B64" c T.Text+endB64T = _implEncodingP (TE.decodeUtf8 . B64.encode . TE.encodeUtf8) ++-- * Decoders++instance (UnexpectedDecodeErr f, Applicative f) => Decode f "enc-B64" "enc-B64" c B.ByteString where+ decoding = decB64B++-- | Effectful decoding for corruption detection. -- This protocol is used, for example, in emails. -- It is a well known encoding and hackers will have no problem -- making undetectable changes, but error handling at this stage -- could verify that email was corrupted.-instance (UnexpectedDecodeErr f, Applicative f) => DecodeF f (Enc ("enc-B64" ': xs) c B.ByteString) (Enc xs c B.ByteString) where- decodeF = implDecodeF (asUnexpected @"enc-B64" . B64.decode) +decB64B :: (UnexpectedDecodeErr f, Applicative f) => Decoding f "enc-B64" "enc-B64" c B.ByteString+decB64B = _implDecodingF (asUnexpected @"enc-B64" . B64.decode) -instance (RecreateErr f, Applicative f) => RecreateF f (Enc xs c B.ByteString) (Enc ("enc-B64" ': xs) c B.ByteString) where- checkPrevF = implCheckPrevF (asRecreateErr @"enc-B64" . B64.decode) +instance (UnexpectedDecodeErr f, Applicative f) => Decode f "enc-B64" "enc-B64" c BL.ByteString where+ decoding = decB64BL -instance Applicative f => RecreateF f (Enc xs c B.ByteString) (Enc ("enc-B64-len" ': xs) c B.ByteString) where- checkPrevF = implTranP id+decB64BL :: (UnexpectedDecodeErr f, Applicative f) => Decoding f "enc-B64" "enc-B64" c BL.ByteString+decB64BL = _implDecodingF (asUnexpected @"enc-B64" . BL64.decode) -instance Applicative f => EncodeF f (Enc xs c BL.ByteString) (Enc ("enc-B64" ': xs) c BL.ByteString) where- encodeF = implEncodeP BL64.encode -instance (UnexpectedDecodeErr f, Applicative f) => DecodeF f (Enc ("enc-B64" ': xs) c BL.ByteString) (Enc xs c BL.ByteString) where- decodeF = implDecodeF (asUnexpected @"enc-B64" . BL64.decode)+-- Kept for now but performance issues -instance (RecreateErr f, Applicative f) => RecreateF f (Enc xs c BL.ByteString) (Enc ("enc-B64" ': xs) c BL.ByteString) where- checkPrevF = implCheckPrevF (asRecreateErr @"enc-B64" . BL64.decode) +-- | WARNING (performance)+instance (UnexpectedDecodeErr f, Applicative f) => Decode f "enc-B64" "enc-B64" c T.Text where+ decoding = decB64T -instance Applicative f => RecreateF f (Enc xs c BL.ByteString) (Enc ("enc-B64-len" ': xs) c BL.ByteString) where- checkPrevF = implTranP id+decB64T :: (UnexpectedDecodeErr f, Applicative f) => Decoding f "enc-B64" "enc-B64" c T.Text+decB64T = _implDecodingF (asUnexpected @"enc-B64" . fmap TE.decodeUtf8 . B64.decode . TE.encodeUtf8) +{-# WARNING decB64T "This method was not optimized for performance." #-} --- B64URL currently not supported--- instance Applicative f => EncodeF f (Enc xs c B.ByteString) (Enc ("enc-B64URL" ': xs) c B.ByteString) where--- encodeF = implEncodeP B64URL.encode --- instance DecodeF (Either String) (Enc ("enc-B64URL" ': xs) c B.ByteString) (Enc xs c B.ByteString) where--- decodeF = implDecodeF B64URL.decode --- instance DecodeF Identity (Enc ("enc-B64URL" ': xs) c B.ByteString) (Enc xs c B.ByteString) where--- decodeF = implTranP B64URL.decodeLenient +-- | WARNING (performance)+instance (UnexpectedDecodeErr f, Applicative f) => Decode f "enc-B64" "enc-B64" c TL.Text where+ decoding = decB64TL --- instance Applicative f => EncodeF f (Enc xs c BL.ByteString) (Enc ("enc-B64URL" ': xs) c BL.ByteString) where--- encodeF = implEncodeP BL64URL.encode --- instance DecodeF (Either String) (Enc ("enc-B64URL" ': xs) c BL.ByteString) (Enc xs c BL.ByteString) where--- decodeF = implDecodeF BL64URL.decode --- instance DecodeF Identity (Enc ("enc-B64URL" ': xs) c BL.ByteString) (Enc xs c BL.ByteString) where--- decodeF = implTranP BL64URL.decodeLenient +decB64TL :: (UnexpectedDecodeErr f, Applicative f) => Decoding f "enc-B64" "enc-B64" c TL.Text+decB64TL = _implDecodingF (asUnexpected @"enc-B64" . fmap TEL.decodeUtf8 . BL64.decode . TEL.encodeUtf8) +{-# WARNING decB64TL "This method was not optimized for performance." #-} -instance Applicative f => EncodeF f (Enc xs c T.Text) (Enc ("enc-B64" ': xs) c T.Text) where- encodeF = implEncodeP (TE.decodeUtf8 . B64.encode . TE.encodeUtf8) -instance (UnexpectedDecodeErr f, Applicative f) => DecodeF f (Enc ("enc-B64" ': xs) c T.Text) (Enc xs c T.Text) where- decodeF = implDecodeF (asUnexpected @"enc-B64" . fmap TE.decodeUtf8 . B64.decode . TE.encodeUtf8) +-- * Validation -instance (RecreateErr f, Applicative f) => RecreateF f (Enc xs c T.Text) (Enc ("enc-B64" ': xs) c T.Text) where- checkPrevF = implCheckPrevF (asRecreateErr @"enc-B64" . fmap TE.decodeUtf8 . B64.decode . TE.encodeUtf8) +instance (RecreateErr f, Applicative f) => Validate f "enc-B64" "enc-B64" c B.ByteString where+ validation = validFromDec decB64B -instance Applicative f => EncodeF f (Enc xs c TL.Text) (Enc ("enc-B64" ': xs) c TL.Text) where- encodeF = implEncodeP (TEL.decodeUtf8 . BL64.encode . TEL.encodeUtf8) +instance (RecreateErr f, Applicative f) => Validate f "enc-B64" "enc-B64" c BL.ByteString where+ validation = validFromDec decB64BL -instance (UnexpectedDecodeErr f, Applicative f) => DecodeF f (Enc ("enc-B64" ': xs) c TL.Text) (Enc xs c TL.Text) where- decodeF = implDecodeF (asUnexpected @"enc-B64" . fmap TEL.decodeUtf8 . BL64.decode . TEL.encodeUtf8) +instance (RecreateErr f, Applicative f) => Validate f "enc-B64" "enc-B64" c T.Text where+ validation = validFromDec decB64T -instance (RecreateErr f, Applicative f) => RecreateF f (Enc xs c TL.Text) (Enc ("enc-B64" ': xs) c TL.Text) where- checkPrevF = implCheckPrevF (asRecreateErr @"enc-B64" . fmap TEL.decodeUtf8 . BL64.decode . TEL.encodeUtf8) +instance (RecreateErr f, Applicative f) => Validate f "enc-B64" "enc-B64" c TL.Text where+ validation = validFromDec decB64TL++-- | Lenient decoding does not fail+instance Applicative f => Validate f "enc-B64-len" "enc-B64-len" c B.ByteString where+ validation = mkValidation $ implTranP id++-- | Lenient decoding does not fail+instance Applicative f => Validate f "enc-B64-len" "enc-B64-len" c BL.ByteString where+ validation = mkValidation $ implTranP id+++
src/Data/TypedEncoding/Instances/Restriction/ASCII.hs view
@@ -15,36 +15,27 @@ -- -- >>> :set -XOverloadedStrings -XMultiParamTypeClasses -XDataKinds -- >>> encodeFAll . toEncoding () $ "Hello World" :: Either EncodeEx (Enc '["r-ASCII"] () T.Text)--- Right (MkEnc Proxy () "Hello World")+-- Right (UnsafeMkEnc Proxy () "Hello World") -- -- >>> encodeFAll . toEncoding () $ "\194\160" :: Either EncodeEx (Enc '["r-ASCII"] () T.Text) -- Left (EncodeEx "r-ASCII" (NonAsciiChar '\194')) module Data.TypedEncoding.Instances.Restriction.ASCII where import Data.TypedEncoding.Instances.Support--import Data.Proxy--import qualified Data.ByteString as B-import qualified Data.ByteString.Lazy as BL-import qualified Data.Text as T-import qualified Data.Text.Lazy as TL-import qualified Data.Text.Encoding as TE -import qualified Data.Text.Lazy.Encoding as TEL -import qualified Data.List as L--import qualified Data.ByteString.Char8 as B8-import qualified Data.ByteString.Lazy.Char8 as BL8+import Data.TypedEncoding.Common.Class.Util.StringConstraints -import Data.Char import Data.TypedEncoding.Internal.Util (explainBool)-import Data.TypedEncoding.Unsafe (withUnsafe)-import Control.Arrow+import Data.Char + -- $setup -- >>> :set -XDataKinds -XTypeApplications+-- >>> import qualified Data.Text as T+-- >>> import qualified Data.ByteString as B+-- >>> import qualified Data.ByteString.Char8 as B8 -- >>> import Test.QuickCheck -- >>> import Test.QuickCheck.Instances.ByteString()+-- >>> import Data.TypedEncoding -- >>> :{ -- instance Arbitrary (Enc '["r-ASCII"] () B.ByteString) where -- arbitrary = fmap (unsafeSetPayload ()) @@ -55,138 +46,38 @@ -------------------- Conversions ------------------------ |--- DEPRECATED use 'Data.TypedEncoding.Conv.Text.Encoding.decodeUtf8'--- --- Will be removed in 0.3.x.x------ This is not type safe, for example, would allow converting------ @Enc `["r-ASCII", "enc-B64"] c B.ByteString@ containing B64 encoded binary --- to @Enc `["r-ASCII", "enc-B64"] c T.Text@ and which then could be decoded causing --- unexpected error. --byteString2TextS :: Enc ("r-ASCII" ': ys) c B.ByteString -> Enc ("r-ASCII" ': ys) c T.Text -byteString2TextS = withUnsafe (fmap TE.decodeUtf8)---- | --- DEPRECATED use 'Data.TypedEncoding.Conv.Text.Lazy.Encoding.decodeUtf8'--- --- Will be removed in 0.3.x.x------ see 'byteString2TextS'-byteString2TextL :: Enc ("r-ASCII" ': ys) c BL.ByteString -> Enc ("r-ASCII" ': ys) c TL.Text -byteString2TextL = withUnsafe (fmap TEL.decodeUtf8)---- | --- DEPRECATED use 'Data.TypedEncoding.Conv.Text.Encoding.encodeUtf8'--- --- Will be removed in 0.3.x.x----text2ByteStringS :: Enc ("r-ASCII" ': ys) c T.Text -> Enc ("r-ASCII" ': ys) c B.ByteString -text2ByteStringS = withUnsafe (fmap TE.encodeUtf8)---- | --- DEPRECATED use 'Data.TypedEncoding.Conv.Text.Lazy.Encoding.encodeUtf8'--- --- Will be removed in 0.3.x.x----text2ByteStringL :: Enc ("r-ASCII" ': ys) c TL.Text -> Enc ("r-ASCII" ': ys) c BL.ByteString -text2ByteStringL = withUnsafe (fmap TEL.encodeUtf8)----- | allow to treat ASCII encodings as UTF8 forgetting about B64 encoding--- --- UTF-8 is backward compatible on first 128 characters using just one byte to store it.--- --- Payload does not change when @ASCII@ only strings are encoded to @UTF8@ in types like @ByteString@.------ >>> let Right tstAscii = encodeFAll . toEncoding () $ "Hello World" :: Either EncodeEx (Enc '["r-ASCII"] () T.Text)--- >>> displ (inject @ "r-UTF8" tstAscii)--- "MkEnc '[r-UTF8] () (Text Hello World)"-instance Superset "r-UTF8" "r-ASCII" where---- type instance IsSuperset "r-UTF8" "r-ASCII" = True --- type instance IsSuperset "r-ASCII" "r-ASCII" = True ------------------- -- Encodings -- ----------------- newtype NonAsciiChar = NonAsciiChar Char deriving (Eq, Show) -prxyAscii = Proxy :: Proxy "r-ASCII"--instance EncodeF (Either EncodeEx) (Enc xs c Char) (Enc ("r-ASCII" ': xs) c Char) where- encodeF = implEncodeF_ prxyAscii (\c -> explainBool NonAsciiChar (c, isAscii c)) -instance Applicative f => DecodeF f (Enc ("r-ASCII" ': xs) c Char) (Enc xs c Char) where- decodeF = implTranP id --instance Encodings (Either EncodeEx) xs grps c String => Encodings (Either EncodeEx) ("r-ASCII" ': xs) ("r-ASCII" ': grps) c String where- encodings = encodeFEncoder @(Either EncodeEx) @"r-ASCII" @"r-ASCII"--instance EncodeF (Either EncodeEx) (Enc xs c String) (Enc ("r-ASCII" ': xs) c String) where- encodeF = implEncodeF_ prxyAscii (encodeImpl L.partition L.head L.null)-instance (RecreateErr f, Applicative f) => RecreateF f (Enc xs c String) (Enc ("r-ASCII" ': xs) c String) where- checkPrevF = implCheckPrevF (asRecreateErr @"r-ASCII" . encodeImpl L.partition L.head L.null)-instance Applicative f => DecodeF f (Enc ("r-ASCII" ': xs) c String) (Enc xs c String) where- decodeF = implTranP id --instance Encodings (Either EncodeEx) xs grps c T.Text => Encodings (Either EncodeEx) ("r-ASCII" ': xs) ("r-ASCII" ': grps) c T.Text where- encodings = encodeFEncoder @(Either EncodeEx) @"r-ASCII" @"r-ASCII"--instance EncodeF (Either EncodeEx) (Enc xs c T.Text) (Enc ("r-ASCII" ': xs) c T.Text) where- encodeF = implEncodeF_ prxyAscii (encodeImpl T.partition T.head T.null)-instance (RecreateErr f, Applicative f) => RecreateF f (Enc xs c T.Text) (Enc ("r-ASCII" ': xs) c T.Text) where- checkPrevF = implCheckPrevF (asRecreateErr @"r-ASCII" . encodeImpl T.partition T.head T.null)-instance Applicative f => DecodeF f (Enc ("r-ASCII" ': xs) c T.Text) (Enc xs c T.Text) where- decodeF = implTranP id +-- * Encoding -instance Encodings (Either EncodeEx) xs grps c TL.Text => Encodings (Either EncodeEx) ("r-ASCII" ': xs) ("r-ASCII" ': grps) c TL.Text where- encodings = encodeFEncoder @(Either EncodeEx) @"r-ASCII" @"r-ASCII"+instance Encode (Either EncodeEx) "r-ASCII" "r-ASCII" c Char where+ encoding = encASCIIChar -instance EncodeF (Either EncodeEx) (Enc xs c TL.Text) (Enc ("r-ASCII" ': xs) c TL.Text) where- encodeF = implEncodeF_ prxyAscii (encodeImpl TL.partition TL.head TL.null)-instance (RecreateErr f, Applicative f) => RecreateF f (Enc xs c TL.Text) (Enc ("r-ASCII" ': xs) c TL.Text) where - checkPrevF = implCheckPrevF (asRecreateErr @"r-ASCII" . encodeImpl TL.partition TL.head TL.null)-instance Applicative f => DecodeF f (Enc ("r-ASCII" ': xs) c TL.Text) (Enc xs c TL.Text) where- decodeF = implTranP id +instance Char8Find str => Encode (Either EncodeEx) "r-ASCII" "r-ASCII" c str where+ encoding = encASCII -instance Encodings (Either EncodeEx) xs grps c B.ByteString => Encodings (Either EncodeEx) ("r-ASCII" ': xs) ("r-ASCII" ': grps) c B.ByteString where- encodings = encodeFEncoder @(Either EncodeEx) @"r-ASCII" @"r-ASCII"+encASCIIChar :: Encoding (Either EncodeEx) "r-ASCII" "r-ASCII" c Char +encASCIIChar = _implEncodingEx (\c -> explainBool NonAsciiChar (c, isAscii c)) -instance EncodeF (Either EncodeEx) (Enc xs c B.ByteString) (Enc ("r-ASCII" ': xs) c B.ByteString) where- encodeF = implEncodeF_ prxyAscii (encodeImpl (\p -> B8.filter p &&& B8.filter (not . p)) B8.head B8.null)-instance (RecreateErr f, Applicative f) => RecreateF f (Enc xs c B.ByteString) (Enc ("r-ASCII" ': xs) c B.ByteString) where- checkPrevF = implCheckPrevF (asRecreateErr @"r-ASCII" . encodeImpl (\p -> B8.filter p &&& B8.filter (not . p)) B8.head B8.null)-instance Applicative f => DecodeF f (Enc ("r-ASCII" ': xs) c B.ByteString) (Enc xs c B.ByteString) where- decodeF = implTranP id +encASCII :: Char8Find str => Encoding (Either EncodeEx) "r-ASCII" "r-ASCII" c str+encASCII = _implEncodingEx @"r-ASCII" encImpl -instance Encodings (Either EncodeEx) xs grps c BL.ByteString => Encodings (Either EncodeEx) ("r-ASCII" ': xs) ("r-ASCII" ': grps) c BL.ByteString where- encodings = encodeFEncoder @(Either EncodeEx) @"r-ASCII" @"r-ASCII"+encImpl :: Char8Find str => str -> Either NonAsciiChar str+encImpl str = case find (not . isAscii) str of + Nothing -> Right str+ Just ch -> Left $ NonAsciiChar ch -instance EncodeF (Either EncodeEx) (Enc xs c BL.ByteString) (Enc ("r-ASCII" ': xs) c BL.ByteString) where- encodeF = implEncodeF_ prxyAscii (encodeImpl (\p -> BL8.filter p &&& BL8.filter (not . p)) BL8.head BL8.null)-instance (RecreateErr f, Applicative f) => RecreateF f (Enc xs c BL.ByteString) (Enc ("r-ASCII" ': xs) c BL.ByteString) where- checkPrevF = implCheckPrevF (asRecreateErr @"r-ASCII" . encodeImpl (\p -> BL8.filter p &&& BL8.filter (not . p)) BL8.head BL8.null)-instance Applicative f => DecodeF f (Enc ("r-ASCII" ': xs) c BL.ByteString) (Enc xs c BL.ByteString) where- decodeF = implTranP id +-- * Decoding +instance (Applicative f) => Decode f "r-ASCII" "r-ASCII" c str where+ decoding = decAnyR+ +instance (Char8Find str, RecreateErr f, Applicative f) => Validate f "r-ASCII" "r-ASCII" () str where+ validation = validR encASCII -encodeImpl :: - ((Char -> Bool) -> a -> (a, a))- -> (a -> Char)- -> (a -> Bool)- -> a- -> Either NonAsciiChar a-encodeImpl partitionf headf nullf t = - let (tascii, nonascii) = partitionf isAscii t - in if nullf nonascii - then Right tascii- else Left . NonAsciiChar $ headf nonascii -- tst = encodeFAll . toEncoding () $ "Hello World" :: Either EncodeEx (Enc '["r-ASCII"] () T.Text) -- tst2 = encodeFAll . toEncoding () $ "\194\160" :: Either EncodeEx (Enc '["r-ASCII"] () T.Text)
+ src/Data/TypedEncoding/Instances/Restriction/Bool.hs view
@@ -0,0 +1,429 @@++{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE AllowAmbiguousTypes #-}++-- |+-- Boolean algebra on encodings+--+-- @since 0.2.1.0+-- +-- /(Experimental, early alpha development stage)/ This module was not converted to v0.3 style yet.+--+-- == Grammar+-- +-- Simple grammar requires boolean terms to be included in parentheses+--+-- @+-- bool[BinaryOp]:(leftTerm)(rightTerm)+-- bool[UnaryOp]:(term)+-- @+--+-- Expected behavior is described next to the corresponding combinator.++module Data.TypedEncoding.Instances.Restriction.Bool where +++import GHC.TypeLits+import Data.Proxy+import Data.Symbol.Ascii++import Data.TypedEncoding+import Data.TypedEncoding.Instances.Support+import Data.TypedEncoding.Instances.Support.Unsafe++++-- $setup+-- >>> :set -XOverloadedStrings -XMultiParamTypeClasses -XDataKinds -XTypeApplications+-- >>> import qualified Data.Text as T+-- >>> import Data.TypedEncoding.Instances.Restriction.Misc()+++-- |+-- See examples in 'encBoolOrRight''+encBoolOrLeft :: forall f s t xs c str . (+ BoolOpIs s "or" ~ 'True+ -- IsBoolOr s ~ 'True+ , Functor f+ , LeftTerm s ~ t+ ) => (Enc xs c str -> f (Enc (t ': xs) c str)) -> Enc xs c str -> f (Enc (s ': xs) c str) +encBoolOrLeft = implChangeAnn ++-- |+-- See examples in 'encBoolOrRight''+encBoolOrLeft' :: forall f s t xs c str . (+ BoolOpIs s "or" ~ 'True+ -- IsBoolOr s ~ 'True+ , Functor f+ , LeftTerm s ~ t+ , Encode f t t c str+ ) => Enc xs c str -> f (Enc (s ': xs) c str) +encBoolOrLeft' = encBoolOrLeft'' @t++encBoolOrLeft'' :: forall alg f s t xs c str . (+ BoolOpIs s "or" ~ 'True+ -- IsBoolOr s ~ 'True+ , Functor f+ , LeftTerm s ~ t+ , Encode f t alg c str+ ) => Enc xs c str -> f (Enc (s ': xs) c str) +encBoolOrLeft'' = encBoolOrLeft (encodeF' @alg @t @xs @f)++-- |+-- +encBoolOrRight :: forall f s t xs c str . (+ BoolOpIs s "or" ~ 'True+ -- IsBoolOr s ~ 'True+ , Functor f+ , RightTerm s ~ t+ ) => (Enc xs c str -> f (Enc (t ': xs) c str)) -> Enc xs c str -> f (Enc (s ': xs) c str) +encBoolOrRight = implChangeAnn ++-- |+-- >>> :{ +-- let tst1, tst2, tst3 :: Either EncodeEx (Enc '["boolOr:(r-Word8-decimal)(r-Int-decimal)"] () T.Text)+-- tst1 = encBoolOrLeft' . toEncoding () $ "212" +-- tst2 = encBoolOrRight' . toEncoding () $ "1000000" +-- tst3 = encBoolOrLeft' . toEncoding () $ "1000000"+-- :}+-- +-- >>> tst1 +-- Right (UnsafeMkEnc Proxy () "212")+--+-- >>> tst2+-- Right (UnsafeMkEnc Proxy () "1000000")+--+-- >>> tst3+-- Left (EncodeEx "r-Word8-decimal" ("Payload does not satisfy format Word8-decimal: 1000000"))+encBoolOrRight' :: forall f s t xs c str . (+ BoolOpIs s "or" ~ 'True+ -- IsBoolOr s ~ 'True+ , Functor f+ , RightTerm s ~ t+ , Encode f t t c str+ ) => Enc xs c str -> f (Enc (s ': xs) c str) +encBoolOrRight' = encBoolOrRight'' @t++encBoolOrRight'' :: forall alg f s t xs c str . (+ BoolOpIs s "or" ~ 'True+ -- IsBoolOr s ~ 'True+ , Functor f+ , RightTerm s ~ t+ , Encode f t alg c str+ ) => Enc xs c str -> f (Enc (s ': xs) c str) +encBoolOrRight'' = encBoolOrRight (encodeF' @alg @t @xs @f) ++encBoolAnd :: forall f s t1 t2 xs c str . (+ BoolOpIs s "and" ~ 'True + , KnownSymbol s+ -- IsBoolAnd s ~ 'True+ , f ~ Either EncodeEx+ , Eq str+ , LeftTerm s ~ t1+ , RightTerm s ~ t2+ ) => + (Enc xs c str -> f (Enc (t1 ': xs) c str)) + -> (Enc xs c str -> f (Enc (t2 : xs) c str)) + -> Enc xs c str -> f (Enc (s ': xs) c str) +encBoolAnd fnl fnr en0 = + let + eent1 = fnl en0+ eent2 = fnr en0+ p = (Proxy :: Proxy s)+ in + case (eent1, eent2) of+ (Right ent1, Right ent2) -> + if getPayload ent1 == getPayload ent2+ then Right . withUnsafeCoerce id $ ent1 + else Left $ EncodeEx p "Left - right encoding do not match" + (_, _) -> mergeErrs (emptyEncErr p) (mergeEncodeEx p) eent1 eent2++-- |+-- @"boolOr:(enc1)(enc2)"@ contains strings that encode the same way under both encodings.+-- for example @"boolOr:(r-UPPER)(r-lower)"@ valid elements would include @"123-34"@ but not @"abc"@+--+-- >>> :{+-- let tst1, tst2 :: Either EncodeEx (Enc '["boolAnd:(r-Word8-decimal)(r-Int-decimal)"] () T.Text)+-- tst1 = encBoolAnd' . toEncoding () $ "234"+-- tst2 = encBoolAnd' . toEncoding () $ "100000"+-- :}+-- +-- >>> tst1+-- Right (UnsafeMkEnc Proxy () "234")+-- >>> tst2+-- Left (EncodeEx "r-Word8-decimal" ("Payload does not satisfy format Word8-decimal: 100000"))+encBoolAnd' :: forall s t1 t2 xs c str . (+ BoolOpIs s "and" ~ 'True + , KnownSymbol s+ -- IsBoolAnd s ~ 'True+ , Eq str+ , LeftTerm s ~ t1+ , RightTerm s ~ t2+ , Encode (Either EncodeEx) t1 t1 c str+ , Encode (Either EncodeEx) t2 t2 c str+ ) => Enc xs c str -> Either EncodeEx (Enc (s ': xs) c str) +encBoolAnd' = encBoolAnd'' @t1 @t2++encBoolAnd'' :: forall al1 al2 s t1 t2 xs c str . (+ BoolOpIs s "and" ~ 'True + , KnownSymbol s+ -- IsBoolAnd s ~ 'True+ , Eq str+ , LeftTerm s ~ t1+ , RightTerm s ~ t2+ , Encode (Either EncodeEx) t1 al1 c str+ , Encode (Either EncodeEx) t2 al2 c str+ ) => Enc xs c str -> Either EncodeEx (Enc (s ': xs) c str) +encBoolAnd'' = encBoolAnd (encodeF' @al1 @t1 @xs) (encodeF' @al2 @t2 @xs) +++-- tst1, tst2 :: Either EncodeEx (Enc '["boolNot:(r-Word8-decimal)"] () T.Text)+-- tst1 = encBoolNot' . toEncoding () $ "334"+-- tst2 = encBoolNot' . toEncoding () $ "127"++encBoolNot :: forall s t xs c str . (+ BoolOpIs s "not" ~ 'True+ , KnownSymbol s+ , FirstTerm s ~ t+ , Restriction t + ) => (Enc xs c str -> Either EncodeEx (Enc (t ': xs) c str)) -> Enc xs c str -> Either EncodeEx (Enc (s ': xs) c str) +encBoolNot fn en0 = + let + een = fn en0+ p = (Proxy :: Proxy s)+ pt = (Proxy :: Proxy t)+ in + case een of+ Left _ -> Right . withUnsafeCoerce id $ en0+ Right _ -> Left $ EncodeEx p $ "Encoding " ++ symbolVal pt ++ " succeeded" + ++-- |+-- >>> :{+-- let tst1, tst2 :: Either EncodeEx (Enc '["boolNot:(r-Word8-decimal)"] () T.Text)+-- tst1 = encBoolNot' . toEncoding () $ "334"+-- tst2 = encBoolNot' . toEncoding () $ "127"+-- :}+--+-- >>> tst1+-- Right (UnsafeMkEnc Proxy () "334")+-- >>> tst2+-- Left (EncodeEx "boolNot:(r-Word8-decimal)" ("Encoding r-Word8-decimal succeeded"))+encBoolNot' :: forall s t xs c str . (+ BoolOpIs s "not" ~ 'True+ , KnownSymbol s+ , FirstTerm s ~ t+ , KnownSymbol t+ , Restriction t + , Encode (Either EncodeEx) t t c str+ ) => Enc xs c str -> Either EncodeEx (Enc (s ': xs) c str) +encBoolNot' = encBoolNot'' @t++encBoolNot'' :: forall alg s t xs c str . (+ BoolOpIs s "not" ~ 'True+ , KnownSymbol s+ , FirstTerm s ~ t+ , Restriction t + , Encode (Either EncodeEx) t alg c str+ ) => Enc xs c str -> Either EncodeEx (Enc (s ': xs) c str) +encBoolNot'' = encBoolNot (encodeF' @alg @t @xs)++-- | +-- Decodes boolean expression if all leaves are @"r-"@+decBoolR :: forall f xs t s c str . (+ NestedR s ~ 'True+ , Applicative f+ ) => Enc (s ': xs) c str -> f (Enc xs c str) ++decBoolR = implTranP id +++recWithEncBoolR :: forall (s :: Symbol) xs c str . (NestedR s ~ 'True) + => (Enc xs c str -> Either EncodeEx (Enc (s ': xs) c str)) + -> Enc xs c str -> Either RecreateEx (Enc (s ': xs) c str)+recWithEncBoolR = unsafeRecWithEncR++unsafeRecWithEncR :: forall (s :: Symbol) xs c str .+ (Enc xs c str -> Either EncodeEx (Enc (s ': xs) c str)) + -> Enc xs c str -> Either RecreateEx (Enc (s ': xs) c str)+unsafeRecWithEncR fn = either (Left . encToRecrEx) Right . fn++-- * Type family based parser ++-- |+-- >>> :kind! BoolOpIs "boolAnd:(someenc)(otherenc)" "and"+-- ...+-- = 'True+type family BoolOpIs (s :: Symbol) (op :: Symbol) :: Bool where+ BoolOpIs s op = AcceptEq ('Text "Invalid bool encoding " ':<>: ShowType s ) (CmpSymbol (BoolOp s) op)++-- | +-- This works fast with @!kind@ but is much slower in declaration +-- :kind! BoolOp "boolOr:()()"+type family BoolOp (s :: Symbol) :: Symbol where+ BoolOp s = Fst (BoolOpHelper (Dupl s))+ -- ToLower (TakeUntil (Drop 4 s) ":")++type family BoolOpHelper (x :: (Symbol, Symbol)) :: (Symbol, Bool) where+ BoolOpHelper ('(,) s1 s2) = '(,) (ToLower (TakeUntil (Drop 4 s1) ":")) ( AcceptEq ('Text "Invalid bool encoding " ':<>: ShowType s2 ) (CmpSymbol "bool" (Take 4 s2)))+++type family IsBool (s :: Symbol) :: Bool where+ IsBool s = AcceptEq ('Text "Not boolean encoding " ':<>: ShowType s ) (CmpSymbol "bool" (Take 4 s))++-- |+-- +-- >>> :kind! NestedR "boolOr:(r-abc)(r-cd)"+-- ...+-- = 'True+--+-- >>> :kind! NestedR "boolOr:(boolAnd:(r-ab)(r-ac))(boolNot:(r-cd))"+-- ...+-- = 'True+--+-- >>> :kind! NestedR "boolOr:(boolAnd:(r-ab)(ac))(boolNot:(r-cd))"+-- ...+-- ... (TypeError ...)+-- ...+type family NestedR (s :: Symbol) :: Bool where+ NestedR "" = 'True -- RightTerm, LeftTerm return "" on "r-"+ NestedR s = Or (IsROrEmpty s) + (And (IsBool s) (And (NestedR (LeftTerm s)) (NestedR (RightTerm s)))) ++-- Value level check+--+-- nestedR :: String -> Bool+-- nestedR s = isROrEmpty s || isBool s && (nestedR (leftTerm s) && nestedR (rightTerm s))++-- isBool ('b' :'o' : 'o' : _) = True+-- isBool _ = False++-- isROrEmpty "" = True+-- isROrEmpty ('r' : '-' : _) = True+-- isROrEmpty _ = False+++type family FirstTerm (s :: Symbol) :: Symbol where+ FirstTerm s = LeftTerm s++-- | +-- returns "" for unary operator+type family SecondTerm (s :: Symbol) :: Symbol where+ SecondTerm s = RightTerm s+++-- |+-- >>> :kind! LeftTerm "boolSomeOp:(agag)(222)"+-- ...+-- = "agag"+--+-- >>> :kind! LeftTerm "r-Int-decimal"+-- ...+-- = ""+type family LeftTerm (s :: Symbol) :: Symbol where+ LeftTerm s = Concat (LDropLast( LTakeFstParen (LParenCnt (ToList s))))++-- |+-- >>> :kind! RightTerm "boolSomeOp:(agag)(222)"+-- ...+-- = "222"+--+-- >>> :kind! RightTerm "r-Int-decimal"+-- ...+-- = ""+type family RightTerm (s :: Symbol) :: Symbol where+ RightTerm s = Concat (LDropLast (LTakeSndParen 0 (LParenCnt (ToList s))))+++type family LDropLast (s :: [Symbol]) :: [Symbol] where+ LDropLast '[] = '[]+ LDropLast '[x] = '[]+ LDropLast (x ': xs) = x ': LDropLast xs+++type family LParenCnt (s :: [Symbol]) :: [(Symbol, Nat)] where+ LParenCnt '[] = '[]+ LParenCnt ("(" ': xs) = LParenCntHelper ('(,) "(" 'Decr) (LParenCnt xs) -- '(,) "(" (LParenCntFstCnt (LParenCnt xs) - 1) ': LParenCnt xs+ LParenCnt (")" ': xs) = LParenCntHelper ('(,) ")" 'Incr) (LParenCnt xs)+ LParenCnt (x ': xs) = LParenCntHelper ('(,) x 'NoChng) (LParenCnt xs)++data Adjust = Incr | Decr | NoChng++type family AdjHelper (a :: Adjust) (n :: Nat) :: Nat where+ AdjHelper 'Incr n = n + 1+ AdjHelper 'Decr 0 = 0+ AdjHelper 'Decr n = n - 1+ AdjHelper 'NoChng n = n++type family LParenCntHelper (s :: (Symbol, Adjust)) (sx :: [(Symbol, Nat)]) :: [(Symbol, Nat)] where+ LParenCntHelper ('(,) x k) '[] = '(,) x (AdjHelper k 0) ': '[]+ LParenCntHelper ('(,) x k) ('(,) c i : xs) = '(,) x (AdjHelper k i) ': '(,) c i ': xs+++type family LTakeFstParen (si :: [(Symbol, Nat)]) :: [Symbol] where + LTakeFstParen '[] = '[]+ LTakeFstParen ('(,) _ 0 ': xs) = LTakeFstParen xs+ LTakeFstParen ('(,) ")" 1 ': _) = '[")"]+ LTakeFstParen ('(,) a p ': xs) = a ': LTakeFstParen xs+++type family LTakeSndParen (n :: Nat) (si :: [(Symbol, Nat)]) :: [Symbol] where+ LTakeSndParen _ '[] = '[]+ LTakeSndParen 0 ('(,) ")" 1 ': xs) = LTakeSndParen 1 xs+ LTakeSndParen 1 ('(,) _ 0 : xs) = LTakeSndParen 1 xs+ LTakeSndParen 0 ('(,) _ _ ': xs) = LTakeSndParen 0 xs+ LTakeSndParen 1 ('(,) a _ ': xs) = a : LTakeSndParen 1 xs+ LTakeSndParen n _ = '[]++-- -- * (Example) Value level equivalend of type family parsers+++-- -- map snd . parenCnt $ "((AGA)(bcd))(123) "+-- -- dropLast . takeFstParen . parenCnt $ "((AGA)(bcd))(123)" +-- -- dropLast . takeSndParen 0 . parenCnt $ "((AGA)(bcd))(123)" ++-- leftTerm :: String -> String+-- leftTerm = dropLast . takeFstParen . parenCnt++-- rightTerm :: String -> String+-- rightTerm = dropLast . takeSndParen 0 . parenCnt++-- parenCnt :: String -> [(Char, Int)]+-- parenCnt [] = []+-- parenCnt ('(' : xs) = parenCntHelper ('(', -1) (parenCnt xs) --('(', parenCntFstCnt (parenCnt xs) - 1) : parenCnt xs+-- parenCnt (')' : xs) = parenCntHelper (')', 1) (parenCnt xs) -- (')', parenCntFstCnt (parenCnt xs) + 1) : parenCnt xs+-- parenCnt (x : xs) = parenCntHelper (x, 0) (parenCnt xs) -- (x, parenCntFstCnt (parenCnt xs) ) : parenCnt xs+++-- parenCntHelper :: (Char, Int) -> [(Char, Int)] -> [(Char, Int)]+-- parenCntHelper (x, k) [] = [(x,k)]+-- parenCntHelper (x, k) ((c,i) : xs) = (x, i + k): (c,i) : xs+++-- takeFstParen :: [(Char, Int)] -> [Char]+-- takeFstParen [] = []+-- takeFstParen ((_, 0) : xs) = takeFstParen xs+-- takeFstParen ((')', 1) : _) = [')']+-- takeFstParen ((a, p) : xs) = a : takeFstParen xs++-- takeSndParen :: Int -> [(Char, Int)] -> [Char]+-- takeSndParen _ [] = []+-- takeSndParen 0 ((')', 1) : xs) = takeSndParen 1 xs+-- takeSndParen 1 ((_, 0) : xs) = takeSndParen 1 xs+-- takeSndParen 0 ((_, _) : xs) = takeSndParen 0 xs+-- takeSndParen 1 ((a, _) : xs) = a : takeSndParen 1 xs+-- takeSndParen _ _ = []++-- dropLast :: [Char] -> [Char]+-- dropLast [] = []+-- dropLast [x] = []+-- dropLast (x:xs) = x : dropLast xs+
+ src/Data/TypedEncoding/Instances/Restriction/BoundedAlphaNums.hs view
@@ -0,0 +1,122 @@++{-# LANGUAGE MultiParamTypeClasses #-}+-- {-# LANGUAGE PolyKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+-- {-# LANGUAGE KindSignatures #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE ConstraintKinds #-}++-- | +-- Restrictions @"r-ban:"@ cover commonly used fixed (short) size strings with restricted+-- characters such as GUID, credit card numbers, etc. +-- +-- Alphanumeric chars are ordered: @0-9@ followed by @A-Z@,+-- followed by @a-z@. Annotation specifies upper character bound. +-- Any non alpha numeric characters are considered fixed delimiters+-- and need to be present exactly as specified.+-- For example @"r-ban:999-99-9999"@ could be used to describe SSN numbers,+-- @"r-ban:FFFF" would describe strings consisting of 4 hex digits.+--+-- This is a simple implementation that converts to @String@, should be used+-- only with short length data.+--+--+-- @since 0.2.1.0+module Data.TypedEncoding.Instances.Restriction.BoundedAlphaNums where +++import GHC.TypeLits+import qualified Data.List as L+import Data.Char+import Data.Proxy+import Data.Either++import Data.TypedEncoding.Common.Util.TypeLits+import Data.TypedEncoding.Common.Class.IsStringR+import Data.TypedEncoding.Instances.Support++-- $setup+-- >>> :set -XOverloadedStrings -XMultiParamTypeClasses -XDataKinds -XTypeApplications+-- >>> import qualified Data.Text as T+-- >>> import Data.TypedEncoding++-- better compilation errors?+type family IsBan (s :: Symbol) :: Bool where+ IsBan s = AcceptEq ('Text "Not ban restriction encoding " ':<>: ShowType s ) (CmpSymbol (TakeUntil s ":") "r-ban")++type Ban s = (KnownSymbol s, IsBan s ~ 'True)++type instance IsSupersetOpen "r-ASCII" "r-ban" xs = 'True+++instance (Ban s, Algorithm s "r-ban", IsStringR str) => Encode (Either EncodeEx) s "r-ban" c str where+ encoding = encFBan+++-- |+-- >>> runEncoding' encFBan . toEncoding () $ "C59F9FB7-4621-44D9-9020-CE37BF6E2BD1" :: Either EncodeEx (Enc '["r-ban:FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF"] () T.Text)+-- Right (UnsafeMkEnc Proxy () "C59F9FB7-4621-44D9-9020-CE37BF6E2BD1")+-- +-- >>> recreateFAll' @'["r-ban"] . toEncoding () $ "211-22-9934" :: Either RecreateEx (Enc '["r-ban:999-99-9999"] () T.Text)+-- Right (UnsafeMkEnc Proxy () "211-22-9934")+encFBan :: forall s c str .+ (+ IsStringR str+ , Ban s+ , Algorithm s "r-ban"+ ) => + Encoding (Either EncodeEx) s "r-ban" c str+encFBan = _implEncodingEx @s (verifyBoundedAlphaNum (Proxy :: Proxy s)) ++++-- * Decoding++instance (KnownSymbol s, Restriction s, Algorithm s "r-ban", Applicative f) => Decode f s "r-ban" c str where+ decoding = decAnyR_+++-- * Validation++instance (KnownSymbol s , Ban s, Algorithm s "r-ban", IsStringR str, RecreateErr f, Applicative f) => Validate f s "r-ban" c str where+ validation = validFromEnc' @"r-ban" encFBan+++-- * Implementation++-- |+-- >>> verifyBoundedAlphaNum (Proxy :: Proxy "r-ban:FF-FF") (T.pack "12-3E")+-- Right "12-3E"+-- >>> verifyBoundedAlphaNum (Proxy :: Proxy "r-ban:FF-FF") (T.pack "1G-3E")+-- Left "'G' not boulded by 'F'"+-- >>> verifyBoundedAlphaNum (Proxy :: Proxy "r-ban:FF-FF") (T.pack "13G3E")+-- Left "'G' not matching '-'"+-- >>> verifyBoundedAlphaNum (Proxy :: Proxy "r-ban:FF-FF") (T.pack "13-234")+-- Left "Input list has wrong size expecting 5 but length \"13-234\" == 6"+verifyBoundedAlphaNum :: forall s str . (KnownSymbol s, IsStringR str) => Proxy s -> str -> Either String str+verifyBoundedAlphaNum p str = + if pattl == inpl + then case lefts match of+ (e: _) -> Left e+ _ -> Right str+ else Left $ "Input list has wrong size expecting " ++ show pattl ++ " but length " ++ show input ++ " == " ++ show inpl + where + patt = L.drop (L.length ("r-ban:" :: String)) . symbolVal $ p+ input = toString str+ pattl = L.length patt+ inpl = L.length input + + match = L.zipWith fn input patt+++ fn ci cp = case (isAlphaNum ci, isAlphaNum cp, ci <= cp, ci == cp) of+ (True, True, True, _) -> Right ()+ (_, _, _, True) -> Right ()+ (_, True, _, False) -> Left $ show ci ++ " not boulded by " ++ show cp+ (_, False, _, False) -> Left $ show ci ++ " not matching " ++ show cp
− src/Data/TypedEncoding/Instances/Restriction/Common.hs
@@ -1,45 +0,0 @@-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE AllowAmbiguousTypes #-}-{-# LANGUAGE ScopedTypeVariables #-}---- | Common /restriction/ "r-" instances-module Data.TypedEncoding.Instances.Restriction.Common where--import Data.Word---- import qualified Data.ByteString as B--- import qualified Data.ByteString.Lazy as BL--- import qualified Data.Text as T--- import qualified Data.Text.Lazy as TL--import Data.TypedEncoding.Internal.Class.IsStringR-import Data.TypedEncoding.Internal.Instances.Combinators-import Data.TypedEncoding.Instances.Support---- $setup--- >>> import qualified Data.Text as T---instance (IsStringR str) => EncodeF (Either EncodeEx) (Enc xs c str) (Enc ("r-Word8-decimal" ': xs) c str) where- encodeF = implEncodeF @"r-Word8-decimal" (verifyWithRead @Word8 "Word8-decimal")-instance (IsStringR str, RecreateErr f, Applicative f) => RecreateF f (Enc xs c str) (Enc ("r-Word8-decimal" ': xs) c str) where- checkPrevF = implCheckPrevF (asRecreateErr @"r-Word8-decimal" . verifyWithRead @Word8 "Word8-decimal")-instance (IsStringR str, Applicative f) => DecodeF f (Enc ("r-Word8-decimal" ': xs) c str) (Enc xs c str) where- decodeF = implTranP id --instance (IsStringR str) => EncodeF (Either EncodeEx) (Enc xs c str) (Enc ("r-Int-decimal" ': xs) c str) where- encodeF = implEncodeF @"r-Int-decimal" (verifyWithRead @Int "Int-decimal")-instance (IsStringR str, RecreateErr f, Applicative f) => RecreateF f (Enc xs c str) (Enc ("r-Int-decimal" ': xs) c str) where- checkPrevF = implCheckPrevF (asRecreateErr @"r-Int-decimal" . verifyWithRead @Int "Int-decimal")-instance (IsStringR str, Applicative f) => DecodeF f (Enc ("r-Int-decimal" ': xs) c str) (Enc xs c str) where- decodeF = implTranP id ----- tst :: T.Text--- tst = fromString $ show $ (fromString $ "123" :: T.Text)
+ src/Data/TypedEncoding/Instances/Restriction/Misc.hs view
@@ -0,0 +1,61 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}++-- | Common /restriction/ "r-" instances+module Data.TypedEncoding.Instances.Restriction.Misc where++import Data.Word+import Data.Functor.Identity+import Data.String+import Data.Proxy+import Text.Read++import Data.TypedEncoding.Common.Class.IsStringR+import Data.TypedEncoding.Instances.Support++-- $setup+-- >>> import qualified Data.Text as T++instance IsString str => ToEncString Identity "r-()" "r-()" () str where+ toEncF _ = Identity $ UnsafeMkEnc Proxy () (fromString "()")+++instance (IsStringR str) => Encode (Either EncodeEx) "r-Word8-decimal" "r-Word8-decimal" c str where+ encoding = encWord8Dec+instance (Applicative f) => Decode f "r-Word8-decimal" "r-Word8-decimal" c str where+ decoding = decAnyR+instance (IsStringR str) => Validate (Either RecreateEx) "r-Word8-decimal" "r-Word8-decimal" c str where+ validation = validR encWord8Dec+instance IsString str => ToEncString Identity "r-Word8-decimal" "r-Word8-decimal" Word8 str where+ toEncF i = Identity $ UnsafeMkEnc Proxy () (fromString . show $ i)+instance (IsStringR str, UnexpectedDecodeErr f, Applicative f) => FromEncString f "r-Word8-decimal" "r-Word8-decimal" Word8 str where+ fromEncF = asUnexpected @ "r-Word8-decimal" . readEither . toString . getPayload++encWord8Dec :: (IsStringR str) => Encoding (Either EncodeEx) "r-Word8-decimal" "r-Word8-decimal" c str+encWord8Dec = _implEncodingEx (verifyWithRead @Word8 "Word8-decimal")+++instance (IsStringR str) => Encode (Either EncodeEx) "r-Int-decimal" "r-Int-decimal" c str where+ encoding = encIntDec+instance (Applicative f) => Decode f "r-Int-decimal" "r-Int-decimal" c str where+ decoding = decAnyR+instance (IsStringR str) => Validate (Either RecreateEx) "r-Int-decimal" "r-Int-decimal" c str where+ validation = validR encIntDec+instance IsString str => ToEncString Identity "r-Int-decimal" "r-Int-decimal" Int str where+ toEncF i = Identity $ UnsafeMkEnc Proxy () (fromString . show $ i)++encIntDec :: (IsStringR str) => Encoding (Either EncodeEx) "r-Int-decimal" "r-Int-decimal" c str+encIntDec = _implEncodingEx (verifyWithRead @Int "Int-decimal")++++++-- All instances of "r-Word8-decimal" are @Show@ / @Read@ based
src/Data/TypedEncoding/Instances/Restriction/UTF8.hs view
@@ -16,95 +16,43 @@ import Data.TypedEncoding.Instances.Support import Data.Proxy-import GHC.TypeLits import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL-import qualified Data.Text as T-import qualified Data.Text.Lazy as TL import qualified Data.Text.Encoding as TE import qualified Data.Text.Lazy.Encoding as TEL ---- import qualified Data.ByteString.Char8 as B8--- import qualified Data.ByteString.Lazy.Char8 as BL8- import Data.Either + -- $setup -- >>> :set -XScopedTypeVariables -XKindSignatures -XMultiParamTypeClasses -XDataKinds -XPolyKinds -XPartialTypeSignatures -XFlexibleInstances -XTypeApplications -- >>> import Test.QuickCheck -- >>> import Test.QuickCheck.Instances.Text() -- >>> import Test.QuickCheck.Instances.ByteString()+-- >>> import Data.TypedEncoding -- >>> import Data.TypedEncoding.Internal.Util (proxiedId)--- >>> :{--- >>> instance Arbitrary (Enc '["r-UTF8"] () B.ByteString) where --- arbitrary = fmap (fromRight (emptyUTF8B ())) +-- >>> let emptyUTF8B = unsafeSetPayload () "" :: Enc '["r-UTF8"] () B.ByteString +-- >>> :{ +-- instance Arbitrary (Enc '["r-UTF8"] () B.ByteString) where +-- arbitrary = fmap (fromRight emptyUTF8B) -- . flip suchThat isRight --- . fmap (encodeFAll @(Either EncodeEx) @'["r-UTF8"] @(). toEncoding ()) $ arbitrary +-- . fmap (encodeFAll @'["r-UTF8"] @(Either EncodeEx) @(). toEncoding ()) $ arbitrary -- :} --- | DEPRECATED will be removed in 0.3 --- empty string is valid utf8-emptyUTF8B :: c -> Enc '["r-UTF8"] c B.ByteString-emptyUTF8B c = unsafeSetPayload c "" --------------------- Conversions -------------------- --- |--- | DEPRECATED will be removed in 0.3 --- --- use 'Data.TypedEncoding.Conv.Text.Lazy.Encoding.encodeUtf8'--- and 'Data.TypedEncoding.Conv.Text.utf8Promote'-text2ByteStringS :: Enc ys c T.Text -> Enc ("r-UTF8" ': ys) c B.ByteString-text2ByteStringS = withUnsafeCoerce TE.encodeUtf8---- | --- DEPRECATED------ | DEPRECATED will be removed in 0.3 --- --- use 'Data.TypedEncoding.Conv.Text.Lazy.Encoding.decodeUtf8'--- and 'Data.TypedEncoding.Conv.Text.utf8Demote'------ See warning in 'Data.TypedEncoding.Instances.Restriction.ASCII.byteString2TextS'------ Type-safer version of Data.Text.Encoding.decodeUtf8----byteString2TextS :: Enc ("r-UTF8" ': ys) c B.ByteString -> Enc ys c T.Text -byteString2TextS = withUnsafeCoerce TE.decodeUtf8---- | To be removed-txtBsSIdProp :: Proxy (ys :: [Symbol]) -> Enc ys c T.Text -> Enc ys c T.Text-txtBsSIdProp _ = byteString2TextS . text2ByteStringS ---- To be removed-bsTxtIdProp :: Proxy (ys :: [Symbol]) -> Enc ("r-UTF8" ': ys) c B.ByteString -> Enc ("r-UTF8" ': ys) c B.ByteString-bsTxtIdProp _ = text2ByteStringS . byteString2TextS---- DEPRECATED see above-text2ByteStringL :: Enc ys c TL.Text -> Enc ("r-UTF8" ': ys) c BL.ByteString-text2ByteStringL = withUnsafeCoerce TEL.encodeUtf8---- DEPRECATED------ See warning in 'Data.TypedEncoding.Instances.Restriction.ASCII.byteString2TextS'-byteString2TextL :: Enc ("r-UTF8" ': ys) c BL.ByteString -> Enc ys c TL.Text -byteString2TextL = withUnsafeCoerce TEL.decodeUtf8- ----------------- -- Encodings -- ----------------- prxyUtf8 = Proxy :: Proxy "r-UTF8" --- TODO these are quick and dirty+-- TODO these may need rethinking (performance) -- | UTF8 encodings are defined for ByteString only as that would not make much sense for Text -- -- >>> encodeFAll . toEncoding () $ "\xc3\xb1" :: Either EncodeEx (Enc '["r-UTF8"] () B.ByteString)--- Right (MkEnc Proxy () "\195\177")+-- Right (UnsafeMkEnc Proxy () "\195\177") -- -- >>> encodeFAll . toEncoding () $ "\xc3\x28" :: Either EncodeEx (Enc '["r-UTF8"] () B.ByteString) -- Left (EncodeEx "r-UTF8" (Cannot decode byte '\xc3': Data.Text.Internal.Encoding.decodeUtf8: Invalid UTF-8 stream))@@ -112,28 +60,32 @@ -- Following test uses 'verEncoding' helper that checks that bytes are encoded as Right iff they are valid UTF8 bytes -- -- prop> \(b :: B.ByteString) -> verEncoding b (fmap (fromEncoding . decodeAll . proxiedId (Proxy :: Proxy (Enc '["r-UTF8"] _ _))) . (encodeFAll :: _ -> Either EncodeEx _). toEncoding () $ b)+instance Encode (Either EncodeEx) "r-UTF8" "r-UTF8" c B.ByteString where+ encoding = encUTF8B -instance Encodings (Either EncodeEx) xs grps c B.ByteString => Encodings (Either EncodeEx) ("r-UTF8" ': xs) ("r-UTF8" ': grps) c B.ByteString where- encodings = encodeFEncoder @(Either EncodeEx) @"r-UTF8" @"r-UTF8"+instance Encode (Either EncodeEx) "r-UTF8" "r-UTF8" c BL.ByteString where+ encoding = encUTF8BL :: Encoding (Either EncodeEx) "r-UTF8" "r-UTF8" c BL.ByteString +encUTF8B :: Encoding (Either EncodeEx) "r-UTF8" "r-UTF8" c B.ByteString+encUTF8B = _implEncodingEx (fmap TE.encodeUtf8 . TE.decodeUtf8')+{-# WARNING encUTF8B "This method was not optimized for performance." #-} -instance EncodeF (Either EncodeEx) (Enc xs c B.ByteString) (Enc ("r-UTF8" ': xs) c B.ByteString) where- encodeF = implEncodeF_ prxyUtf8 (fmap TE.encodeUtf8 . TE.decodeUtf8')-instance (RecreateErr f, Applicative f) => RecreateF f (Enc xs c B.ByteString) (Enc ("r-UTF8" ': xs) c B.ByteString) where- checkPrevF = implCheckPrevF (asRecreateErr @"r-UTF8" . fmap TE.encodeUtf8 . TE.decodeUtf8')-instance Applicative f => DecodeF f (Enc ("r-UTF8" ': xs) c B.ByteString) (Enc xs c B.ByteString) where- decodeF = implTranP id +encUTF8BL :: Encoding (Either EncodeEx) "r-UTF8" "r-UTF8" c BL.ByteString+encUTF8BL = _implEncodingEx (fmap TEL.encodeUtf8 . TEL.decodeUtf8')+{-# WARNING encUTF8BL "This method was not optimized for performance." #-} -instance Encodings (Either EncodeEx) xs grps c BL.ByteString => Encodings (Either EncodeEx) ("r-UTF8" ': xs) ("r-UTF8" ': grps) c BL.ByteString where- encodings = encodeFEncoder @(Either EncodeEx) @"r-UTF8" @"r-UTF8"+-- * Decoding -instance EncodeF (Either EncodeEx) (Enc xs c BL.ByteString) (Enc ("r-UTF8" ': xs) c BL.ByteString) where- encodeF = implEncodeF_ prxyUtf8 (fmap TEL.encodeUtf8 . TEL.decodeUtf8')-instance (RecreateErr f, Applicative f) => RecreateF f (Enc xs c BL.ByteString) (Enc ("r-UTF8" ': xs) c BL.ByteString) where- checkPrevF = implCheckPrevF (asRecreateErr @"r-UTF8" . fmap TEL.encodeUtf8 . TEL.decodeUtf8')-instance Applicative f => DecodeF f (Enc ("r-UTF8" ': xs) c BL.ByteString) (Enc xs c BL.ByteString) where- decodeF = implTranP id +instance (Applicative f) => Decode f "r-UTF8" "r-UTF8" c str where+ decoding = decAnyR++instance (RecreateErr f, Applicative f) => Validate f "r-UTF8" "r-UTF8" c B.ByteString where+ validation = validR encUTF8B++instance (RecreateErr f, Applicative f) => Validate f "r-UTF8" "r-UTF8" c BL.ByteString where+ validation = validR encUTF8BL+ --- Utilities ---
src/Data/TypedEncoding/Instances/Support.hs view
@@ -4,16 +4,29 @@ -- Contains typical things needed when implementing -- encoding, decoding, recreate, or type to string conversions. module Data.TypedEncoding.Instances.Support (+ module Data.TypedEncoding.Instances.Support -- * Types- module Data.TypedEncoding.Internal.Types+ , module Data.TypedEncoding.Common.Types -- * Classes- , module Data.TypedEncoding.Internal.Class+ , module Data.TypedEncoding.Common.Class -- * Combinators- , module Data.TypedEncoding.Internal.Instances.Combinators+ , module Data.TypedEncoding.Instances.Support.Common+ , module Data.TypedEncoding.Instances.Support.Helpers+ , module Data.TypedEncoding.Instances.Support.Encode+ , module Data.TypedEncoding.Instances.Support.Decode+ , module Data.TypedEncoding.Instances.Support.Validate+ , module Data.TypedEncoding.Combinators.Unsafe -- * Type level conveniences- , module Data.TypedEncoding.Internal.Util.TypeLits+ , module Data.TypedEncoding.Common.Util.TypeLits ) where-import Data.TypedEncoding.Internal.Types-import Data.TypedEncoding.Internal.Class -import Data.TypedEncoding.Internal.Instances.Combinators -import Data.TypedEncoding.Internal.Util.TypeLits ++import Data.TypedEncoding.Instances.Support.Common +import Data.TypedEncoding.Instances.Support.Helpers +import Data.TypedEncoding.Instances.Support.Encode +import Data.TypedEncoding.Instances.Support.Decode +import Data.TypedEncoding.Instances.Support.Validate +import Data.TypedEncoding.Common.Types+import Data.TypedEncoding.Common.Class +import Data.TypedEncoding.Common.Util.TypeLits+import Data.TypedEncoding.Combinators.Unsafe +
+ src/Data/TypedEncoding/Instances/Support/Common.hs view
@@ -0,0 +1,36 @@++{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE TypeApplications #-}++-- | Exports for instance creation.+-- +-- Contains typical things needed when implementing+-- encoding, decoding, recreate, or type to string conversions.+module Data.TypedEncoding.Instances.Support.Common where++import Data.TypedEncoding.Instances.Support.Unsafe+import Data.TypedEncoding.Common.Types+import Data.Proxy++-- $setup+-- >>> :set -XOverloadedStrings -XMultiParamTypeClasses -XDataKinds -XTypeApplications+++-- * Decoding ++-- | Universal decoding for all "r-" types+decAnyR :: forall r f c str . (Restriction r, Applicative f) => Decoding f r r c str+decAnyR = decAnyR' @r @r++decAnyR' :: forall alg r f c str . (Restriction r, Applicative f) => Decoding f r alg c str+decAnyR' = UnsafeMkDecoding Proxy (implTranP id) ++decAnyR_ :: forall r f c str alg . (Restriction r, Algorithm r alg, Applicative f) => Decoding f r alg c str+decAnyR_ = mkDecoding $ implTranP id+
+ src/Data/TypedEncoding/Instances/Support/Decode.hs view
@@ -0,0 +1,39 @@++{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+-- {-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE FlexibleContexts #-}++-- | v0.2 style decoding combinators+module Data.TypedEncoding.Instances.Support.Decode where++import Data.TypedEncoding.Instances.Support.Unsafe+import Data.TypedEncoding.Common.Types.Decoding+import Data.Proxy+import Data.TypedEncoding.Common.Types++++-- * Compiler figure out algorithm, these appear fast enough ++_implDecodingF :: forall nm f c str . Functor f => (str -> f str) -> Decoding f nm (AlgNm nm) c str+_implDecodingF f = mkDecoding $ implTranF f++_implDecodingConfF :: forall nm f c str . Functor f => (c -> str -> f str) -> Decoding f nm (AlgNm nm) c str+_implDecodingConfF f = mkDecoding $ implTranF' f+++-- * Assume @alg ~ nm@ or explicit @alg@ ++implDecodingF :: forall nm f c str . Functor f => (str -> f str) -> Decoding f nm nm c str+implDecodingF = implDecodingF' @nm @nm++implDecodingF' :: forall alg nm f c str . Functor f => (str -> f str) -> Decoding f nm alg c str+implDecodingF' f = UnsafeMkDecoding Proxy $ implTranF f+++
+ src/Data/TypedEncoding/Instances/Support/Encode.hs view
@@ -0,0 +1,63 @@++{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+-- {-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE FlexibleContexts #-}+++module Data.TypedEncoding.Instances.Support.Encode where++import Data.TypedEncoding.Instances.Support.Unsafe+import Data.TypedEncoding.Common.Types.Enc+import Data.Proxy+import Data.TypedEncoding.Common.Types+import GHC.TypeLits+++-- * Compiler figure out algorithm, these appear fast enough ++_implEncodingP :: forall nm f c str . Applicative f => (str -> str) -> Encoding f nm (AlgNm nm) c str+_implEncodingP f = _mkEncoding $ implTranF (pure . f)++_implEncodingConfP :: forall nm f c str . Applicative f => (c -> str -> str) -> Encoding f nm (AlgNm nm) c str+_implEncodingConfP f = _mkEncoding $ implTranF' (\c -> pure . f c)++_implEncodingEx :: forall nm err c str . (KnownSymbol nm, Show err) => (str -> Either err str) -> Encoding (Either EncodeEx) nm (AlgNm nm) c str+_implEncodingEx f = _mkEncoding $ implTranF (either (Left . EncodeEx p) Right . f) + where+ p = Proxy :: Proxy nm++_implEncodingEncodeEx :: forall nm c str . (KnownSymbol nm) => (str -> Either EncodeEx str) -> Encoding (Either EncodeEx) nm (AlgNm nm) c str+_implEncodingEncodeEx f = _mkEncoding $ implTranF f +++_implEncodingConfEx :: forall nm err c str . (KnownSymbol nm, Show err) => (c -> str -> Either err str) -> Encoding (Either EncodeEx) nm (AlgNm nm) c str+_implEncodingConfEx f = _mkEncoding $ implTranF' (\c -> either (Left . EncodeEx p) Right . f c) + where+ p = Proxy :: Proxy nm++++-- * Assume @alg ~ nm@ or explicit @alg@ ++implEncodingP :: forall nm f c str . Applicative f => (str -> str) -> Encoding f nm nm c str+implEncodingP f = UnsafeMkEncoding Proxy $ implTranF (pure . f)++implEncodingEx :: forall nm err c str . (KnownSymbol nm, Show err) => (str -> Either err str) -> Encoding (Either EncodeEx) nm nm c str+implEncodingEx = implEncodingEx' @nm @nm++implEncodingEx' :: forall alg nm err c str . (KnownSymbol nm, Show err) => (str -> Either err str) -> Encoding (Either EncodeEx) nm alg c str+implEncodingEx' f = UnsafeMkEncoding Proxy $ implTranF (either (Left . EncodeEx p) Right . f) + where+ p = Proxy :: Proxy nm++implEncodingEncodeEx' :: forall alg nm c str . (KnownSymbol nm) => (str -> Either EncodeEx str) -> Encoding (Either EncodeEx) nm alg c str+implEncodingEncodeEx' f = UnsafeMkEncoding Proxy $ implTranF f + where+ p = Proxy :: Proxy nm++
+ src/Data/TypedEncoding/Instances/Support/Helpers.hs view
@@ -0,0 +1,112 @@++{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+-- {-# LANGUAGE AllowAmbiguousTypes #-}+-- {-# LANGUAGE TypeApplications #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE FlexibleContexts #-}++-- | Support for mostly creating instances ToEncString and FromEncString conversions++module Data.TypedEncoding.Instances.Support.Helpers where++import Data.String+import Data.Proxy+import Text.Read+import Data.TypedEncoding.Common.Types+import Data.TypedEncoding.Combinators.Unsafe+import Data.TypedEncoding.Common.Class.IsStringR +import GHC.TypeLits+++-- $setup+-- >>> :set -XTypeApplications+-- >>> import qualified Data.Text as T+-- >>> import Data.Word+++-- * Composite encodings from 'Foldable' 'Functor' types++-- | allows to fold payload in Enc to create another Enc, assumes homogeneous input encodings.+-- This yields not a type safe code, better implementation code should use fixed size+-- dependently typed @Vect n@ or some @HList@ like foldable.+foldEnc :: forall (xs2 :: [Symbol]) (xs1 :: [Symbol]) f c s1 s2 + . (Foldable f, Functor f) + => c -> (s1 -> s2 -> s2) -> s2 -> f (Enc xs1 c s1) -> Enc xs2 c s2+foldEnc c f sinit = unsafeSetPayload c . foldr f sinit . fmap getPayload ++-- | Similar to 'foldEnc', assumes that destination payload has @IsString@ instance and uses @""@ as base case. +foldEncStr :: forall (xs2 :: [Symbol]) (xs1 :: [Symbol]) f c s1 s2 + . (Foldable f, Functor f, IsString s2) + => c -> (s1 -> s2 -> s2) -> f (Enc xs1 c s1) -> Enc xs2 c s2+foldEncStr c f = foldEnc c f ""++-- | Similar to 'foldEnc', works with untyped 'CheckedEnc'+foldCheckedEnc :: forall (xs2 :: [Symbol]) f c s1 s2 + . (Foldable f, Functor f) + => c -> ([EncAnn] -> s1 -> s2 -> s2) -> s2 -> f (CheckedEnc c s1) -> Enc xs2 c s2+foldCheckedEnc c f sinit = unsafeSetPayload c . foldr (uncurry f) sinit . fmap getCheckedEncPayload++-- | Similar to 'foldEncStr', works with untyped 'CheckedEnc'+foldCheckedEncStr :: forall (xs2 :: [Symbol]) f c s1 s2 . (Foldable f, Functor f, IsString s2) => c -> ([EncAnn] -> s1 -> s2 -> s2) -> f (CheckedEnc c s1) -> Enc xs2 c s2+foldCheckedEncStr c f = foldCheckedEnc c f ""+++-- * Composite encoding: Recreate and Encode helpers++-- | Splits composite payload into homogenious chunks+splitPayload :: forall (xs2 :: [Symbol]) (xs1 :: [Symbol]) c s1 s2 . + (s1 -> [s2]) + -> Enc xs1 c s1 + -> [Enc xs2 c s2]+splitPayload f (UnsafeMkEnc _ c s1) = map (UnsafeMkEnc Proxy c) (f s1)+ +-- | Untyped version of 'splitPayload'+splitSomePayload :: forall c s1 s2 . + ([EncAnn] -> s1 -> [([EncAnn], s2)]) + -> CheckedEnc c s1 + -> [CheckedEnc c s2]+splitSomePayload f (UnsafeMkCheckedEnc ann1 c s1) = map (\(ann2, s2) -> UnsafeMkCheckedEnc ann2 c s2) (f ann1 s1)+++-- * Utility combinators ++-- | sometimes show . read is not identity, eg. Word8:+--+-- >>> read "256" :: Word8+-- 0+--+-- >>> verifyWithRead @Word8 "Word8-decimal" (T.pack "256")+-- Left "Payload does not satisfy format Word8-decimal: 256"+--+-- >>> verifyWithRead @Word8 "Word8-decimal" (T.pack "123")+-- Right "123"+verifyWithRead :: forall a str . (IsStringR str, Read a, Show a) => String -> str -> Either String str+verifyWithRead msg x = + let s = toString x+ a :: Maybe a = readMaybe s+ check = (show <$> a) == Just s + in if check+ then Right x+ else Left $ "Payload does not satisfy format " ++ msg ++ ": " ++ s +++-- | Convenience function for checking if @str@ decodes without error+-- using @enc@ encoding markers and decoders that can pick decoder based+-- on that marker+verifyDynEnc :: forall s str err1 err2 enc a. (KnownSymbol s, Show err1, Show err2) => + Proxy s -- ^ proxy defining encoding annotation+ -> (Proxy s -> Either err1 enc) -- ^ finds encoding marker @enc@ for given annotation or fails+ -> (enc -> str -> Either err2 a) -- ^ decoder based on @enc@ marker+ -> str -- ^ input+ -> Either EncodeEx str+verifyDynEnc p findenc decoder str = + do+ enc <- asEncodeEx p . findenc $ p+ case decoder enc str of+ Left err -> Left $ EncodeEx p err+ Right r -> Right str++
+ src/Data/TypedEncoding/Instances/Support/Unsafe.hs view
@@ -0,0 +1,35 @@++{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+-- {-# LANGUAGE AllowAmbiguousTypes #-}+-- {-# LANGUAGE TypeApplications #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE FlexibleContexts #-}++-- | Direct use of these methods is discouraged.+-- Use "Data.TypedEncoding.Instances.Support.Encode" or "Data.TypedEncoding.Instances.Support.Decode" +module Data.TypedEncoding.Instances.Support.Unsafe where++import Data.Proxy+import Data.TypedEncoding.Common.Types+import Data.TypedEncoding.Combinators.Unsafe++++implTranF :: Functor f => (str -> f str) -> Enc enc1 conf str -> f (Enc enc2 conf str)+implTranF f = implTranF' (const f)++implTranF' :: Functor f => (conf -> str -> f str) -> Enc enc1 conf str -> f (Enc enc2 conf str)+implTranF' f (UnsafeMkEnc _ conf str) = UnsafeMkEnc Proxy conf <$> f conf str++implTranP :: Applicative f => (str -> str) -> Enc enc1 conf str -> f (Enc enc2 conf str)+implTranP f = implTranF' (\c -> pure . f)++implTranP' :: Applicative f => (conf -> str -> str) -> Enc enc1 conf str -> f (Enc enc2 conf str)+implTranP' f = implTranF' (\c -> pure . f c)++implChangeAnn :: Functor f => (Enc enc1 conf str -> f (Enc enc2a conf str)) -> Enc enc1 conf str -> f (Enc enc2b conf str)+implChangeAnn fn = fmap (withUnsafeCoerce id) . fn+
+ src/Data/TypedEncoding/Instances/Support/Validate.hs view
@@ -0,0 +1,60 @@++{-# LANGUAGE DataKinds #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE TypeApplications #-}++-- | Exports for instance creation.+-- +-- Contains typical things needed when implementing+-- encoding, decoding, recreate, or type to string conversions.+module Data.TypedEncoding.Instances.Support.Validate where+import Data.TypedEncoding.Common.Types+import Data.TypedEncoding.Common.Class ++import GHC.TypeLits+import Data.Proxy++-- $setup+-- >>> :set -XOverloadedStrings -XMultiParamTypeClasses -XDataKinds -XTypeApplications+++-- * Validation++validFromDec :: forall nm f c str . (KnownSymbol nm, RecreateErr f, Applicative f) => Decoding (Either UnexpectedDecodeEx) nm nm c str -> Validation f nm nm c str +validFromDec = validFromDec' @nm @nm ++validFromDec' :: forall alg nm f c str . (KnownSymbol nm, RecreateErr f, Applicative f) => Decoding (Either UnexpectedDecodeEx) nm alg c str -> Validation f nm alg c str +validFromDec' (UnsafeMkDecoding p fn) = UnsafeMkValidation p (decAsRecreateErr . fn)+ where + decAsRecreateErr :: Either UnexpectedDecodeEx a -> f a+ decAsRecreateErr (Left (UnexpectedDecodeEx p err)) = recoveryErr $ RecreateEx p err+ decAsRecreateErr (Right r) = pure r+++validR :: forall nm f c str . (Restriction nm, KnownSymbol nm, RecreateErr f, Applicative f) => Encoding (Either EncodeEx) nm nm c str -> Validation f nm nm c str +validR = validFromEnc' @nm @nm ++-- | Can cause slow compilation if used+validR' :: forall nm f c str alg . (Restriction nm, Algorithm nm alg, KnownSymbol nm, RecreateErr f, Applicative f) => Encoding (Either EncodeEx) nm alg c str -> Validation f nm alg c str +validR' = validFromEnc' @alg @nm +++validFromEnc' :: forall alg nm f c str . (KnownSymbol nm, RecreateErr f, Applicative f) => Encoding (Either EncodeEx) nm alg c str -> Validation f nm alg c str +validFromEnc' (UnsafeMkEncoding p fn) = UnsafeMkValidation p (encAsRecreateErr . rfn) + where+ encAsRecreateErr :: Either EncodeEx a -> f a+ encAsRecreateErr (Left (EncodeEx p err)) = recoveryErr $ RecreateEx p err+ encAsRecreateErr (Right r) = pure r + rfn :: forall (xs :: [Symbol]) . Enc (nm ': xs) c str -> Either EncodeEx (Enc xs c str)+ rfn (UnsafeMkEnc _ conf str) = + let re = fn $ UnsafeMkEnc Proxy conf str+ in UnsafeMkEnc Proxy conf . getPayload <$> re ++++
− src/Data/TypedEncoding/Instances/ToEncString/Common.hs
@@ -1,44 +0,0 @@-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE PolyKinds #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE AllowAmbiguousTypes #-}-{-# LANGUAGE OverloadedStrings #-}---- | Common 'ToEncString' and 'FromEncString' instances.-module Data.TypedEncoding.Instances.ToEncString.Common where----- import qualified Data.Text as T--- import qualified Data.Text.Lazy as TL--- import qualified Data.ByteString as B--- -- import qualified Data.ByteString.Lazy as BL--import Data.TypedEncoding.Instances.Support-import Data.TypedEncoding.Internal.Class.IsStringR--import Data.String-import Data.Proxy-import Data.Word-import Text.Read-import Data.Functor.Identity---instance IsString str => ToEncString "r-()" str Identity () where- toEncStringF _ = Identity $ MkEnc Proxy () (fromString "()")--instance IsString str => ToEncString "r-Int-decimal" str Identity Int where- toEncStringF i = Identity $ MkEnc Proxy () (fromString . show $ i)--instance IsString str => ToEncString "r-Word8-decimal" str Identity Word8 where- toEncStringF i = Identity $ MkEnc Proxy () (fromString . show $ i)---- All instances of "r-Word8-decimal" are @Show@ / @Read@ based-instance (IsStringR str, UnexpectedDecodeErr f, Applicative f) => FromEncString Word8 f str "r-Word8-decimal" where- fromEncStringF = asUnexpected @ "r-Word8-decimal" . readEither . toString . getPayload
− src/Data/TypedEncoding/Internal/Class.hs
@@ -1,72 +0,0 @@--{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE PolyKinds #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE AllowAmbiguousTypes #-}--module Data.TypedEncoding.Internal.Class (- module Data.TypedEncoding.Internal.Class- , module Data.TypedEncoding.Internal.Class.Util- , module Data.TypedEncoding.Internal.Class.Encode- , module Data.TypedEncoding.Internal.Class.Decode- , module Data.TypedEncoding.Internal.Class.Recreate - , module Data.TypedEncoding.Internal.Class.Superset - -- * Encoder and Encoding replace EncodeFAll- , module Data.TypedEncoding.Internal.Class.Encoder - ) where--import Data.TypedEncoding.Internal.Class.Util-import Data.TypedEncoding.Internal.Class.Encode-import Data.TypedEncoding.Internal.Class.Decode-import Data.TypedEncoding.Internal.Class.Recreate-import Data.TypedEncoding.Internal.Class.Superset-import Data.TypedEncoding.Internal.Class.Encoder --import Data.TypedEncoding.Internal.Types (Enc(..) - , withUnsafeCoerce- -- , getPayload- )-import Data.Functor.Identity-import GHC.TypeLits----- | --- Generalized Java @toString@ or a type safe version of Haskell's 'Show'.------ Encodes @a@ as @Enc '[xs]@.----class KnownSymbol x => ToEncString x str f a where- toEncStringF :: a -> f (Enc '[x] () str)--toEncString :: forall x str f a . (ToEncString x str Identity a) => a -> Enc '[x] () str-toEncString = runIdentity . toEncStringF----- | --- Reverse of 'ToEncString' decodes encoded string back to @a@-class (KnownSymbol x) => FromEncString a f str x where- fromEncStringF :: Enc '[x] () str -> f a--fromEncString :: forall a str x . (FromEncString a Identity str x) => Enc '[x] () str -> a-fromEncString = runIdentity . fromEncStringF---- Other classes ------ | Flatten is more permissive than 'Superset'--- @--- instance FlattenAs "r-ASCII" "enc-B64" where -- OK--- @--- --- Now encoded data has form @Enc '["r-ASCII"] c str@ --- and there is no danger of it begin incorrectly decoded.--class FlattenAs (y :: Symbol) (x :: Symbol) where- flattenAs :: Enc (x ': xs) c str -> Enc '[y] c str- flattenAs = withUnsafeCoerce id
− src/Data/TypedEncoding/Internal/Class/Decode.hs
@@ -1,81 +0,0 @@--{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE PolyKinds #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE AllowAmbiguousTypes #-}--module Data.TypedEncoding.Internal.Class.Decode where--import Data.TypedEncoding.Internal.Class.Util--import Data.TypedEncoding.Internal.Types (Enc(..) - , toEncoding- , getPayload- , UnexpectedDecodeEx(..))-import Data.Proxy-import Data.Functor.Identity-import GHC.TypeLits---class DecodeF f instr outstr where - decodeF :: instr -> f outstr--class DecodeFAll f (xs :: [Symbol]) c str where- decodeFAll :: Enc xs c str -> f (Enc '[] c str)--instance Applicative f => DecodeFAll f '[] c str where- decodeFAll (MkEnc _ c str) = pure $ toEncoding c str --instance (Monad f, DecodeFAll f xs c str, DecodeF f (Enc (x ': xs) c str) (Enc xs c str)) => DecodeFAll f (x ': xs) c str where- decodeFAll str = - let re :: f (Enc xs c str) = decodeF str- in re >>= decodeFAll--decodeAll :: forall xs c str . DecodeFAll Identity (xs :: [Symbol]) c str => - Enc xs c str- -> Enc '[] c str-decodeAll = runIdentity . decodeFAll ---decodeFPart_ :: forall f xs xsf c str . (Functor f, DecodeFAll f xs c str) => Proxy xs -> Enc (Append xs xsf) c str -> f (Enc xsf c str)-decodeFPart_ p (MkEnc _ conf str) = - let re :: f (Enc '[] c str) = decodeFAll $ MkEnc (Proxy :: Proxy xs) conf str- in MkEnc Proxy conf . getPayload <$> re --decodeFPart :: forall (xs :: [Symbol]) xsf f c str . (Functor f, DecodeFAll f xs c str) => Enc (Append xs xsf) c str -> f (Enc xsf c str)-decodeFPart = decodeFPart_ (Proxy :: Proxy xs) --decodePart_ :: DecodeFAll Identity (xs :: [Symbol]) c str => - Proxy xs - -> Enc (Append xs xsf) c str - -> Enc xsf c str -decodePart_ p = runIdentity . decodeFPart_ p--decodePart :: forall (xs :: [Symbol]) xsf c str . DecodeFAll Identity xs c str => - Enc (Append xs xsf) c str - -> Enc xsf c str-decodePart = decodePart_ (Proxy :: Proxy xs) ---- | With type safety in place decoding errors should be unexpected.--- This class can be used to provide extra info if decoding could fail-class UnexpectedDecodeErr f where - unexpectedDecodeErr :: UnexpectedDecodeEx -> f a--instance UnexpectedDecodeErr Identity where- unexpectedDecodeErr x = fail $ show x--instance UnexpectedDecodeErr (Either UnexpectedDecodeEx) where- unexpectedDecodeErr = Left --asUnexpected_ :: (KnownSymbol x, UnexpectedDecodeErr f, Applicative f, Show err) => Proxy x -> Either err a -> f a-asUnexpected_ p (Left err) = unexpectedDecodeErr $ UnexpectedDecodeEx p err-asUnexpected_ _ (Right r) = pure r--asUnexpected :: forall x f err a . (KnownSymbol x, UnexpectedDecodeErr f, Applicative f, Show err) => Either err a -> f a-asUnexpected = asUnexpected_ (Proxy :: Proxy x)
− src/Data/TypedEncoding/Internal/Class/Encode.hs
@@ -1,71 +0,0 @@--{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE PolyKinds #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeFamilies #-}--- {-# LANGUAGE TypeApplications #-}-{-# LANGUAGE AllowAmbiguousTypes #-}--module Data.TypedEncoding.Internal.Class.Encode where--import Data.TypedEncoding.Internal.Class.Util--import Data.TypedEncoding.Internal.Types (Enc(..) - , toEncoding- , getPayload- )-import Data.Proxy-import Data.Functor.Identity-import GHC.TypeLits---class EncodeF f instr outstr where - encodeF :: instr -> f outstr--class EncodeFAll f (xs :: [Symbol]) c str where- encodeFAll :: Enc '[] c str -> f (Enc xs c str)--instance Applicative f => EncodeFAll f '[] c str where- encodeFAll (MkEnc _ c str) = pure $ toEncoding c str --instance (Monad f, EncodeFAll f xs c str, EncodeF f (Enc xs c str) (Enc (x ': xs) c str)) => EncodeFAll f (x ': xs) c str where- encodeFAll str = - let re :: f (Enc xs c str) = encodeFAll str- in re >>= encodeF---encodeAll :: forall xs c str . EncodeFAll Identity (xs :: [Symbol]) c str => - Enc '[] c str - -> Enc xs c str-encodeAll = runIdentity . encodeFAll ----encodeFPart_ :: forall f xs xsf c str . (Functor f, EncodeFAll f xs c str) => Proxy xs -> Enc xsf c str -> f (Enc (Append xs xsf) c str)-encodeFPart_ p (MkEnc _ conf str) = - let re :: f (Enc xs c str) = encodeFAll $ MkEnc Proxy conf str- in MkEnc Proxy conf . getPayload <$> re ---encodeFPart :: forall (xs :: [Symbol]) xsf f c str . (Functor f, EncodeFAll f xs c str) => Enc xsf c str -> f (Enc (Append xs xsf) c str)-encodeFPart = encodeFPart_ (Proxy :: Proxy xs) ---encodePart_ :: EncodeFAll Identity (xs :: [Symbol]) c str => - Proxy xs - -> Enc xsf c str- -> Enc (Append xs xsf) c str -encodePart_ p = runIdentity . encodeFPart_ p---- | for some reason ApplyTypes syntax does not want to work if xs is specified with --- polymorphic [Symbol]-encodePart :: forall (xs :: [Symbol]) xsf c str . EncodeFAll Identity xs c str => - Enc xsf c str- -> Enc (Append xs xsf) c str -encodePart = encodePart_ (Proxy :: Proxy xs) -
− src/Data/TypedEncoding/Internal/Class/Encoder.hs
@@ -1,49 +0,0 @@-{-# LANGUAGE GADTs #-}-{-# LANGUAGE PolyKinds #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE AllowAmbiguousTypes #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE PartialTypeSignatures #-}--- {-# LANGUAGE RankNTypes #-}-{-# OPTIONS_GHC -Wno-partial-type-signatures #-}---- |--- Internal definition of types------ Possible replacement for EncodeFAll class that works with open definitions such as "r-ban"--module Data.TypedEncoding.Internal.Class.Encoder where--import Data.TypedEncoding.Internal.Types.Enc--- import Data.TypedEncoding.Internal.Class.Util-import Data.TypedEncoding.Internal.Class.Encode-import Data.TypedEncoding.Internal.Util.TypeLits-import GHC.TypeLits--- import Data.Symbol.Ascii---data Encoder f (enc :: [Symbol]) (grps :: [Symbol]) conf str where- -- | constructor is to be treated as Unsafe to Encode and Decode instance implementations- -- particular encoding instances may expose smart constructors for limited data types- ZeroEnc :: Encoder f '[] '[] conf str- AppendEnc :: (Enc xs conf str -> f (Enc (x ': xs) conf str)) -> Encoder f xs grps conf str -> Encoder f (x ': xs) ((TakeUntil x ":") ': grps) conf str--runEncoder :: forall grps enc f c str . (Monad f) => Encoder f enc grps c str -> Enc ('[]::[Symbol]) c str -> f (Enc enc c str)-runEncoder ZeroEnc enc0 = pure enc0-runEncoder (AppendEnc fn enc) enc0 = - let re :: f (Enc _ c str) = runEncoder enc enc0- in re >>= fn--encodeFEncoder :: forall f t tg xs gxs c str . (tg ~ TakeUntil t ":", Encodings f xs gxs c str, EncodeF f (Enc xs c str) (Enc (t ': xs) c str)) => Encoder f (t ': xs) (tg ': gxs) c str-encodeFEncoder = AppendEnc (encodeF @f @(Enc xs c str) @(Enc (t ': xs) c str)) encodings--class Encodings f (enc :: [Symbol]) (grps :: [Symbol]) c str where- encodings :: Encoder f enc grps c str--instance Encodings f '[] '[] c str where- encodings = ZeroEnc
− src/Data/TypedEncoding/Internal/Class/IsStringR.hs
@@ -1,73 +0,0 @@---{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE AllowAmbiguousTypes #-}-{-# LANGUAGE FlexibleInstances #-}---- | This Module will be removed in 0.3.x.x in favor of --- "Data.TypedEncoding.Internal.Class.Util.StringConstraints"-module Data.TypedEncoding.Internal.Class.IsStringR where--import Data.Proxy--import Data.String--- import qualified Data.ByteString.Char8 as B-import qualified Data.Text as T-import qualified Data.Text.Lazy as TL------ $setup--- >>> :set -XScopedTypeVariables -XTypeApplications -XAllowAmbiguousTypes--- >>> import Test.QuickCheck--- >>> import Test.QuickCheck.Instances.Text()--- >>> import Test.QuickCheck.Instances.ByteString()---- | This class will be removed in 0.3.x.x in favor of classes definined in --- "Data.TypedEncoding.Internal.Class.Util.StringConstraints"------ Reverses 'Data.String.IsString'------ laws:------ @--- toString . fromString == id--- fromString . toString == id--- @------ Note: ByteString is not a valid instance, ByteString "r-ASCII", or "r-UTF8" would--- be needed.--- @B.unpack $ B.pack "\160688" == "\176"@-class IsStringR a where- toString :: a -> String--prop_fromStringToString :: forall s . (IsString s, IsStringR s, Eq s) => s -> Bool-prop_fromStringToString x = x == (fromString @s . toString $ x)--prop_toStringFromString :: forall s . (IsString s, IsStringR s) => Proxy s -> String -> Bool-prop_toStringFromString _ x = x == (toString @s . fromString $ x)---- This would not work--- @B.unpack $ B.pack "\160688" == "\176"@------ prop> prop_toStringFromString (Proxy :: Proxy B.ByteString) --- prop> prop_fromStringToString @B.ByteString--- instance IsStringR B.ByteString where--- toString = B.unpack- ---- |--- prop> prop_toStringFromString (Proxy :: Proxy T.Text) --- prop> prop_fromStringToString @T.Text-instance IsStringR T.Text where- toString = T.unpack ---- |--- prop> prop_toStringFromString (Proxy :: Proxy TL.Text) --- prop> prop_fromStringToString @TL.Text-instance IsStringR TL.Text where- toString = TL.unpack --instance IsStringR [Char] where- toString = id
− src/Data/TypedEncoding/Internal/Class/Recreate.hs
@@ -1,90 +0,0 @@--{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE PolyKinds #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeFamilies #-}--- {-# LANGUAGE TypeApplications #-}-{-# LANGUAGE AllowAmbiguousTypes #-}--module Data.TypedEncoding.Internal.Class.Recreate where--import Data.TypedEncoding.Internal.Types (Enc(..) - , toEncoding- , withUnsafeCoerce- , RecreateEx(..)- , getPayload- )-import Data.TypedEncoding.Internal.Class.Util -import Data.Proxy-import Data.Functor.Identity-import GHC.TypeLits----- | Used to safely recover encoded data validating all encodingss-class RecreateF f instr outstr where - checkPrevF :: outstr -> f instr--class (Functor f) => RecreateFAll f (xs :: [Symbol]) c str where- checkFAll :: Enc xs c str -> f (Enc '[] c str)- recreateFAll :: Enc '[] c str -> f (Enc xs c str)- recreateFAll str@(MkEnc _ _ pay) = - let str0 :: Enc xs c str = withUnsafeCoerce id str- in withUnsafeCoerce (const pay) <$> checkFAll str0 --instance Applicative f => RecreateFAll f '[] c str where- checkFAll (MkEnc _ c str) = pure $ toEncoding c str ---instance (Monad f, RecreateFAll f xs c str, RecreateF f (Enc xs c str) (Enc (x ': xs) c str)) => RecreateFAll f (x ': xs) c str where- checkFAll str = - let re :: f (Enc xs c str) = checkPrevF str- in re >>= checkFAll---recreateAll :: forall xs c str . RecreateFAll Identity xs c str => - Enc '[] c str - -> Enc xs c str-recreateAll = runIdentity . recreateFAll ----- | Useful for partially manual recreation-recreateFPart_ :: forall f xs xsf c str . (Functor f, RecreateFAll f xs c str) => Proxy xs -> Enc xsf c str -> f (Enc (Append xs xsf) c str)-recreateFPart_ p (MkEnc _ conf str) = - let re :: f (Enc xs c str) = recreateFAll $ MkEnc Proxy conf str- in MkEnc Proxy conf . getPayload <$> re ---recreateFPart :: forall (xs :: [Symbol]) xsf f c str . (Functor f, RecreateFAll f xs c str) => Enc xsf c str -> f (Enc (Append xs xsf) c str)-recreateFPart = recreateFPart_ (Proxy :: Proxy xs) ---recreatePart_ :: RecreateFAll Identity (xs :: [Symbol]) c str => - Proxy xs - -> Enc xsf c str- -> Enc (Append xs xsf) c str -recreatePart_ p = runIdentity . recreateFPart_ p--recreatePart :: forall (xs :: [Symbol]) xsf c str . RecreateFAll Identity xs c str => - Enc xsf c str- -> Enc (Append xs xsf) c str -recreatePart = recreatePart_ (Proxy :: Proxy xs) ---- | Recovery errors are expected unless Recovery allows Identity instance-class RecreateErr f where - recoveryErr :: RecreateEx -> f a--instance RecreateErr (Either RecreateEx) where- recoveryErr = Left --asRecreateErr_ :: (RecreateErr f, Applicative f, Show err, KnownSymbol x) => Proxy x -> Either err a -> f a-asRecreateErr_ p (Left err) = recoveryErr $ RecreateEx p err-asRecreateErr_ _ (Right r) = pure r---asRecreateErr :: forall x f err a . (RecreateErr f, Applicative f, Show err, KnownSymbol x) => Either err a -> f a-asRecreateErr = asRecreateErr_ (Proxy :: Proxy x)
− src/Data/TypedEncoding/Internal/Class/Superset.hs
@@ -1,131 +0,0 @@--{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE PolyKinds #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeFamilies #-}--- {-# LANGUAGE TypeApplications #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE AllowAmbiguousTypes #-}--module Data.TypedEncoding.Internal.Class.Superset where--import Data.TypedEncoding.Internal.Util.TypeLits---import Data.TypedEncoding.Internal.Class.Util (displ)--import Data.TypedEncoding.Internal.Types (Enc(..) - , withUnsafeCoerce- --, unsafeSetPayload- )-import GHC.TypeLits-import Data.Symbol.Ascii--- import Data.Proxy---- $setup--- >>> :set -XOverloadedStrings -XMultiParamTypeClasses -XDataKinds -XTypeApplications--- >>> import Data.TypedEncoding.Internal.Class.Util (displ)--- >>> import Data.TypedEncoding.Internal.Types (unsafeSetPayload)--- >>> import Data.Text as T----- |--- DEPRECATED see 'IsSuperset'--- --- Subsets are useful for restriction encodings--- like r-UFT8 but should not be used for other encodings.------ This would be dangerous, it would, for example, permit converting encoded binary --- @"Enc '["enc-"] c ByteString@ to @"Enc '["enc-"] c Text@, decoding which--- could result in runtime errors.------ The requirement is that that the decoding in the superset--- can replace the decoding from injected subset.------ @--- instance Superset "r-ASCII" "enc-B64" where -- DANGEROUS--- @------ 'inject' is identity on payloads------ @Superset bigger smaller@ reads as @bigger@ is a superset of @smaller@-class Superset (y :: Symbol) (x :: Symbol) where- inject :: Enc (x ': xs) c str -> Enc (y ': xs) c str- inject = withUnsafeCoerce id--instance Superset x x where---- | more permissive than class-type family IsSuperset (y :: Symbol) (x :: Symbol) :: Bool where- IsSuperset "r-ASCII" "r-ASCII" = 'True- IsSuperset "r-UTF8" "r-ASCII" = 'True- IsSuperset "r-UTF8" "r-UTF8" = 'True- IsSuperset y x = IsSupersetOpen y (TakeUntil x ":") (ToList x)--type family IsSupersetOpen (y :: Symbol) (x :: Symbol) (xs :: [Symbol]) :: Bool--injectInto :: forall y x xs c str . (IsSuperset y x ~ 'True) => Enc (x ': xs) c str -> Enc (y ': xs) c str-injectInto = withUnsafeCoerce id---- | remove redundant superset right after the top (at second last encoding position)------ >>> displ $ demoteFlattenTop (unsafeSetPayload () "" :: Enc '["r-ASCII", "r-UTF8", "r-boo"] () T.Text)--- "MkEnc '[r-ASCII,r-boo] () (Text )"-demoteFlattenTop :: forall y x xs c str . (IsSuperset y x ~ 'True) => Enc (x ': y ': xs) c str -> Enc (x ': xs) c str-demoteFlattenTop = withUnsafeCoerce id---- | add redundant superset right after------ >>> displ $ promoteUnFlattenTop @"r-UTF8" (unsafeSetPayload () "" :: Enc '["r-ASCII", "r-boo"] () T.Text)--- "MkEnc '[r-ASCII,r-UTF8,r-boo] () (Text )"-promoteUnFlattenTop :: forall y x xs c str . (IsSuperset y x ~ 'True) => Enc (x ': xs) c str -> Enc (x ': y ': xs) c str-promoteUnFlattenTop = withUnsafeCoerce id---- | remove redunant superset from the top (at last applied encoding position)------ >>> displ $ demoteRemoveTop (unsafeSetPayload () "" :: Enc '["r-UTF8", "r-ASCII", "r-boo"] () T.Text)--- "MkEnc '[r-ASCII,r-boo] () (Text )"-demoteRemoveTop :: forall y x xs c str . (IsSuperset y x ~ 'True) => Enc (y ': x ' : xs) c str -> Enc (x ': xs) c str-demoteRemoveTop = withUnsafeCoerce id---- | add redundant superset at the top------ >>> displ $ promoteAddTop @"r-UTF8" (unsafeSetPayload () "" :: Enc '["r-ASCII", "r-boo"] () T.Text)--- "MkEnc '[r-UTF8,r-ASCII,r-boo] () (Text )"-promoteAddTop :: forall y x xs c str . (IsSuperset y x ~ 'True) => Enc (x ': xs) c str -> Enc (y ': x ' : xs) c str -promoteAddTop = withUnsafeCoerce id---- | remove redundant superset at bottom (first encoding) position------ >>> displ $ demoteRemoveBot (unsafeSetPayload () "" :: Enc '["r-boo", "r-ASCII", "r-UTF8"] () T.Text)--- "MkEnc '[r-boo,r-ASCII] () (Text )"-demoteRemoveBot :: (UnSnoc xs ~ '(,) ys y, UnSnoc ys ~ '(,) zs x, IsSuperset y x ~ 'True) => Enc xs c str -> Enc ys c str-demoteRemoveBot = withUnsafeCoerce id---- | add redundant superset at bottom (first encoding) position------ >>> displ $ promoteAddBot @"r-UTF8" (unsafeSetPayload () "" :: Enc '["r-boo", "r-ASCII"] () T.Text)--- "MkEnc '[r-boo,r-ASCII,r-UTF8] () (Text )"-promoteAddBot :: forall y x xs c str ys . (UnSnoc xs ~ '(,) ys x, IsSuperset y x ~ 'True) => Enc xs c str -> Enc (Snoc xs y) c str-promoteAddBot = withUnsafeCoerce id---- | remove redundant superset at second bottom (second encoding) position------ >>> displ $ demoteFlattenBot (unsafeSetPayload () "" :: Enc '["r-boo", "r-UTF8", "r-ASCII"] () T.Text)--- "MkEnc '[r-boo,r-ASCII] () (Text )"-demoteFlattenBot :: (UnSnoc xs ~ '(,) ys x, UnSnoc ys ~ '(,) zs y, IsSuperset y x ~ 'True) => Enc xs c str -> Enc (Snoc zs x) c str-demoteFlattenBot = withUnsafeCoerce id---- | add redundant superset at second bottom (second encoding) position------ >>> displ $ promoteUnFlattenBot @"r-UTF8" (unsafeSetPayload () "" :: Enc '["r-boo", "r-ASCII"] () T.Text)--- "MkEnc '[r-boo,r-UTF8,r-ASCII] () (Text )"-promoteUnFlattenBot :: forall y x xs c str ys . (UnSnoc xs ~ '(,) ys x, IsSuperset y x ~ 'True) => Enc xs c str -> Enc (Snoc (Snoc ys y) x) c str-promoteUnFlattenBot = withUnsafeCoerce id---- prop_Superset :: forall y x xs c str . (Superset y x, Eq str) => Enc (x ': xs) c str -> Bool--- prop_Superset x = getPayload x == (getPayload . inject @y @x $ x)-
− src/Data/TypedEncoding/Internal/Class/Util.hs
@@ -1,94 +0,0 @@--{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE PolyKinds #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE AllowAmbiguousTypes #-}--module Data.TypedEncoding.Internal.Class.Util where--import Data.TypedEncoding.Internal.Types.Common--import qualified Data.ByteString.Char8 as B-import qualified Data.ByteString.Lazy.Char8 as BL-import qualified Data.Text as T-import qualified Data.Text.Lazy as TL--import Data.Proxy-import qualified Data.List as L-import GHC.TypeLits---- $setup--- >>> :set -XScopedTypeVariables -XTypeApplications -XAllowAmbiguousTypes -XDataKinds---- * Symbol List--class SymbolList (xs::[Symbol]) where - symbolVals :: [String]--instance SymbolList '[] where- symbolVals = []---- |--- >>> symbolVals @ '["FIRST", "SECOND"]--- ["FIRST","SECOND"]-instance (SymbolList xs, KnownSymbol x) => SymbolList (x ': xs) where- symbolVals = symbolVal (Proxy :: Proxy x) : symbolVals @xs- -symbolVals_ :: forall xs . SymbolList xs => Proxy xs -> [String]-symbolVals_ _ = symbolVals @xs---- * Display ---- | Human friendly version of Show-class Displ x where - displ :: x -> String---- | TODO 0.3 TO BE REMOVED-instance Displ EncAnn where- displ = id --instance Displ [EncAnn] where - displ x = "[" ++ L.intercalate "," x ++ "]"-instance Displ T.Text where- displ x = "(Text " ++ T.unpack x ++ ")"-instance Displ TL.Text where- displ x = "(TL.Text " ++ TL.unpack x ++ ")"-instance Displ B.ByteString where- displ x = "(ByteString " ++ B.unpack x ++ ")" -instance Displ BL.ByteString where- displ x = "(ByteString " ++ BL.unpack x ++ ")" ---- TODO 0.3 replaces: instance Displ EncAnn --- instance Displ String where--- displ x = "(String " ++ x ++ ")" ------ |--- >>> displ (Proxy :: Proxy ["FIRST", "SECOND"])--- "[FIRST,SECOND]"-instance (SymbolList xs) => Displ (Proxy xs) where- displ _ = displ $ symbolVals @ xs- -- "[" ++ (L.intercalate "," $ map displ $ symbolVals @ xs) ++ "]"----- * Other---- | TODO should this be imported from somewhere?-type family Append (xs :: [k]) (ys :: [k]) :: [k] where- Append '[] xs = xs- Append (y ': ys) xs = y ': Append ys xs---- | Polymorphic data payloads used to encode/decode-class HasA a c where- has :: c -> a--instance HasA () c where- has = const ()
− src/Data/TypedEncoding/Internal/Class/Util/StringConstraints.hs
@@ -1,134 +0,0 @@-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE PolyKinds #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE AllowAmbiguousTypes #-}---- | Future replacement for "Data.TypedEncoding.Internal.Class.IsStringR"-module Data.TypedEncoding.Internal.Class.Util.StringConstraints () where--import qualified Data.Text as T-import qualified Data.Text.Lazy as TL-import Data.String-import Data.Proxy-import qualified Data.ByteString as B-import qualified Data.ByteString.Lazy as BL-import qualified Data.ByteString.Char8 as B8-import qualified Data.ByteString.Lazy.Char8 as BL8---- $setup--- >>> :set -XScopedTypeVariables -XTypeApplications -XAllowAmbiguousTypes--- >>> import Test.QuickCheck--- >>> import Test.QuickCheck.Instances.Text()--- >>> import Test.QuickCheck.Instances.ByteString()----- * IsString reversal---- | Reverses 'Data.String.IsString'------ law for types that are also @IsString@:--- --- @--- toString . fromString == id--- @------ Note: ByteString is not a valid instance, ByteString "r-ASCII", or "r-UTF8" would--- be needed.--- @B8.unpack $ B8.pack "\160688" == "\176"@------ This class is separated from @ToStrIso@ to allow instances from /smaller/ types--- the can inject into the 'String' type.-class ToStrInj str from where- toString :: from -> str--prop_toStringFromString :: forall s . (IsString s, ToStrInj String s) => Proxy s -> String -> Bool-prop_toStringFromString _ x = x == (toString @String @s . fromString $ x)----- |--- prop> prop_toStringFromString (Proxy :: Proxy T.Text) -instance ToStrInj String T.Text where- toString = T.unpack ---- |--- prop> prop_toStringFromString (Proxy :: Proxy TL.Text) -instance ToStrInj String TL.Text where- toString = TL.unpack ---instance ToStrInj String String where- toString = id---- will not work!--- prop> prop_toStringFromString (Proxy :: Proxy B.ByteString) --- instance ToStrInj String B.ByteString where--- toString = B8.unpack---- will not work!--- prop> prop_toStringFromString (Proxy :: Proxy BL.ByteString) --- instance ToStrInj String BL.ByteString where--- toString = BL8.unpack----- | Same as @ToStrInj@ but with additional------ law for types that are also @IsString@:--- @--- fromString . toString == id--- @-class ToStrInj str from => ToStrIso str from where--prop_fromStringToString :: forall s . (IsString s, ToStrIso String s, Eq s) => s -> Bool-prop_fromStringToString x = x == (fromString @s . toString $ x)---- |--- prop> prop_fromStringToString @T.Text-instance ToStrIso String T.Text where---- |--- prop> prop_fromStringToString @TL.Text-instance ToStrIso String TL.Text where --instance ToStrIso String String where---- |--- Used to find exceptions that violated "r-" encoding--- Expected to be used to check encoding of ASCII-7 so Text and ByteString are compatible.-class Char7Find str where- find :: (Char -> Bool) -> str -> Maybe Char---instance Char7Find T.Text where- find = T.find--instance Char7Find TL.Text where- find = TL.find ---- |--- B8.pack is basically a convenient way to get Word8 elements into ByteString.------ During B8.pack conversion charters are downsized to the 0-255 range (become a Word8).--- The length is preserved. --- --- >>> B8.pack "\160582"--- "F"------ >>> B.length $ B8.pack "\160582"--- 1------ This instance allows to check elements of ByteString interpreting them as Char.--- --- This may or may not work with UTF8 conversions.--- Safe if restricting to 7bit code points.s-instance Char7Find B.ByteString where- find = B8.find --instance Char7Find BL.ByteString where- find = BL8.find
− src/Data/TypedEncoding/Internal/Combinators.hs
@@ -1,54 +0,0 @@--{-# LANGUAGE PolyKinds #-}-{-# LANGUAGE DataKinds #-}--- {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE AllowAmbiguousTypes #-}-{-# LANGUAGE FlexibleContexts #-}---- | Combinators reexported in Data.TypedEncoding-module Data.TypedEncoding.Internal.Combinators where--import Data.TypedEncoding.Internal.Types-import Data.TypedEncoding.Internal.Class.Recreate-import Data.TypedEncoding.Internal.Class.Util (SymbolList)-import GHC.TypeLits--- import Data.Proxy---- $setup--- >>> :set -XTypeApplications--- >>> import qualified Data.Text as T--- >>> import Data.Word---- * Converting 'UncheckedEnc' to 'Enc'---- | Maybe signals annotation mismatch, effect @f@ is not evaluated unless there is match-verifyUncheckedEnc :: forall (xs :: [Symbol]) f c str . (- RecreateFAll f xs c str- , RecreateErr f- , Applicative f- , SymbolList xs- ) - =>- UncheckedEnc c str- -> Maybe (f (Enc xs c str))--verifyUncheckedEnc x = - -- let perr = Proxy :: Proxy "e-mismatch"- --in - case verifyAnn @xs x of- Left err -> Nothing -- asRecreateErr_ perr $ Left err- Right (MkUncheckedEnc _ c str) -> Just $ recreateFAll . toEncoding c $ str---verifyUncheckedEnc' :: forall (xs :: [Symbol]) c str . (- RecreateFAll (Either RecreateEx) xs c str- , SymbolList xs- ) - =>- UncheckedEnc c str- -> Maybe (Either RecreateEx (Enc xs c str))-verifyUncheckedEnc' = verifyUncheckedEnc--
− src/Data/TypedEncoding/Internal/Instances/Combinators.hs
@@ -1,107 +0,0 @@--{-# LANGUAGE PolyKinds #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ScopedTypeVariables #-}--- {-# LANGUAGE AllowAmbiguousTypes #-}--- {-# LANGUAGE TypeApplications #-}-{-# LANGUAGE AllowAmbiguousTypes #-}-{-# LANGUAGE FlexibleContexts #-}---- | Combinators that can be helpful in instance creation.-module Data.TypedEncoding.Internal.Instances.Combinators where--import Data.String-import Data.Proxy-import Text.Read-import Data.TypedEncoding.Internal.Types-import Data.TypedEncoding.Internal.Class.IsStringR -import GHC.TypeLits---- $setup--- >>> :set -XTypeApplications--- >>> import qualified Data.Text as T--- >>> import Data.Word----- * Composite encodings from 'Foldable' 'Functor' types---- | allows to fold payload in Enc to create another Enc, assumes homogeneous input encodings.--- This yields not a type safe code, better implementation code should use fixed size--- dependently typed @Vect n@ or some @HList@ like foldable.-foldEnc :: forall (xs2 :: [Symbol]) (xs1 :: [Symbol]) f c s1 s2 - . (Foldable f, Functor f) - => c -> (s1 -> s2 -> s2) -> s2 -> f (Enc xs1 c s1) -> Enc xs2 c s2-foldEnc c f sinit = unsafeSetPayload c . foldr f sinit . fmap getPayload ---- | Similar to 'foldEnc', assumes that destination payload has @IsString@ instance and uses @""@ as base case. -foldEncStr :: forall (xs2 :: [Symbol]) (xs1 :: [Symbol]) f c s1 s2 - . (Foldable f, Functor f, IsString s2) - => c -> (s1 -> s2 -> s2) -> f (Enc xs1 c s1) -> Enc xs2 c s2-foldEncStr c f = foldEnc c f ""---- | Similar to 'foldEnc', works with untyped 'CheckedEnc'-foldCheckedEnc :: forall (xs2 :: [Symbol]) f c s1 s2 - . (Foldable f, Functor f) - => c -> ([EncAnn] -> s1 -> s2 -> s2) -> s2 -> f (CheckedEnc c s1) -> Enc xs2 c s2-foldCheckedEnc c f sinit = unsafeSetPayload c . foldr (uncurry f) sinit . fmap getCheckedEncPayload---- | Similar to 'foldEncStr', works with untyped 'CheckedEnc'-foldCheckedEncStr :: forall (xs2 :: [Symbol]) f c s1 s2 . (Foldable f, Functor f, IsString s2) => c -> ([EncAnn] -> s1 -> s2 -> s2) -> f (CheckedEnc c s1) -> Enc xs2 c s2-foldCheckedEncStr c f = foldCheckedEnc c f ""----- * Composite encoding: Recreate and Encode helpers---- | Splits composite payload into homogenious chunks-splitPayload :: forall (xs2 :: [Symbol]) (xs1 :: [Symbol]) c s1 s2 . - (s1 -> [s2]) - -> Enc xs1 c s1 - -> [Enc xs2 c s2]-splitPayload f (MkEnc _ c s1) = map (MkEnc Proxy c) (f s1)- --- | Untyped version of 'splitPayload'-splitSomePayload :: forall c s1 s2 . - ([EncAnn] -> s1 -> [([EncAnn], s2)]) - -> CheckedEnc c s1 - -> [CheckedEnc c s2]-splitSomePayload f (MkCheckedEnc ann1 c s1) = map (\(ann2, s2) -> MkCheckedEnc ann2 c s2) (f ann1 s1)----- * Utility combinators ---- | sometimes show . read is not identity, eg. Word8:------ >>> read "256" :: Word8--- 0------ >>> verifyWithRead @Word8 "Word8-decimal" (T.pack "256")--- Left "Payload does not satisfy format Word8-decimal: 256"------ >>> verifyWithRead @Word8 "Word8-decimal" (T.pack "123")--- Right "123"-verifyWithRead :: forall a str . (IsStringR str, Read a, Show a) => String -> str -> Either String str-verifyWithRead msg x = - let s = toString x- a :: Maybe a = readMaybe s- check = (show <$> a) == Just s - in if check- then Right x- else Left $ "Payload does not satisfy format " ++ msg ++ ": " ++ s ----- | Convenience function for checking if @str@ decodes properly--- using @enc@ encoding markers and decoders that can pick decoder based--- on that marker-verifyDynEnc :: forall s str err1 err2 enc a. (KnownSymbol s, Show err1, Show err2) => - Proxy s -- ^ proxy defining encoding annotation- -> (Proxy s -> Either err1 enc) -- ^ finds encoding marker @enc@ for given annotation or fails- -> (enc -> str -> Either err2 a) -- ^ decoder based on @enc@ marker- -> str -- ^ input- -> Either EncodeEx str-verifyDynEnc p findenc decoder str = - do- enc <- asEncodeEx p . findenc $ p- case decoder enc str of- Left err -> Left $ EncodeEx p err- Right r -> Right str
− src/Data/TypedEncoding/Internal/Types.hs
@@ -1,109 +0,0 @@-{-# LANGUAGE GADTs #-}-{-# LANGUAGE PolyKinds #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE AllowAmbiguousTypes #-}--- {-# LANGUAGE RankNTypes #-}---- |--- Internal definition of types--module Data.TypedEncoding.Internal.Types (- module Data.TypedEncoding.Internal.Types- -- * Main encoding type and basic combinators.- , module Data.TypedEncoding.Internal.Types.Enc- -- * Untyped version and existentially quantified versions of Enc- , module Data.TypedEncoding.Internal.Types.CheckedEnc- -- * Not verified encoded data- , module Data.TypedEncoding.Internal.Types.UncheckedEnc- -- * Commmon types- , module Data.TypedEncoding.Internal.Types.Common- ) where--import Data.TypedEncoding.Internal.Types.Enc-import Data.TypedEncoding.Internal.Types.CheckedEnc-import Data.TypedEncoding.Internal.Types.UncheckedEnc-import Data.TypedEncoding.Internal.Types.Common--import Data.Proxy--- import Data.Functor.Identity-import GHC.TypeLits--- import Data.TypedEncoding.Internal.Class.Util---- $setup--- >>> :set -XOverloadedStrings -XMultiParamTypeClasses -XDataKinds -XAllowAmbiguousTypes--- >>> import qualified Data.Text as T----- | Represents errors in recovery (recreation of encoded types).-data RecreateEx where- RecreateEx:: (Show e, KnownSymbol x) => Proxy x -> e -> RecreateEx- RecreateExUnkStep:: (Show e) => e -> RecreateEx--instance Show RecreateEx where- show (RecreateEx prxy a) = "(RecreateEx \"" ++ symbolVal prxy ++ "\" (" ++ show a ++ "))"- show (RecreateExUnkStep a) = "(UnknownDecodeStep (" ++ show a ++ "))"---recreateErrUnknown :: (Show e) => e -> RecreateEx-recreateErrUnknown = RecreateExUnkStep---- instance Eq RecreateEx where--- (RecreateEx prxy1 a1) == RecreateEx prxy2 a2 = (symbolVal prxy1) == (symbolVal prxy2)----- | Represents errors in encoding-data EncodeEx where- EncodeEx:: (Show a, KnownSymbol x) => Proxy x -> a -> EncodeEx --instance Show EncodeEx where- show (EncodeEx prxy a) = "(EncodeEx \"" ++ symbolVal prxy ++ "\" (" ++ show a ++ "))"--asEncodeEx :: (Show a, KnownSymbol x) => Proxy x -> Either a b -> Either EncodeEx b-asEncodeEx p = either (Left . EncodeEx p) Right ----- | Useful when manually recreating using recovery-encToRecrEx :: EncodeEx -> RecreateEx-encToRecrEx (EncodeEx p a) = RecreateEx p a--mergeEncodeEx :: KnownSymbol x => Proxy x -> EncodeEx -> Maybe EncodeEx -> EncodeEx-mergeEncodeEx _ ex Nothing = ex-mergeEncodeEx p (EncodeEx _ a) (Just (EncodeEx _ b)) = EncodeEx p $ "Errors: " ++ show (a,b)--emptyEncErr :: KnownSymbol x => Proxy x -> EncodeEx -emptyEncErr p = EncodeEx p ("unexpected" :: String)---- | Type safety over encodings makes decoding process safe.--- However failures are still possible due to bugs or unsafe payload modifications.--- UnexpectedDecodeEx represents such errors.-data UnexpectedDecodeEx where- UnexpectedDecodeEx :: (Show a, KnownSymbol x) => Proxy x -> a -> UnexpectedDecodeEx--instance Show UnexpectedDecodeEx where- show (UnexpectedDecodeEx prxy a) = "(UnexpectedDecodeEx \"" ++ symbolVal prxy ++ "\" (" ++ show a ++ "))"----- * Base combinators that rely on types defined here---- TODO could this type be more precise?-implEncodeF_ :: (Show err, KnownSymbol x) => Proxy x -> (str -> Either err str) -> Enc enc1 conf str -> Either EncodeEx (Enc enc2 conf str) -implEncodeF_ p f = implTranF (either (Left . EncodeEx p) Right . f) --implEncodeF :: forall x enc1 enc2 err conf str . - (Show err, KnownSymbol x) - => (str -> Either err str) -> Enc enc1 conf str -> Either EncodeEx (Enc enc2 conf str) -implEncodeF = implEncodeF_ (Proxy :: Proxy x)--implEncodeF_' :: (Show err, KnownSymbol x) => Proxy x -> (conf -> str -> Either err str) -> Enc enc1 conf str -> Either EncodeEx (Enc enc2 conf str) -implEncodeF_' p f = implTranF' (\c -> either (Left . EncodeEx p) Right . f c) ---mergeErrs :: err -> (err -> Maybe err -> err) -> Either err a -> Either err b -> Either err c-mergeErrs _ fn (Left er1) (Left er2) = Left (fn er1 $ Just er2)-mergeErrs _ fn (Left er1) _ = Left (fn er1 Nothing)-mergeErrs _ fn _ (Left er2) = Left (fn er2 Nothing) -mergeErrs de fn (Right r) (Right y) = Left de
− src/Data/TypedEncoding/Internal/Types/CheckedEnc.hs
@@ -1,85 +0,0 @@-{-# LANGUAGE GADTs #-}-{-# LANGUAGE ScopedTypeVariables #-}--- {-# LANGUAGE PolyKinds #-}--- {-# LANGUAGE DataKinds #-}--- {-# LANGUAGE TypeOperators #-}--- {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE StandaloneDeriving #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE AllowAmbiguousTypes #-}-{-# LANGUAGE RankNTypes #-}---- |--- Module defines 'CheckedEnc' - untyped ADT version of 'Enc' --module Data.TypedEncoding.Internal.Types.CheckedEnc where--import Data.TypedEncoding.Internal.Types.Enc-import Data.TypedEncoding.Internal.Types.Common-import Data.TypedEncoding.Internal.Class.Util-import Data.Proxy---- $setup--- >>> :set -XOverloadedStrings -XMultiParamTypeClasses -XDataKinds -XAllowAmbiguousTypes--- >>> import qualified Data.Text as T----- * Untyped Enc---- constructor is to be treated as Unsafe to Encode and Decode instance implementations--- particular encoding instances may expose smart constructors for limited data types---- | Represents some validated encoded string. ------ @CheckedEnc@ is untyped version of 'Data.TypedEncoding.Internal.Types.Enc.Enc'. --- @CheckedEnc@ contains verified encoded data, encoding is visible--- at the value level only.-data CheckedEnc conf str = MkCheckedEnc [EncAnn] conf str- deriving (Show, Eq) --unsafeCheckedEnc:: [EncAnn] -> c -> s -> CheckedEnc c s-unsafeCheckedEnc = MkCheckedEnc--getCheckedPayload :: CheckedEnc conf str -> str-getCheckedPayload = snd . getCheckedEncPayload--getCheckedEncPayload :: CheckedEnc conf str -> ([EncAnn], str) -getCheckedEncPayload (MkCheckedEnc t _ s) = (t,s)--toCheckedEnc :: forall xs c str . (SymbolList xs) => Enc xs c str -> CheckedEnc c str -toCheckedEnc (MkEnc p c s) = - MkCheckedEnc (symbolVals @ xs) c s ---fromCheckedEnc :: forall xs c str . SymbolList xs => CheckedEnc c str -> Maybe (Enc xs c str)-fromCheckedEnc (MkCheckedEnc xs c s) = - let p = Proxy :: Proxy xs- in if symbolVals @ xs == xs- then Just $ MkEnc p c s- else Nothing------------------------------ |--- >>> let encsometest = MkCheckedEnc ["TEST"] () $ T.pack "hello"--- >>> proc_toCheckedEncFromCheckedEnc @'["TEST"] encsometest--- True--- >>> proc_toCheckedEncFromCheckedEnc @'["TEST1"] encsometest--- False-proc_toCheckedEncFromCheckedEnc :: forall xs c str . (SymbolList xs, Eq c, Eq str) => CheckedEnc c str -> Bool-proc_toCheckedEncFromCheckedEnc x = (== Just x) . fmap (toCheckedEnc @ xs) . fromCheckedEnc $ x---- |--- >>> let enctest = unsafeSetPayload () "hello" :: Enc '["TEST"] () T.Text--- >>> proc_fromCheckedEncToCheckedEnc enctest--- True-proc_fromCheckedEncToCheckedEnc :: forall xs c str . (SymbolList xs, Eq c, Eq str) => Enc xs c str -> Bool-proc_fromCheckedEncToCheckedEnc x = (== Just x) . fromCheckedEnc . toCheckedEnc $ x---- |--- >>> displ $ unsafeCheckedEnc ["TEST"] () ("hello" :: T.Text)--- "MkCheckedEnc [TEST] () (Text hello)"-instance (Show c, Displ str) => Displ (CheckedEnc c str) where- displ (MkCheckedEnc xs c s) = - "MkCheckedEnc " ++ displ xs ++ " " ++ show c ++ " " ++ displ s-
− src/Data/TypedEncoding/Internal/Types/Common.hs
@@ -1,4 +0,0 @@-module Data.TypedEncoding.Internal.Types.Common where---- | Represents value level (single) annotation.-type EncAnn = String
− src/Data/TypedEncoding/Internal/Types/Enc.hs
@@ -1,97 +0,0 @@-{-# LANGUAGE GADTs #-}-{-# LANGUAGE PolyKinds #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ScopedTypeVariables #-}--- {-# LANGUAGE TypeApplications #-}-{-# LANGUAGE AllowAmbiguousTypes #-}--- {-# LANGUAGE RankNTypes #-}---- |--- Internal definition of types--module Data.TypedEncoding.Internal.Types.Enc where--import Data.Proxy--import Data.TypedEncoding.Internal.Class.Util---- $setup--- >>> :set -XOverloadedStrings -XMultiParamTypeClasses -XDataKinds -XAllowAmbiguousTypes--- >>> import qualified Data.Text as T---- This type contains type level encoding information as well as--- configuration and payload.-data Enc enc conf str where- -- | constructor is to be treated as Unsafe to Encode and Decode instance implementations- -- particular encoding instances may expose smart constructors for limited data types- MkEnc :: Proxy enc -> conf -> str -> Enc enc conf str- deriving (Show, Eq) ---- |--- >>> let disptest = unsafeSetPayload () "hello" :: Enc '["TEST"] () T.Text--- >>> displ disptest--- "MkEnc '[TEST] () (Text hello)"-instance (SymbolList xs, Show c, Displ str) => Displ ( Enc xs c str) where- displ (MkEnc p c s) = - "MkEnc '" ++ displ (Proxy :: Proxy xs) ++ " " ++ show c ++ " " ++ displ s---toEncoding :: conf -> str -> Enc '[] conf str-toEncoding = MkEnc Proxy --fromEncoding :: Enc '[] conf str -> str-fromEncoding = getPayload---- TODO make all implTran functions module-private--- TODO disambiguate implEncode from implDecode, from implCheckPrevF for type safety--- especially since these are always used in combo with asRecreateErr_ or asUnexpected --implTranF :: Functor f => (str -> f str) -> Enc enc1 conf str -> f (Enc enc2 conf str)-implTranF f = implTranF' (const f)---implDecodeF :: Functor f => (str -> f str) -> Enc enc1 conf str -> f (Enc enc2 conf str)-implDecodeF = implTranF--implCheckPrevF :: Functor f => (str -> f str) -> Enc enc1 conf str -> f (Enc enc2 conf str)-implCheckPrevF = implTranF---implTranF' :: Functor f => (conf -> str -> f str) -> Enc enc1 conf str -> f (Enc enc2 conf str)-implTranF' f (MkEnc _ conf str) = MkEnc Proxy conf <$> f conf str---implDecodeF' :: Functor f => (conf -> str -> f str) -> Enc enc1 conf str -> f (Enc enc2 conf str)-implDecodeF' = implTranF'--implTranP :: Applicative f => (str -> str) -> Enc enc1 conf str -> f (Enc enc2 conf str)-implTranP f = implTranF' (\c -> pure . f)--implEncodeP :: Applicative f => (str -> str) -> Enc enc1 conf str -> f (Enc enc2 conf str)-implEncodeP = implTranP--implTranP' :: Applicative f => (conf -> str -> str) -> Enc enc1 conf str -> f (Enc enc2 conf str)-implTranP' f = implTranF' (\c -> pure . f c)--implEncodeP' :: Applicative f => (conf -> str -> str) -> Enc enc1 conf str -> f (Enc enc2 conf str)-implEncodeP' = implTranP'--implChangeAnn :: Functor f => (Enc enc1 conf str -> f (Enc enc2a conf str)) -> Enc enc1 conf str -> f (Enc enc2b conf str)-implChangeAnn fn = fmap (withUnsafeCoerce id) . fn----getPayload :: Enc enc conf str -> str -getPayload (MkEnc _ _ str) = str--unsafeSetPayload :: conf -> str -> Enc enc conf str -unsafeSetPayload = MkEnc Proxy --withUnsafeCoerce :: (s1 -> s2) -> Enc e1 c s1 -> Enc e2 c s2-withUnsafeCoerce f (MkEnc _ conf str) = MkEnc Proxy conf (f str)--unsafeChangePayload :: (s1 -> s2) -> Enc e c s1 -> Enc e c s2-unsafeChangePayload f (MkEnc p conf str) = MkEnc p conf (f str)
− src/Data/TypedEncoding/Internal/Types/SomeAnnotation.hs
@@ -1,34 +0,0 @@-{-# LANGUAGE GADTs #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE PolyKinds #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE AllowAmbiguousTypes #-}-{-# LANGUAGE RankNTypes #-}---- | internally used existential type for taking track of annotations-module Data.TypedEncoding.Internal.Types.SomeAnnotation where--import Data.TypedEncoding.Internal.Types.Common-import Data.TypedEncoding.Internal.Class.Util-import Data.TypedEncoding.Internal.Util-import Data.Proxy-import GHC.TypeLits---data SomeAnnotation where- MkSomeAnnotation :: SymbolList xs => Proxy xs -> SomeAnnotation--withSomeAnnotation :: SomeAnnotation -> (forall xs . SymbolList xs => Proxy xs -> r) -> r-withSomeAnnotation (MkSomeAnnotation p) fn = fn p---- folds over SomeSymbol list using withSomeSymbol and proxyCons-someAnnValue :: [EncAnn] -> SomeAnnotation-someAnnValue xs = - foldr (fn . someSymbolVal) (MkSomeAnnotation (Proxy :: Proxy '[])) xs- where - somesymbs = map someSymbolVal xs- fn ss (MkSomeAnnotation pxs) = withSomeSymbol ss (\px -> MkSomeAnnotation (px `proxyCons` pxs)) --proxyCons :: forall (x :: Symbol) (xs :: [Symbol]) . Proxy x -> Proxy xs -> Proxy (x ': xs)-proxyCons _ _ = Proxy
− src/Data/TypedEncoding/Internal/Types/SomeEnc.hs
@@ -1,61 +0,0 @@-{-# LANGUAGE GADTs #-}-{-# LANGUAGE ScopedTypeVariables #-}--- {-# LANGUAGE PolyKinds #-}--- {-# LANGUAGE DataKinds #-}--- {-# LANGUAGE TypeOperators #-}--- {-# LANGUAGE FlexibleInstances #-}--- {-# LANGUAGE StandaloneDeriving #-}--- {-# LANGUAGE TypeApplications #-}-{-# LANGUAGE AllowAmbiguousTypes #-}-{-# LANGUAGE RankNTypes #-}---- |--- Module defines 'SomeEnc' - existentially quantified version of @Enc@--- and basic combinators--module Data.TypedEncoding.Internal.Types.SomeEnc where--import Data.TypedEncoding.Internal.Types.Enc-import Data.TypedEncoding.Internal.Class.Util-import Data.TypedEncoding.Internal.Types.SomeAnnotation-import Data.TypedEncoding.Internal.Types.CheckedEnc---- $setup--- >>> :set -XOverloadedStrings -XMultiParamTypeClasses -XDataKinds -XAllowAmbiguousTypes--- >>> import qualified Data.Text as T------ | Existentially quantified quanitified @Enc@--- effectively isomorphic to 'CheckedEnc'-data SomeEnc conf str where- MkSomeEnc :: SymbolList xs => Enc xs conf str -> SomeEnc conf str- -withSomeEnc :: SomeEnc conf str -> (forall xs . SymbolList xs => Enc xs conf str -> r) -> r-withSomeEnc (MkSomeEnc enc) f = f enc--toSome :: SymbolList xs => Enc xs conf str -> SomeEnc conf str-toSome = MkSomeEnc---- | --- >>> let enctest = unsafeSetPayload () "hello" :: Enc '["TEST"] () T.Text--- >>> someToChecked . MkSomeEnc $ enctest--- MkCheckedEnc ["TEST"] () "hello"-someToChecked :: SomeEnc conf str -> CheckedEnc conf str-someToChecked se = withSomeEnc se toCheckedEnc---- | --- >>> let tst = unsafeCheckedEnc ["TEST"] () "test"--- >>> displ $ checkedToSome tst--- "Some (MkEnc '[TEST] () test)"-checkedToSome :: CheckedEnc conf str -> SomeEnc conf str-checkedToSome (MkCheckedEnc xs c s) = withSomeAnnotation (someAnnValue xs) (\p -> MkSomeEnc (MkEnc p c s))----- |--- >>> let enctest = unsafeSetPayload () "hello" :: Enc '["TEST"] () T.Text--- >>> displ $ MkSomeEnc enctest--- "Some (MkEnc '[TEST] () (Text hello))"-instance (Show c, Displ str) => Displ (SomeEnc c str) where- displ (MkSomeEnc en) = - "Some (" ++ displ en ++ ")"
− src/Data/TypedEncoding/Internal/Types/UncheckedEnc.hs
@@ -1,52 +0,0 @@-{-# LANGUAGE GADTs #-}-{-# LANGUAGE PolyKinds #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE AllowAmbiguousTypes #-}---- |--- Internal definition of types--module Data.TypedEncoding.Internal.Types.UncheckedEnc where--import Data.Proxy-import Data.TypedEncoding.Internal.Class.Util-import Data.TypedEncoding.Internal.Types.Common---- $setup--- >>> :set -XOverloadedStrings -XMultiParamTypeClasses -XDataKinds -XAllowAmbiguousTypes--- >>> import qualified Data.Text as T----- * UncheckedEnc for recreate, similar to CheckedEnc only not verified---- | Represents some encoded string where encoding was not validated.------ Similar to 'Data.TypedEncoding.Internal.Types.CheckedEnc' but unlike--- @CheckedEnc@ it can contain payloads that have invalid encoding.--- -data UncheckedEnc c str = MkUncheckedEnc [EncAnn] c str deriving (Show, Eq)--toUncheckedEnc :: [EncAnn] -> c -> str -> UncheckedEnc c str-toUncheckedEnc = MkUncheckedEnc--getUncheckedEncAnn :: UncheckedEnc c str -> [EncAnn]-getUncheckedEncAnn (MkUncheckedEnc ann _ _) = ann--verifyAnn :: forall xs c str . SymbolList xs => UncheckedEnc c str -> Either String (UncheckedEnc c str)-verifyAnn x@(MkUncheckedEnc xs _ _) = - let p = Proxy :: Proxy xs- in if symbolVals @ xs == xs- then Right x- else Left $ "UncheckedEnc has not matching annotation " ++ displ xs---- |--- >>> displ $ MkUncheckedEnc ["TEST"] () ("hello" :: T.Text)--- "MkUncheckedEnc [TEST] () (Text hello)"-instance (Show c, Displ str) => Displ (UncheckedEnc c str) where- displ (MkUncheckedEnc xs c s) = - "MkUncheckedEnc " ++ displ xs ++ " " ++ show c ++ " " ++ displ s
− src/Data/TypedEncoding/Internal/Types/Unsafe.hs
@@ -1,29 +0,0 @@-{-# LANGUAGE GADTs #-}-{-# LANGUAGE PolyKinds #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE FlexibleInstances #-}--module Data.TypedEncoding.Internal.Types.Unsafe where--import Data.Proxy-import Data.TypedEncoding.Internal.Types-- --- | Allows to operate within Enc. These are considered unsafe.--- keeping the same list of encodings -newtype Unsafe enc conf str = Unsafe {runUnsafe :: Enc enc conf str} deriving (Show)--withUnsafe :: (Unsafe e c s1 -> Unsafe e c s2) -> Enc e c s1 -> Enc e c s2-withUnsafe f enc = runUnsafe . f $ Unsafe enc--instance Functor (Unsafe enc conf) where- fmap f (Unsafe (MkEnc p c x)) = Unsafe (MkEnc p c (f x))- -instance Applicative (Unsafe enc ()) where- pure = Unsafe . MkEnc Proxy () - Unsafe (MkEnc p c1 f) <*> Unsafe (MkEnc _ c2 x) = Unsafe (MkEnc p () (f x)) --instance Monad (Unsafe enc ()) where - Unsafe (MkEnc _ _ x) >>= f = f x -
− src/Data/TypedEncoding/Internal/Util/TypeLits.hs
@@ -1,146 +0,0 @@---{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE PolyKinds #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE KindSignatures #-}-{-# LANGUAGE AllowAmbiguousTypes #-}----- | TypeLits related utilities.------ Lots of this could be avoided by adding @singletons@ as dependency.------ Uses @symbols@ library for its ToList type family.------ Currently this is spread out in different modules------ * "Data.TypedEncoding.Internal.Class.Util"--- * "Data.TypedEncoding.Internal.Types.SomeAnnotation"------ (TODO) these will need to get consolidated.-module Data.TypedEncoding.Internal.Util.TypeLits where--import GHC.TypeLits--- import Data.Symbol.Utils-import Data.Symbol.Ascii--- import Data.Proxy---- $setup--- >>> :set -XScopedTypeVariables -XTypeFamilies -XKindSignatures -XDataKinds--type family AcceptEq (msg :: ErrorMessage) (c :: Ordering) :: Bool where- AcceptEq _ EQ = True- AcceptEq msg _ = TypeError msg--type family And (b1 :: Bool) (b2 :: Bool) :: Bool where- And 'True 'True = 'True- And _ _ = 'False--type family Or (b1 :: Bool) (b2 :: Bool) :: Bool where- Or 'False 'False = 'False- Or _ _ = 'True--type family Repeat (n :: Nat) (s :: Symbol) :: Symbol where- Repeat 0 s = ""- Repeat n s = AppendSymbol s (Repeat (n - 1) s)--type family Fst (s :: (k,h)) :: k where- Fst ('(,) a _) = a--type family Dupl (s :: k) :: (k,k) where- Dupl a = '(,) a a---- | :kind! Concat (LDrop 6 (ToList "bool: \"r-ban:ff-ff\" | \"r-ban:ffff\""))-type family Concat (s :: [Symbol]) :: Symbol where- Concat '[] = ""- Concat (x ': xs) = AppendSymbol x (Concat xs)---type family Drop (n :: Nat) (s :: Symbol) :: Symbol where- Drop n s = Concat (LDrop n (ToList s))---- TODO create TypeList.List module ?---- | :kind! LDrop 6 (ToList "bool: \"r-ban:ff-ff\" | \"r-ban:ffff\"")-type family LDrop (n :: Nat) (s :: [k]) :: [k] where- LDrop 0 s = s- LDrop n '[] = '[]- LDrop n (x ': xs) = LDrop (n - 1) xs ----- type family DropVerify (n :: Nat) (s :: Symbol) (v :: Symbol) :: Symbol where--- Drop n s v = Concat (LDrop n (ToList s))---- type family DropVerify (n :: Nat) (s :: Symbol) (v :: Symbol) :: Symbol where--- Drop n s v = Concat (LDrop n (ToList s))---- | --- :kind! Take 3 "123456"-type family Take (n :: Nat) (s :: Symbol) :: Symbol where- Take n s = Concat (LTake n (ToList s))---- | :kind! LTake 3 (ToList "123456")-type family LTake (n :: Nat) (s :: [k]) :: [k] where- LTake 0 s = '[]- LTake n '[] = '[]- LTake n (x ': xs) = x ': LTake (n - 1) xs ---- --- :kind! TakeUntil "findme:blah" ":"-type family TakeUntil (s :: Symbol) (stop :: Symbol) :: Symbol where- TakeUntil s stop = Concat (LTakeUntil (ToList s) stop)--type family LTakeUntil (s :: [Symbol]) (stop :: Symbol) :: [Symbol] where- LTakeUntil '[] _ = '[]- LTakeUntil (x ': xs) stop = LTakeUntilHelper (x ': LTakeUntil xs stop) (CmpSymbol x stop)--type family LTakeUntilHelper (s :: [Symbol]) (o :: Ordering) :: [Symbol] where- LTakeUntilHelper '[] _ = '[]- LTakeUntilHelper (x ': xs) 'EQ = '[]- LTakeUntilHelper (x ': xs) _ = (x ': xs)---type family Length (s :: Symbol) :: Nat where - Length x = LLengh (ToList x)--type family LLengh (s :: [k]) :: Nat where- LLengh '[] = 0- LLengh (x ': xs) = 1 + LLengh xs----- |--- >>> :kind! LLast '["1","2","3"]--- ...--- = "3"-type family LLast (s :: [Symbol]) :: Symbol where- LLast '[] = TypeError ('Text "Empty Symbol list not allowed")- LLast '[x] = x- LLast (_ ': xs) = LLast xs----- |--- Concat (Snoc '["1","2","3"] "4") --- ...--- = "1234"-type family Snoc (s :: [k]) (t :: k) :: [k] where- Snoc '[] x = '[x]- Snoc (x ': xs) y = x ': Snoc xs y----- |--- :kind! UnSnoc '["1","2","3"] -type family UnSnoc (s :: [k]) :: ([k], k) where- UnSnoc '[] = TypeError ('Text "Empty list, no last element") - UnSnoc '[x] = '(,) '[] x- UnSnoc (x ': xs) = UnSnocHelper x (UnSnoc xs)---type family UnSnocHelper (s :: k) (t :: ([k], k)) :: ([k], k) where - UnSnocHelper y ('(,) xs x) = '(,) (y ': xs) x
src/Data/TypedEncoding/Unsafe.hs view
@@ -5,8 +5,8 @@ {-# LANGUAGE FlexibleInstances #-} module Data.TypedEncoding.Unsafe (- module Data.TypedEncoding.Internal.Types.Unsafe+ module Data.TypedEncoding.Common.Types.Unsafe ) where -import Data.TypedEncoding.Internal.Types.Unsafe+import Data.TypedEncoding.Common.Types.Unsafe
src/Examples/TypedEncoding/Conversions.hs view
@@ -38,7 +38,7 @@ -- EncodeF SomeErr (Enc xs () Text) (Enc ("enc-B64" ': xs) () Text) -- @ -- --- Then @typed-encoding@ expects @pack@ @encodeF@ to commute:+-- Then @typed-encoding@ expects @pack@ @encodeF@ to commute (if encoding instances exist): -- -- @ -- str -- EncT.pack --> txt@@ -70,7 +70,7 @@ import GHC.TypeLits import qualified Data.TypedEncoding.Conv.ByteString.Char8 as EncB8-import Data.TypedEncoding.Combinators.Restriction.BoundedAlphaNums ()+import Data.TypedEncoding.Instances.Restriction.BoundedAlphaNums () -- $setup -- >>> :set -XDataKinds -XMultiParamTypeClasses -XKindSignatures -XFlexibleInstances -XFlexibleContexts -XOverloadedStrings -XTypeApplications -XScopedTypeVariables@@ -93,7 +93,7 @@ -- ^ Example value to play with -- -- >>> encodeFAll . toEncoding () $ "HeLlo world" :: Either EncodeEx (Enc '["r-ASCII"] () B.ByteString) --- Right (MkEnc Proxy () "HeLlo world")+-- Right (UnsafeMkEnc Proxy () "HeLlo world") Right helloAsciiB = eHelloAsciiB -- ^ above with either removed@@ -101,13 +101,13 @@ helloAsciiT :: Enc '["r-ASCII"] () T.Text helloAsciiT = EncTe.decodeUtf8 helloAsciiB -- ^ --- We use a tween function to the popular 'Data.Text.Encoding.decodeUtf8' +-- We use a tween function of the popular 'Data.Text.Encoding.decodeUtf8' -- from the @test@ package. -- -- Notice the encoding annotation is preserved. -- -- >>> displ $ EncTe.decodeUtf8 helloAsciiB--- "MkEnc '[r-ASCII] () (Text HeLlo world)"+-- "Enc '[r-ASCII] () (Text HeLlo world)" -- * pack and unpack@@ -117,7 +117,7 @@ -- ^ Consider 0-encoding of a 'String', to move it to @Enc '[] () String@ one could try: -- -- >>> displ . EncT.pack $ helloZero--- "MkEnc '[] () (Text Hello)"+-- "Enc '[] () (Text Hello)" -- -- this works, but: -- @@ -133,19 +133,19 @@ -- -- @EncB8.pack@ will not compile unless the encoding is ASCII restricted, this works: -- --- >>> fmap (displ . EncB8.pack) . encodeFAll @(Either EncodeEx) @'["r-ASCII"] $ helloZero--- Right "MkEnc '[r-ASCII] () (ByteString Hello)"+-- >>> fmap (displ . EncB8.pack) . encodeFAll @'["r-ASCII"] @(Either EncodeEx) $ helloZero+-- Right "Enc '[r-ASCII] () (ByteString Hello)" -- -- And the result is a @ByteString@ with bonus annotation describing its content. helloRestricted :: Either EncodeEx (Enc '["r-ban:zzzzz"] () B.ByteString)-helloRestricted = fmap EncB8.pack . runEncoder @'["r-ban"] encodings $ toEncoding () "Hello"--- ^ more interstingly @EncB8.pack@ works fine on "r-" encodings that are subsets of "r-ASCII"--- this example @"r-ban:zzzzz"@ restricts to 5 alapha-numeric charters all < @'z'@+helloRestricted = fmap EncB8.pack . _runEncodings encodings $ toEncoding () "Hello"+-- ^ more interestingly @EncB8.pack@ works fine on "r-" encodings that are subsets of "r-ASCII"+-- this example @"r-ban:zzzzz"@ restricts to 5 alpha-numeric charters all < @\'z\'@ -- -- >>> displ <$> helloRestricted--- Right "MkEnc '[r-ban:zzzzz] () (ByteString Hello)"+-- Right "Enc '[r-ban:zzzzz] () (ByteString Hello)" -- -- Adding @"r-ASCII"@ annotation on this ByteString would have been redundant since @"r-ban:zzzzz"@ is more -- restrictive (see Supersets below).@@ -153,54 +153,30 @@ -- @unpack@, as expected will put us back in a String keeping the annotation -- -- >>> fmap (displ . EncB8.unpack) helloRestricted--- Right "MkEnc '[r-ban:zzzzz] () Hello"+-- Right "Enc '[r-ban:zzzzz] () (String Hello)" -- --- * Supersets--helloUtf8B :: Enc '["r-UTF8"] () B.ByteString-helloUtf8B = injectInto helloAsciiB--- ^ To get UTF8 annotation, instead of doing this: ------ >>> encodeFAll . toEncoding () $ "HeLlo world" :: Either EncodeEx (Enc '["r-UTF8"] () B.ByteString)--- Right (MkEnc Proxy () "HeLlo world")--- --- We should be able to convert the ASCII version we already have.------ This is done using 'Superset' typeclass.------ @injectInto@ method accepts proxy to specify superset to use.------ >>> displ $ injectInto @ "r-UTF8" helloAsciiB--- "MkEnc '[r-UTF8] () (ByteString HeLlo world)"------ Superset is intended for @"r-"@ annotations only, should not be used--- with general encodings like @"enc-B64"@, it assumes that decoding in the superset--- can replace the decoding from injected subset.--- -- * More complex rules helloUtf8B64B :: Enc '["enc-B64", "r-UTF8"] () B.ByteString helloUtf8B64B = encodePart @'["enc-B64"] helloUtf8B --- ^ We put Base64 on a ByteString which adheres to UTF8 layout+-- ^ We Base64 encode a ByteString which adheres to UTF8 layout ----- >>> displ $ encodePart_ (Proxy :: Proxy '["enc-B64"]) helloUtf8B--- "MkEnc '[enc-B64,r-UTF8] () (ByteString SGVMbG8gd29ybGQ=)"+-- >>> displ $ encodePart @'["enc-B64"] helloUtf8B+-- "Enc '[enc-B64,r-UTF8] () (ByteString SGVMbG8gd29ybGQ=)" helloUtf8B64T :: Enc '["enc-B64"] () T.Text helloUtf8B64T = EncT.utf8Demote . EncTe.decodeUtf8 $ helloUtf8B64B -- ^ .. and copy it over to Text. -- -- >>> displ $ EncTe.decodeUtf8 helloUtf8B64B--- "MkEnc '[enc-B64,r-UTF8] () (Text SGVMbG8gd29ybGQ=)"+-- "Enc '[enc-B64,r-UTF8] () (Text SGVMbG8gd29ybGQ=)" -- -- but UTF8 would be redundant in Text so the "r-UTF8" can be dropped: -- -- >>> displ . EncT.utf8Demote . EncTe.decodeUtf8 $ helloUtf8B64B--- "MkEnc '[enc-B64] () (Text SGVMbG8gd29ybGQ=)"+-- "Enc '[enc-B64] () (Text SGVMbG8gd29ybGQ=)" -- -- Conversely moving back to ByteString we need to recover the annotation -- @@ -213,7 +189,7 @@ -- This is not allowed! We need to add the redundant "r-UTF8" back: -- -- >>> displ . EncTe.encodeUtf8 . EncT.utf8Promote $ helloUtf8B64T--- "MkEnc '[enc-B64,r-UTF8] () (ByteString SGVMbG8gd29ybGQ=)"+-- "Enc '[enc-B64,r-UTF8] () (ByteString SGVMbG8gd29ybGQ=)" -- -- To achieve type safety, our @encodeUtf8@ and @decodeUtf8@ require "r-UTF8" annotation. -- But since @Text@ values can always emit @UTF8@ layout, we can simply add and remove@@ -224,7 +200,7 @@ -- ^ 'notTextB' a binary, one that does not even represent a valid UTF8. -- -- >>> encodeAll . toEncoding () $ "\195\177" :: Enc '["enc-B64"] () B.ByteString--- MkEnc Proxy () "w7E="+-- UnsafeMkEnc Proxy () "w7E=" -- -- Decoding it to Text is prevented by the compiler --@@ -242,13 +218,60 @@ -- This can be done, for example, using flattening (see below). +-- * Supersets++helloUtf8B :: Enc '["r-UTF8"] () B.ByteString+helloUtf8B = injectInto helloAsciiB+-- ^ To claim UTF8 on @helloAsciiB@, instead encoding again: +--+-- >>> encodeFAll . toEncoding () $ "HeLlo world" :: Either EncodeEx (Enc '["r-UTF8"] () B.ByteString)+-- Right (UnsafeMkEnc Proxy () "HeLlo world")+-- +-- We should be able to convert the ASCII annotation directly.+--+-- This is done using 'IsSuperset' type family.+--+-- @injectInto@ method accepts proxy to specify superset to use.+--+-- >>> displ $ injectInto @ "r-UTF8" helloAsciiB+-- "Enc '[r-UTF8] () (ByteString HeLlo world)"+--+-- Superset is intended for @"r-"@ annotations only, should not be used+-- with general encodings like @"enc-B64"@, it assumes that decoding in the superset+-- can replace the decoding from injected subset.+++notTextBB64Ascii :: Enc '["r-ASCII", "enc-B64"] () B.ByteString+notTextBB64Ascii = _encodesInto notTextB+-- ^ /Base64/ encoding represents binary data in an ASCII string format.+-- +-- In Haskell, we should be able to express this in types.+--+-- 'EncodingSuperset' class is what specifies this.+--+-- We can use it with '_encodesInto' combinator. +-- 'EncodingSuperset' should not be used directly at the calling site. +--+-- >>> displ (_encodesInto @"r-ASCII" $ notTextB)+-- "Enc '[r-ASCII,enc-B64] () (ByteString w7E=)"+--+-- '_encodesInto' can be used with a superset of the encoding +-- character set as well making it more backward compatible +-- (the definition of @EncodingSuperset "enc-B64" could be made more precise without breaking the code).+--+-- >>> displ (_encodesInto @"r-UTF8" $ notTextB)+-- "Enc '[r-UTF8,enc-B64] () (ByteString w7E=)"+--+++ -- * Lenient recovery lenientSomething :: Enc '["enc-B64-len"] () B.ByteString lenientSomething = recreateAll . toEncoding () $ "abc==CB" -- ^ -- >>> recreateAll . toEncoding () $ "abc==CB" :: Enc '["enc-B64-len"] () B.ByteString--- MkEnc Proxy () "abc==CB"+-- UnsafeMkEnc Proxy () "abc==CB" -- -- The rest of Haskell does lenient decoding, type safety allows this library to use it for recovery. -- lenient algorithms are not partial and automatically fix invalid input:@@ -261,17 +284,17 @@ -- 'EnB64.acceptLenientS' allows to convert "enc-B64-len" to "enc-B64" -- -- >>> displ $ EnB64.acceptLenientS lenientSomething--- "MkEnc '[enc-B64] () (ByteString abc=)"+-- "Enc '[enc-B64] () (ByteString abc=)" -- -- This is now properly encoded data -- -- >>> recreateFAll . toEncoding () $ "abc=" :: Either RecreateEx (Enc '["enc-B64"] () B.ByteString)--- Right (MkEnc Proxy () "abc=")+-- Right (UnsafeMkEnc Proxy () "abc=") -- -- Except the content could be surprising -- -- >>> decodeAll $ EnB64.acceptLenientS lenientSomething--- MkEnc Proxy () "i\183"+-- UnsafeMkEnc Proxy () "i\183" -- * Flattening
src/Examples/TypedEncoding/DiySignEncoding.hs view
@@ -19,7 +19,7 @@ -- Decoding cannot fail unless somehow underlying data has been corrupted. -- -- Such integrity of data should be enforced at boundaries--- (JSON instances, DB retrievals, etc). This can be accomplished using provided 'RecreateF' typeclass.+-- (JSON instances, DB retrievals, etc). This can be accomplished using provided support for /Validation/ or using 'Data.TypedEncoding.Common.Types.UncheckedEnc.UncheckedEnc'. -- -- This still is user decision, the errors during decoding process are considered unexpected 'UnexpectedDecodeErr'. -- In particular user can decide to use unsafe operations with the encoded type. See 'Examples.TypedEncoding.Unsafe'.@@ -66,7 +66,7 @@ -- | Encoded hello world example. -- -- >>> helloSigned--- MkEnc Proxy () "11:Hello World"+-- UnsafeMkEnc Proxy () "11:Hello World" -- -- >>> fromEncoding . decodeAll $ helloSigned -- "Hello World"@@ -99,23 +99,27 @@ -- Left (RecreateEx "my-sign" ("Corrupted Signature")) -- -- >>> recreateFAll . toEncoding () $ payload :: Either RecreateEx (Enc '["my-sign"] () T.Text)--- Right (MkEnc Proxy () "11:Hello World")+-- Right (UnsafeMkEnc Proxy () "11:Hello World") -- | Because encoding function is pure we can create instance of EncodeF -- that is polymorphic in effect @f@. This is done using 'EnT.implTranP' combinator.-instance Applicative f => EncodeF f (Enc xs c T.Text) (Enc ("my-sign" ': xs) c T.Text) where- encodeF = EnT.implEncodeP encodeSign +instance Applicative f => Encode f "my-sign" "my-sign" c T.Text where+ encoding = EnT._implEncodingP encodeSign -- | Decoding allows effectful @f@ to allow for troubleshooting and unsafe payload changes. ----- Implementation simply uses 'EnT.implDecodeF' combinator on the 'asUnexpected' composed with decoding function.+-- Implementation simply uses 'EnT.implDecodingF' combinator on the 'asUnexpected' composed with decoding function. -- 'UnexpectedDecodeErr' has Identity instance allowing for decoding that assumes errors are not possible. -- For debugging purposes or when unsafe changes to "my-sign" @Error UnexpectedDecodeEx@ instance can be used.-instance (UnexpectedDecodeErr f, Applicative f) => DecodeF f (Enc ("my-sign" ': xs) c T.Text) (Enc xs c T.Text) where- decodeF = EnT.implDecodeF (asUnexpected @"my-sign" . decodeSign) +instance (UnexpectedDecodeErr f, Applicative f) => Decode f "my-sign" "my-sign" c T.Text where+ decoding = decMySign +decMySign :: (UnexpectedDecodeErr f, Applicative f) => Decoding f "my-sign" "my-sign" c T.Text+decMySign = EnT.implDecodingF (asUnexpected @"my-sign" . decodeSign) + -- | Recreation allows effectful @f@ to check for tampering with data.--- Implementation simply uses 'EnT.implCheckPrevF' combinator on the recovery function.-instance (RecreateErr f, Applicative f) => RecreateF f (Enc xs c T.Text) (Enc ("my-sign" ': xs) c T.Text) where - checkPrevF = EnT.implCheckPrevF (asRecreateErr @"my-sign" . decodeSign) +-- Implementation simply uses 'EnT.validFromDec' combinator on the recovery function.+instance (RecreateErr f, Applicative f) => Validate f "my-sign" "my-sign" c T.Text where+ validation = EnT.validFromDec decMySign+
src/Examples/TypedEncoding/Overview.hs view
@@ -32,6 +32,7 @@ -- $setup -- >>> :set -XOverloadedStrings -XMultiParamTypeClasses -XDataKinds -XTypeApplications+-- >>> import Data.Functor.Identity -- -- This module contains some ghci friendly values to play with. --@@ -43,23 +44,38 @@ -- | "Hello World" encoded as Base64 -- -- >>> helloB64 --- MkEnc Proxy () "SGVsbG8gV29ybGQ="+-- UnsafeMkEnc Proxy () "SGVsbG8gV29ybGQ=" -- -- >>> displ helloB64--- "MkEnc '[enc-B64] () (ByteString SGVsbG8gV29ybGQ=)"+-- "Enc '[enc-B64] () (ByteString SGVsbG8gV29ybGQ=)" -- -- >>> encodeAll . toEncoding () $ "Hello World" :: Enc '["enc-B64"] () B.ByteString--- MkEnc Proxy () "SGVsbG8gV29ybGQ="------ There is currently an alternative polymorphic way (prototype) to create encodings by /running/ 'Encoder' on 'encodings'.--- This approach works better with more open / dynamic encoding setup it also provides first class @Encoder@. --- In future versions encodeAll will either be improved to provide similar flexibility or will be deprecated and removed.--- --- >>> displ <$> (runEncoder @'["enc-B64"] encodings $ toEncoding () "Hello" :: Either EncodeEx (Enc '["enc-B64"] () B.ByteString))--- Right "MkEnc '[enc-B64] () (ByteString SGVsbG8=)"+-- UnsafeMkEnc Proxy () "SGVsbG8gV29ybGQ=" helloB64 :: Enc '["enc-B64"] () B.ByteString helloB64 = encodeAll . toEncoding () $ "Hello World" +-- | "Hello World" double-Base64 encoded.+-- Notice the same code used as in single encoding, the game is played at type level.+--+-- >>> encodeAll . toEncoding () $ "Hello World" :: Enc '["enc-B64","enc-B64"] () B.ByteString +-- UnsafeMkEnc Proxy () "U0dWc2JHOGdWMjl5YkdRPQ=="+--+-- >>> displ helloB64B64+-- "Enc '[enc-B64,enc-B64] () (ByteString U0dWc2JHOGdWMjl5YkdRPQ==)"+--+-- An alternative version of the above code is this:+--+-- >>> fmap displ . runEncodings' @'["enc-B64","enc-B64"] @'["enc-B64","enc-B64"] @Identity encodings . toEncoding () $ ("Hello World" :: B.ByteString)+-- Identity "Enc '[enc-B64,enc-B64] () (ByteString U0dWc2JHOGdWMjl5YkdRPQ==)"+--+-- This is how @typed-encoding@ works, the "Data.TypedEncoding.Common.Class.Encode.EncodeAll"+-- constraint can be used to get access to list to encodings required by the symbol annotation. +-- 'runEncodings'' executes all the necessary transformations.+--+-- Similar story is true for /decoding/ and /validation/. In these examples we will use shortcut combinators.+helloB64B64 :: Enc '["enc-B64","enc-B64"] () B.ByteString+helloB64B64 = encodeAll . toEncoding () $ "Hello World"+ -- | Previous text decoded from Base64 -- -- >>> fromEncoding . decodeAll $ helloB64 @@ -71,7 +87,7 @@ -- It makes sure that the content satisfies specified encodings. -- -- >>> recreateFAll . toEncoding () $ "SGVsbG8gV29ybGQ=" :: Either RecreateEx (Enc '["enc-B64"] () B.ByteString)--- Right (MkEnc Proxy () "SGVsbG8gV29ybGQ=")+-- Right (UnsafeMkEnc Proxy () "SGVsbG8gV29ybGQ=") -- -- >>> recreateFAll . toEncoding () $ "SGVsbG8gV29ybGQ" :: Either RecreateEx (Enc '["enc-B64"] () B.ByteString) -- Left (RecreateEx "enc-B64" ("invalid padding"))@@ -86,29 +102,24 @@ -- This module is concerned only with the first approach. -- -- >>> let unchecked = toUncheckedEnc ["enc-B64"] () ("SGVsbG8gV29ybGQ=" :: T.Text)--- >>> verifyUncheckedEnc' @'["enc-B64"] unchecked--- Just (Right (MkEnc Proxy () "SGVsbG8gV29ybGQ="))+-- >>> check @'["enc-B64"] @(Either RecreateEx) unchecked+-- Just (Right (UnsafeMkEnc Proxy () "SGVsbG8gV29ybGQ=")) helloB64Recovered :: Either RecreateEx (Enc '["enc-B64"] () B.ByteString) helloB64Recovered = recreateFAll . toEncoding () $ "SGVsbG8gV29ybGQ=" --- | "Hello World" double-Base64 encoded.--- Notice the same code used as in single encoding, the game is played at type level.------ >>> encodeAll . toEncoding () $ "Hello World" :: Enc '["enc-B64","enc-B64"] () B.ByteString --- MkEnc Proxy () "U0dWc2JHOGdWMjl5YkdRPQ=="------ >>> displ helloB64B64--- "MkEnc '[enc-B64,enc-B64] () (ByteString U0dWc2JHOGdWMjl5YkdRPQ==)"-helloB64B64 :: Enc '["enc-B64","enc-B64"] () B.ByteString-helloB64B64 = encodeAll . toEncoding () $ "Hello World" -- | Double Base64 encoded "Hello World" with one layer of encoding removed -- -- >>> decodePart @'["enc-B64"] $ helloB64B64 :: Enc '["enc-B64"] () B.ByteString--- MkEnc Proxy () "SGVsbG8gV29ybGQ="+-- UnsafeMkEnc Proxy () "SGVsbG8gV29ybGQ=" -- -- >>> helloB64B64PartDecode == helloB64 -- True+--+-- @decodePart@ is a convenience function that simply replies decoding 'above' first "enc-B64"+--+-- >>> above @'["enc-B64"] @'["enc-B64"] @'[] decodeAll $ helloB64B64 +-- UnsafeMkEnc Proxy () "SGVsbG8gV29ybGQ=" helloB64B64PartDecode :: Enc '["enc-B64"] () B.ByteString helloB64B64PartDecode = decodePart @'["enc-B64"] helloB64B64 @@ -148,14 +159,14 @@ -- The same code is used as in "enc-" examples to encode (now transform). -- -- >>> encodeAll . toEncoding () $ "Hello World" :: Enc '["do-UPPER"] () T.Text--- MkEnc Proxy () "HELLO WORLD"+-- UnsafeMkEnc Proxy () "HELLO WORLD" helloUPP :: Enc '["do-UPPER"] () T.Text helloUPP = encodeAll . toEncoding () $ "Hello World" -- | Sample compound transformation -- -- >>> encodeAll . toEncoding () $ "HeLLo world" :: Enc '["do-reverse", "do-Title"] () T.Text--- MkEnc Proxy () "dlroW olleH" +-- UnsafeMkEnc Proxy () "dlroW olleH" helloTitleRev :: Enc '["do-reverse", "do-Title"] () T.Text helloTitleRev = encodeAll . toEncoding () $ "HeLLo world" @@ -184,27 +195,32 @@ -- configuration we can also do this: -- -- >>> encodeAll . toEncoding exampleConf $ "HeLLo world" :: Enc '["do-reverse", "do-Title"] Config T.Text--- MkEnc Proxy (Config {sizeLimit = SizeLimit {unSizeLimit = 8}}) "dlroW olleH"+-- UnsafeMkEnc Proxy (Config {sizeLimit = SizeLimit {unSizeLimit = 8}}) "dlroW olleH" -- -- >>> encodeAll . toEncoding exampleConf $ "HeLlo world" :: Enc '["do-size-limit", "do-reverse", "do-Title"] Config T.Text--- MkEnc Proxy (Config {sizeLimit = SizeLimit {unSizeLimit = 8}}) "dlroW ol"+-- UnsafeMkEnc Proxy (Config {sizeLimit = SizeLimit {unSizeLimit = 8}}) "dlroW ol" -- -- Instead, encode previously defined 'helloTitle' by reversing it and adding size limit -- -- >>> encodePart @'["do-size-limit", "do-reverse"] helloTitle :: Enc '["do-size-limit", "do-reverse", "do-Title"] Config T.Text--- MkEnc Proxy (Config {sizeLimit = SizeLimit {unSizeLimit = 8}}) "dlroW ol"+-- UnsafeMkEnc Proxy (Config {sizeLimit = SizeLimit {unSizeLimit = 8}}) "dlroW ol"+--+-- @encodePart@ is simply encodeAll played above "do-Title" encoding:+--+-- >>> above @'["do-Title"] @'[] @'["do-size-limit", "do-reverse"] encodeAll helloTitle+-- UnsafeMkEnc Proxy (Config {sizeLimit = SizeLimit {unSizeLimit = 8}}) "dlroW ol" helloRevLimit :: Enc '["do-size-limit", "do-reverse", "do-Title"] Config T.Text helloRevLimit = encodePart @'["do-size-limit", "do-reverse"] helloTitle -- >>> encodeAll . toEncoding exampleConf $ "HeLlo world" :: Enc '["enc-B64", "do-size-limit"] Config B.ByteString--- MkEnc Proxy (Config {sizeLimit = SizeLimit {unSizeLimit = 8}}) "SGVMbG8gd28="+-- UnsafeMkEnc Proxy (Config {sizeLimit = SizeLimit {unSizeLimit = 8}}) "SGVMbG8gd28=" helloLimitB64 :: Enc '["enc-B64", "do-size-limit"] Config B.ByteString helloLimitB64 = encodeAll . toEncoding exampleConf $ "HeLlo world" -- | ... and we unwrap the B64 part only -- -- >>> decodePart @'["enc-B64"] $ helloLimitB64--- MkEnc Proxy (Config {sizeLimit = SizeLimit {unSizeLimit = 8}}) "HeLlo wo"+-- UnsafeMkEnc Proxy (Config {sizeLimit = SizeLimit {unSizeLimit = 8}}) "HeLlo wo" helloRevLimitParDec :: Enc '["do-size-limit"] Config B.ByteString helloRevLimitParDec = decodePart @'["enc-B64"] helloLimitB64 @@ -224,7 +240,7 @@ -- Note naming thing: "r-" is partial identity ("r-" is from restriction). -- -- >>> encodeFAll . toEncoding () $ "HeLlo world" :: Either EncodeEx (Enc '["r-ASCII"] () B.ByteString) --- Right (MkEnc Proxy () "HeLlo world")+-- Right (UnsafeMkEnc Proxy () "HeLlo world") helloAscii :: Either EncodeEx (Enc '["r-ASCII"] () B.ByteString) helloAscii = encodeFAll . toEncoding () $ "HeLlo world" @@ -232,12 +248,12 @@ -- a better version is here: -- -- >>> encodeFAll . toEncoding () $ "Hello World" :: Either EncodeEx (Enc '["enc-B64", "r-ASCII"] () B.ByteString)--- Right (MkEnc Proxy () "SGVsbG8gV29ybGQ=") +-- Right (UnsafeMkEnc Proxy () "SGVsbG8gV29ybGQ=") helloAsciiB64 :: Either EncodeEx (Enc '["enc-B64", "r-ASCII"] () B.ByteString) helloAsciiB64 = encodeFAll . toEncoding () $ "Hello World" -- | -- >>> decodePart @'["enc-B64"] <$> helloAsciiB64--- Right (MkEnc Proxy () "Hello World")+-- Right (UnsafeMkEnc Proxy () "Hello World") helloAsciiB64PartDec :: Either EncodeEx (Enc '["r-ASCII"] () B.ByteString) helloAsciiB64PartDec = decodePart @'["enc-B64"] <$> helloAsciiB64
src/Examples/TypedEncoding/ToEncString.hs view
@@ -36,8 +36,7 @@ import Data.TypedEncoding import qualified Data.TypedEncoding.Instances.Support as EnT-import Data.TypedEncoding.Instances.Restriction.Common ()-import Data.TypedEncoding.Instances.ToEncString.Common ()+import Data.TypedEncoding.Instances.Restriction.Misc () import Data.TypedEncoding.Instances.Enc.Base64 () import Data.TypedEncoding.Instances.Restriction.ASCII () import Data.TypedEncoding.Instances.Restriction.UTF8 ()@@ -81,10 +80,10 @@ -- In this example @toEncString@ converts 'IpV4' to @Enc '["r-IPv4"] Text@. -- -- This is done with help of existing @"r-Word8-decimal"@ annotation defined--- in "Data.TypedEncoding.Instances.Restriction.Common"+-- in "Data.TypedEncoding.Instances.Restriction.Misc" ----- >>> toEncString @"r-IPv4" @T.Text tstIp--- MkEnc Proxy () "128.1.1.10"+-- >>> toEncString @"r-IPv4" @IpV4 @T.Text tstIp+-- UnsafeMkEnc Proxy () "128.1.1.10" -- -- Implementation is a classic map reduce where reduce is done with help of -- 'EnT.foldEncStr'@@ -92,7 +91,7 @@ -- >>> let fn a b = if b == "" then a else a <> "." <> b -- >>> let reduce = EnT.foldEncStr @'["r-IPv4"] @'["r-Word8-decimal"] () fn -- >>> displ . reduce . fmap toEncString $ tstIp--- "MkEnc '[r-IPv4] () 128.1.1.10"+-- "Enc '[r-IPv4] () (String 128.1.1.10)" -- -- Note lack of type safety here, the same code would work just fine if we added -- 5th field to 'IpV4F' constructor. @@ -107,8 +106,8 @@ -- @HList@ could be used for record types with heterogeneous fields. -- -- Currently, 'type-encoding' library does not have these types in scope. -instance ToEncString "r-IPv4" T.Text Identity IpV4 where- toEncStringF = Identity . reduce . map+instance ToEncString Identity "r-IPv4" "r-IPv4" IpV4 T.Text where+ toEncF = Identity . reduce . map where map :: IpV4F Word8 -> IpV4F (Enc '["r-Word8-decimal"] () T.Text) map = fmap toEncString @@ -117,23 +116,23 @@ -- | ----- >>> let enc = toEncString @"r-IPv4" @T.Text tstIp--- >>> fromEncString @IpV4 enc+-- >>> let enc = toEncString @"r-IPv4" @IpV4 @T.Text tstIp+-- >>> fromEncString @"r-IPv4" @IpV4 enc -- IpV4F {oct1 = 128, oct2 = 1, oct3 = 1, oct4 = 10} -- -- To get 'IpV4' out of the string we need to reverse previous @reduce@. -- This is currently done using helper 'EnT.splitPayload' combinator. -- -- >>> EnT.splitPayload @ '["r-Word8-decimal"] (T.splitOn $ T.pack ".") $ enc --- [MkEnc Proxy () "128",MkEnc Proxy () "1",MkEnc Proxy () "1",MkEnc Proxy () "10"]+-- [UnsafeMkEnc Proxy () "128",UnsafeMkEnc Proxy () "1",UnsafeMkEnc Proxy () "1",UnsafeMkEnc Proxy () "10"] -- -- The conversion of a list to IpV4F needs handle errors but these errors -- are considered unexpected. -- -- Note, again, the error condition exposed by this implementation could have been avoided -- if 'EnT.splitPayload' returned fixed size @Vect 4@.-instance (UnexpectedDecodeErr f, Applicative f) => FromEncString IpV4 f T.Text "r-IPv4" where - fromEncStringF = fmap map . unreduce+instance (UnexpectedDecodeErr f, Applicative f) => FromEncString f "r-IPv4" "r-IPv4" IpV4 T.Text where + fromEncF = fmap map . unreduce where unreduce :: Enc '["r-IPv4"] () T.Text -> f (IpV4F (Enc '["r-Word8-decimal"] () T.Text)) unreduce = asUnexpected @"r-IPv4" . recover . EnT.splitPayload @ '["r-Word8-decimal"] (T.splitOn ".") @@ -205,10 +204,10 @@ -- -- We can play 'Alternative' ('<|>') game (we acually use @Maybe@) with final option being a 'RecreateEx' error: ----- >>> verifyUncheckedEnc' @'["enc-B64","r-ASCII"] $ unchecked+-- >>> check @'["enc-B64","r-ASCII"] @(Either RecreateEx) $ unchecked -- Nothing--- >>> verifyUncheckedEnc' @'["enc-B64","r-UTF8"] $ unchecked--- Just (Right (MkEnc Proxy () "U29tZSBVVEY4IFRleHQ="))+-- >>> check @'["enc-B64","r-UTF8"] @(Either RecreateEx) $ unchecked+-- Just (Right (UnsafeMkEnc Proxy () "U29tZSBVVEY4IFRleHQ=")) -- -- Since the data is heterogeneous (each piece has a different encoding annotation), we need wrap the result in another plain ADT: 'CheckedEnc'. -- @@ -236,11 +235,11 @@ runAlternatives' (fromMaybe def) [try1, try2, try3, try4, try5] body where unchecked = toUncheckedEnc (parseHeader parth) () - try1 = fmap (fmap toCheckedEnc) . verifyUncheckedEnc' @'["enc-B64","r-UTF8"] . unchecked- try2 = fmap (fmap toCheckedEnc) . verifyUncheckedEnc' @'["enc-B64","r-ASCII"] . unchecked- try3 = fmap (fmap toCheckedEnc) . verifyUncheckedEnc' @'["r-ASCII"] . unchecked- try4 = fmap (fmap toCheckedEnc) . verifyUncheckedEnc' @'["r-UTF8"] . unchecked- try5 = fmap (fmap toCheckedEnc) . verifyUncheckedEnc' @'["enc-B64"] . unchecked+ try1 = fmap (fmap toCheckedEnc) . check @'["enc-B64","r-UTF8"] . unchecked+ try2 = fmap (fmap toCheckedEnc) . check @'["enc-B64","r-ASCII"] . unchecked+ try3 = fmap (fmap toCheckedEnc) . check @'["r-ASCII"] . unchecked+ try4 = fmap (fmap toCheckedEnc) . check @'["r-UTF8"] . unchecked+ try5 = fmap (fmap toCheckedEnc) . check @'["enc-B64"] . unchecked def = Left $ recreateErrUnknown ("Invalid Header " ++ show parth) @@ -251,7 +250,7 @@ -- (like trying to decode base 64 on a plain text part). -- -- >>> decodeB64ForTextOnly <$> recreateEncoding tstEmail--- Right (SimplifiedEmailF {emailHeader = "Some Header", parts = [MkCheckedEnc ["enc-B64"] () "U29tZSBBU0NJSSBUZXh0",MkCheckedEnc ["r-ASCII"] () "Some ASCII Text",MkCheckedEnc ["r-UTF8"] () "Some UTF8 Text",MkCheckedEnc ["r-ASCII"] () "Some ASCII plain text"]})+-- Right (SimplifiedEmailF {emailHeader = "Some Header", parts = [UnsafeMkCheckedEnc ["enc-B64"] () "U29tZSBBU0NJSSBUZXh0",UnsafeMkCheckedEnc ["r-ASCII"] () "Some ASCII Text",UnsafeMkCheckedEnc ["r-UTF8"] () "Some UTF8 Text",UnsafeMkCheckedEnc ["r-ASCII"] () "Some ASCII plain text"]}) -- -- Combinator @fromCheckedEnc \@'["enc-B64", "r-UTF8"]@ acts as a selector and picks only the -- @["enc-B64", "r-UTF8"]@ values from our 'Traversable' type. @@ -262,7 +261,7 @@ -- -- >>> let piece = unsafeCheckedEnc ["enc-B64","r-ASCII"] () ("U29tZSBBU0NJSSBUZXh0" :: B.ByteString) -- >>> displ piece--- "MkCheckedEnc [enc-B64,r-ASCII] () (ByteString U29tZSBBU0NJSSBUZXh0)"+-- "UnsafeMkCheckedEnc [enc-B64,r-ASCII] () (ByteString U29tZSBBU0NJSSBUZXh0)" -- -- This code will not pick it up: --@@ -272,12 +271,12 @@ -- But this one will: -- -- >>> fromCheckedEnc @ '["enc-B64", "r-ASCII"] $ piece--- Just (MkEnc Proxy () "U29tZSBBU0NJSSBUZXh0")+-- Just (UnsafeMkEnc Proxy () "U29tZSBBU0NJSSBUZXh0") -- -- so we can apply the decoding on the selected piece -- -- >>> fmap (toCheckedEnc . decodePart @'["enc-B64"]) . fromCheckedEnc @ '["enc-B64", "r-ASCII"] $ piece--- Just (MkCheckedEnc ["r-ASCII"] () "Some ASCII Text")+-- Just (UnsafeMkCheckedEnc ["r-ASCII"] () "Some ASCII Text") decodeB64ForTextOnly :: SimplifiedEmailEncB -> SimplifiedEmailEncB decodeB64ForTextOnly = fmap (runAlternatives fromMaybe [tryUtf8, tryAscii])
src/Examples/TypedEncoding/Unsafe.hs view
@@ -41,7 +41,7 @@ -- >>> let payload = getPayload exAsciiT -- >>> let newPayload = payload <> " some extra stuff" -- >>> recreateFAll . toEncoding () $ newPayload :: Either RecreateEx (Enc '["r-ASCII"] () T.Text)--- Right (MkEnc Proxy () "HELLO some extra stuff")+-- Right (UnsafeMkEnc Proxy () "HELLO some extra stuff") -- -- Alternatively, 'UncheckedEnc' type can be used in recreation, see 'Examples.TypedEncoding.Overview' -- @@ -58,9 +58,9 @@ -- the internal payload. -- -- >>> exAsciiTE--- Right (MkEnc Proxy () "HELLO")+-- Right (UnsafeMkEnc Proxy () "HELLO") -- >>> exAsciiTE >>= pure . Unsafe.withUnsafe (fmap T.toLower)--- Right (MkEnc Proxy () "hello")+-- Right (UnsafeMkEnc Proxy () "hello") -- -- Example uses of 'T.toLower' within encoded data -- this operation is safe for ASCII restriction@@ -75,7 +75,7 @@ -- >>> let Right hELLO = exAsciiTE -- >>> let Right hello = toLowerAscii -- >>> displ $ Unsafe.runUnsafe ((<>) <$> Unsafe.Unsafe hELLO <*> Unsafe.Unsafe hello)--- "MkEnc '[r-ASCII] () (Text HELLOhello)"+-- "Enc '[r-ASCII] () (Text HELLOhello)" appendAscii :: Either EncodeEx (Enc '["r-ASCII"] () T.Text) appendAscii = do hELLO <- exAsciiTE
+ test/Test/Bc/ASCIISpec.hs view
@@ -0,0 +1,76 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# OPTIONS_GHC -fwarn-unused-imports #-}++-- | Verified backward compatibility of ASCII encoder changes in v0.3+module Test.Bc.ASCIISpec where++import qualified Data.TypedEncoding.Instances.Restriction.ASCII as ASCII+import Data.TypedEncoding.Common.Class.Util.StringConstraints++import qualified Data.Text as T+import qualified Data.Text.Lazy as TL+import qualified Data.List as L++import qualified Data.ByteString.Char8 as B8+import qualified Data.ByteString.Lazy.Char8 as BL8++import Data.Char+import Control.Arrow++import Test.QuickCheck.Instances.ByteString ()+import Test.QuickCheck.Instances.Text ()+import Test.QuickCheck+import Test.QuickCheck.Property+import Test.Hspec+++equivalProperty :: Eq str => (str -> Either NonAsciiChar str) -> (str -> Either NonAsciiChar str) -> str -> Property+equivalProperty alg1 alg2 str = property $ liftBool (alg1 str == alg2 str)++forwardCompatible :: (Char8Find str, Eq str) => (str -> Either NonAsciiChar str) -> str -> Property+forwardCompatible = equivalProperty newImpl+++type NonAsciiChar = ASCII.NonAsciiChar+++oldImplS = oldEncodeImpl L.partition L.head L.null+oldImplT = oldEncodeImpl T.partition T.head T.null+oldImplTL = oldEncodeImpl TL.partition TL.head TL.null+oldImplB = oldEncodeImpl (\p -> B8.filter p &&& B8.filter (not . p)) B8.head B8.null+oldImplBL = oldEncodeImpl (\p -> BL8.filter p &&& BL8.filter (not . p)) BL8.head BL8.null+++newImpl :: Char8Find str => str -> Either NonAsciiChar str+newImpl = ASCII.encImpl++oldEncodeImpl :: + ((Char -> Bool) -> a -> (a, a))+ -> (a -> Char)+ -> (a -> Bool)+ -> a+ -> Either NonAsciiChar a+oldEncodeImpl partitionf headf nullf t = + let (tascii, nonascii) = partitionf isAscii t + in if nullf nonascii + then Right tascii+ else Left . ASCII.NonAsciiChar $ headf nonascii ++spec :: Spec +spec = + describe "ASCII backward compapatibilty check" $ do+ it "String" $ property $ forwardCompatible oldImplS + it "Text" $ property $ forwardCompatible oldImplT+ it "Lazy.Text" $ property $ forwardCompatible oldImplTL+ it "ByteString" $ property $ forwardCompatible oldImplB+ it "Lazy.ByteString" $ property $ forwardCompatible oldImplBL++runSpec :: IO ()+runSpec = hspec spec
test/Test/Bc/IsStringRSpec.hs view
@@ -29,9 +29,8 @@ import Data.Word import Data.Either import Data.TypedEncoding-import Data.TypedEncoding.Internal.Class.IsStringR -import Data.TypedEncoding.Instances.Restriction.Common ()-import Data.TypedEncoding.Instances.ToEncString.Common ()+import Data.TypedEncoding.Common.Class.IsStringR +import Data.TypedEncoding.Instances.Restriction.Misc () newtype MyStr = MyStr String deriving (Eq, Show) @@ -63,13 +62,13 @@ it "Word8 fromEncString err works" $ fromEncStringTestErr `shouldSatisfy` isLeft it "EncodeF works" $- (encodeFAll @(Either EncodeEx) @'["r-Word8-decimal"] . toEncoding () $ tstWord8) `shouldSatisfy` isRight + (encodeFAll @'["r-Word8-decimal"] @(Either EncodeEx) . toEncoding () $ tstWord8) `shouldSatisfy` isRight it "EncodeF err works" $- (encodeFAll @(Either EncodeEx) @'["r-Word8-decimal"] . toEncoding () $ tstNotWord8') `shouldSatisfy` isLeft+ (encodeFAll @'["r-Word8-decimal"] @(Either EncodeEx). toEncoding () $ tstNotWord8') `shouldSatisfy` isLeft it "RecreateF works" $- (recreateFAll @(Either RecreateEx) @'["r-Word8-decimal"] . toEncoding () $ tstWord8) `shouldSatisfy` isRight + (recreateFAll @'["r-Word8-decimal"] @(Either RecreateEx) . toEncoding () $ tstWord8) `shouldSatisfy` isRight it "RecreateF err works" $- (recreateFAll @(Either RecreateEx) @'["r-Word8-decimal"] . toEncoding () $ tstNotWord8) `shouldSatisfy` isLeft+ (recreateFAll @'["r-Word8-decimal"] @(Either RecreateEx) . toEncoding () $ tstNotWord8) `shouldSatisfy` isLeft it "DecodeF works" $ (fromEncoding . decodeAll $ tstWord8Enc) `shouldBe` tstWord8
typed-encoding.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 2bc32a22f6f7e7c49333ee187e608cb1c101c6711ae2fa7e0ee92c5d524e97ed+-- hash: f053eb403362652b43e8d0680f5e3a36c5eff26bb2f32fe383f0ab4612ba3c0f name: typed-encoding-version: 0.2.2.0+version: 0.3.0.0 synopsis: Type safe string transformations description: See README.md in the project github repository. category: Data, Text@@ -30,9 +30,34 @@ library exposed-modules: Data.TypedEncoding- Data.TypedEncoding.Combinators.Restriction.Bool- Data.TypedEncoding.Combinators.Restriction.BoundedAlphaNums- Data.TypedEncoding.Combinators.Restriction.Common+ Data.TypedEncoding.Combinators.Common+ Data.TypedEncoding.Combinators.Decode+ Data.TypedEncoding.Combinators.Encode+ Data.TypedEncoding.Combinators.Encode.Experimental+ Data.TypedEncoding.Combinators.Promotion+ Data.TypedEncoding.Combinators.ToEncStr+ Data.TypedEncoding.Combinators.Unsafe+ Data.TypedEncoding.Combinators.Validate+ Data.TypedEncoding.Common.Class+ Data.TypedEncoding.Common.Class.Decode+ Data.TypedEncoding.Common.Class.Encode+ Data.TypedEncoding.Common.Class.IsStringR+ Data.TypedEncoding.Common.Class.Superset+ Data.TypedEncoding.Common.Class.Util+ Data.TypedEncoding.Common.Class.Util.StringConstraints+ Data.TypedEncoding.Common.Class.Validate+ Data.TypedEncoding.Common.Types+ Data.TypedEncoding.Common.Types.CheckedEnc+ Data.TypedEncoding.Common.Types.Common+ Data.TypedEncoding.Common.Types.Decoding+ Data.TypedEncoding.Common.Types.Enc+ Data.TypedEncoding.Common.Types.Exceptions+ Data.TypedEncoding.Common.Types.SomeAnnotation+ Data.TypedEncoding.Common.Types.SomeEnc+ Data.TypedEncoding.Common.Types.UncheckedEnc+ Data.TypedEncoding.Common.Types.Unsafe+ Data.TypedEncoding.Common.Types.Validation+ Data.TypedEncoding.Common.Util.TypeLits Data.TypedEncoding.Conv.ByteString.Char8 Data.TypedEncoding.Conv.ByteString.Lazy.Char8 Data.TypedEncoding.Conv.Text@@ -42,31 +67,18 @@ Data.TypedEncoding.Instances.Do.Sample Data.TypedEncoding.Instances.Enc.Base64 Data.TypedEncoding.Instances.Restriction.ASCII- Data.TypedEncoding.Instances.Restriction.Common+ Data.TypedEncoding.Instances.Restriction.Bool+ Data.TypedEncoding.Instances.Restriction.BoundedAlphaNums+ Data.TypedEncoding.Instances.Restriction.Misc Data.TypedEncoding.Instances.Restriction.UTF8 Data.TypedEncoding.Instances.Support- Data.TypedEncoding.Instances.ToEncString.Common- Data.TypedEncoding.Internal.Class- Data.TypedEncoding.Internal.Class.Decode- Data.TypedEncoding.Internal.Class.Encode- Data.TypedEncoding.Internal.Class.Encoder- Data.TypedEncoding.Internal.Class.IsStringR- Data.TypedEncoding.Internal.Class.Recreate- Data.TypedEncoding.Internal.Class.Superset- Data.TypedEncoding.Internal.Class.Util- Data.TypedEncoding.Internal.Class.Util.StringConstraints- Data.TypedEncoding.Internal.Combinators- Data.TypedEncoding.Internal.Instances.Combinators- Data.TypedEncoding.Internal.Types- Data.TypedEncoding.Internal.Types.CheckedEnc- Data.TypedEncoding.Internal.Types.Common- Data.TypedEncoding.Internal.Types.Enc- Data.TypedEncoding.Internal.Types.SomeAnnotation- Data.TypedEncoding.Internal.Types.SomeEnc- Data.TypedEncoding.Internal.Types.UncheckedEnc- Data.TypedEncoding.Internal.Types.Unsafe+ Data.TypedEncoding.Instances.Support.Common+ Data.TypedEncoding.Instances.Support.Decode+ Data.TypedEncoding.Instances.Support.Encode+ Data.TypedEncoding.Instances.Support.Helpers+ Data.TypedEncoding.Instances.Support.Unsafe+ Data.TypedEncoding.Instances.Support.Validate Data.TypedEncoding.Internal.Util- Data.TypedEncoding.Internal.Util.TypeLits Data.TypedEncoding.Unsafe Examples.TypedEncoding Examples.TypedEncoding.Conversions@@ -113,6 +125,7 @@ main-is: Spec.hs other-modules: Test.Bc.IsStringRSpec+ Test.Bc.ASCIISpec hs-source-dirs: test ghc-options: -threaded -rtsopts -with-rtsopts=-N