typed-encoding 0.4.0.0 → 0.4.1.0
raw patch · 21 files changed
+324/−48 lines, 21 files
Files
- ChangeLog.md +26/−1
- README.md +1/−0
- src/Data/TypedEncoding.hs +73/−1
- src/Data/TypedEncoding/Combinators/Decode.hs +2/−2
- src/Data/TypedEncoding/Common/Class/Superset.hs +35/−16
- src/Data/TypedEncoding/Common/Types/Common.hs +20/−2
- src/Data/TypedEncoding/Common/Types/Decoding.hs +15/−3
- src/Data/TypedEncoding/Common/Types/UncheckedEnc.hs +4/−0
- src/Data/TypedEncoding/Common/Types/Validation.hs +7/−2
- src/Data/TypedEncoding/Conv.hs +7/−3
- src/Data/TypedEncoding/Conv/ByteString/Char8.hs +33/−1
- src/Data/TypedEncoding/Conv/ByteString/Lazy/Char8.hs +20/−0
- src/Data/TypedEncoding/Conv/Text.hs +5/−2
- src/Data/TypedEncoding/Conv/Text/Encoding.hs +10/−0
- src/Data/TypedEncoding/Instances/Enc/Base64.hs +15/−0
- src/Data/TypedEncoding/Instances/Restriction/BoundedAlphaNums.hs +1/−1
- src/Data/TypedEncoding/Instances/Restriction/D76.hs +1/−1
- src/Data/TypedEncoding/Instances/Restriction/UTF8.hs +18/−7
- src/Data/TypedEncoding/Instances/Support/Validate.hs +21/−2
- src/Examples/TypedEncoding/Conversions.hs +8/−2
- typed-encoding.cabal +2/−2
ChangeLog.md view
@@ -6,7 +6,32 @@ - (post 0.3) "enc-B64" will be moved to a different package (more distant goal) - Improved constrains in `Data.TypedEncoding.Conv` modules -##+## 0.4.1++- Code Changes. Backward compatible+ - changed order in `IsSuperset` definition to speed up compilation of of more common cases (there is a small chance that it impacts GHC error messages)+ - Faster "r-UTF8", possible issue is changed error message in case ByteString is invalid.+ - Deprecated 'validFromEnc' for its confusing name+ - Deprecated `runDecodings` in favor of consistently named `runDecodings'`+ - Deprecated `runDecoding` in favor of consistently named `runDecoding'`+ - Deprecated `runValidation` in favor of consistently named `runValidation'`++- Documentation / code comment fixes and improvements.++- New functionality `validRFromEnc'` replacing 'validFromEnc' confusing name+ - `propCompEncoding` property+ - `propSupersetCheck` property+ - `propSafeDecoding'` properties+ - `propSafeValidatedDecoding` properties+ - `IsEnc` type family and `Encoding` constraint+ - `getUncheckedPayload` function+ - `pack` and `unpack` overloads in `Data.TypedEncoding.Conv.ByteString.Char8`++- Fixes+ - corrected `propEncodesInto'` property test specification+++## 0.4 - Breaking - IsSupersetOpen type family type arguments have changed
README.md view
@@ -72,6 +72,7 @@ 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.
src/Data/TypedEncoding.hs view
@@ -1,4 +1,8 @@ +{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE DataKinds #-} -- | -- = Overview --@@ -149,7 +153,14 @@ -- * @UncheckedEnc@ is an /untyped/ version of Enc that represents not validated encoding , module Data.TypedEncoding.Common.Types.UncheckedEnc- ++ -- * Laws / properties++ , propSafeDecoding' + , _propSafeDecoding+ , propSafeValidatedDecoding'+ , _propSafeValidatedDecoding+ -- * Classes , module Data.TypedEncoding.Common.Class @@ -187,3 +198,64 @@ import Data.TypedEncoding.Combinators.Unsafe import Data.TypedEncoding.Combinators.ToEncStr import Data.TypedEncoding.Combinators.Promotion+++-- | +-- Main property that encodings are expected to enforce.+--+-- Decoding is safe and can use @Identity@ instance of 'UnexpectedDecodeErr'.+--+-- Errors are handled during the encoding phase.+propSafeDecoding' :: forall alg nm c str. (Eq c, Eq str) =>+ Encoding (Either EncodeEx) nm alg c str + -> Decoding (Either UnexpectedDecodeEx) nm alg c str + -> c + -> str + -> Bool+propSafeDecoding' encf decf c str = Right enc0 == edec+ where + enc0 = toEncoding c str+ eenc = runEncoding' @alg encf enc0+ edec = case eenc of + Right enc -> either (Left . show) Right $ runDecoding' @alg decf enc+ Left enc -> Right enc0 -- quick and dirty, magically decode all encoded failures +++_propSafeDecoding :: forall nm c str alg . (Algorithm nm alg, Eq c, Eq str) => + Encoding (Either EncodeEx) nm alg c str + -> Decoding (Either UnexpectedDecodeEx) nm alg c str + -> c + -> str + -> Bool+_propSafeDecoding = propSafeDecoding' @(AlgNm nm)++-- |+-- Similar to 'propSafeDecoding'' but 'Validation' based.+-- 'Validation' acts as 'Decoding' recovering original payload value.+-- Recovering with validation keeps the encoded value and that value+-- is supposed to decode without error. +--+-- Expects input of encoded values+propSafeValidatedDecoding' :: forall alg nm c str. (Eq c, Eq str) =>+ Validation (Either RecreateEx) nm alg c str + -> Decoding (Either UnexpectedDecodeEx) nm alg c str + -> c + -> str + -> Bool+propSafeValidatedDecoding' validf decf c str = edec == echeck+ where + enc0 = toEncoding c str+ valfs = validf `ConsV` ZeroV+ eenc = recreateWithValidations @'[alg] @'[nm] valfs enc0 + (edec :: Either String str, echeck :: Either String str) = case eenc of + Right enc -> (either (Left . show) (Right . getPayload) $ runDecoding' @alg decf enc+ , either (Left . show) (Right . getPayload) $ runValidation' @alg validf enc)+ Left _ -> (Left "not-recovered", Left "not-recovered") ++_propSafeValidatedDecoding :: forall nm c str alg. (Algorithm nm alg, Eq c, Eq str) =>+ Validation (Either RecreateEx) nm alg c str + -> Decoding (Either UnexpectedDecodeEx) nm alg c str + -> c + -> str + -> Bool+_propSafeValidatedDecoding = propSafeValidatedDecoding' @(AlgNm nm)
src/Data/TypedEncoding/Combinators/Decode.hs view
@@ -52,12 +52,12 @@ -- * Convenience combinators which mimic v0.2 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)+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 +decodeFAll' = runDecodings' @algs @nms @f decodings -- | --
src/Data/TypedEncoding/Common/Class/Superset.hs view
@@ -54,7 +54,7 @@ -- @IsSuperset bigger smaller@ reads as @bigger@ is a superset of @smaller@ -- -- Note, no IsSuperset "r-UNICODE.D76" "r-CHAR8" even though the numeric range of D76 includes all CHAR8 bytes.--- This is more 'nominal' decision that prevents certain unwanted conversions from being possible.+-- This is more /nominal/ decision that prevents certain unwanted conversions from being possible. -- -- @since 0.2.2.0 type family IsSuperset (y :: Symbol) (x :: Symbol) :: Bool where@@ -63,15 +63,15 @@ IsSuperset "r-UTF8" "r-UTF8" = 'True IsSuperset "r-CHAR8" "r-ASCII" = 'True -- "r-CHAR8" is phantom, no explicit instances so it does not need reflexive case IsSuperset "r-CHAR8" "r-ByteRep" = 'True- IsSuperset "r-CHAR8" x = Or (IsSuperset "r-ASCII" x) (IsSupersetOpen "r-CHAR8" x (TakeUntil x ":") (ToList x)) IsSuperset "r-UNICODE.D76" "r-UNICODE.D76" = 'True IsSuperset "r-UNICODE.D76" "r-ASCII" = 'True IsSuperset "r-UNICODE.D76" x = Or (IsSuperset "r-CHAR8" x) (IsSupersetOpen "r-UNICODE.D76" x (TakeUntil x ":") (ToList x))+ IsSuperset "r-CHAR8" x = Or (IsSuperset "r-ASCII" x) (IsSupersetOpen "r-CHAR8" x (TakeUntil x ":") (ToList x)) IsSuperset y x = IsSupersetOpen y x (TakeUntil x ":") (ToList x) --- backward compatible r-CHAR8--- IsSuperset "r-CHAR8" x = Or (IsSuperset "r-ASCII" x) (IsSupersetOpen "r-CHAR8" (TakeUntil x ":") (ToList x))+-- TODO introduce "r-NODEC" which does not decode + -- | -- @since 0.2.2.0 type family IsSupersetOpen (big :: Symbol) (nm :: Symbol) (alg :: Symbol) (nmltrs :: [Symbol]) :: Bool@@ -101,7 +101,18 @@ -> Encoding (Either EncodeEx) s algs () str -> str -> Bool-propSuperset' encb encs str = +propSuperset' = propSupersetCheck @algb @algs+ +propSuperset_ :: forall b s str algb algs. (Superset b s, Eq str, AlgNm b ~ algb, AlgNm s ~ algs) => Encoding (Either EncodeEx) b algb () str -> Encoding (Either EncodeEx) s algs () str -> str -> Bool+propSuperset_ = propSupersetCheck @algb @algs++propSupersetCheck :: forall algb algs b s str . (Eq str) + => + Encoding (Either EncodeEx) b algb () str + -> Encoding (Either EncodeEx) s algs () str + -> str + -> Bool+propSupersetCheck encb encs str = case (isLeft rb, isLeft rs) of (True, False) -> False (False, False) -> pb == ps@@ -112,9 +123,6 @@ rs = runEncoding' @algs encs . toEncoding () $ str pb = either (const str) id $ getPayload <$> rb ps = either (const str) id $ getPayload <$> rs- -propSuperset_ :: forall b s str algb algs. (Superset b s, Eq str, AlgNm b ~ algb, AlgNm s ~ algs) => Encoding (Either EncodeEx) b algb () str -> Encoding (Either EncodeEx) s algs () str -> str -> Bool-propSuperset_ = propSuperset' @algb @algs -- | -- IsSuperset is not intended for @"enc-"@ encodings. This class is.@@ -140,14 +148,12 @@ -- Actual tests in the project /test/ suite. propEncodesInto' :: forall algb algr b r str . (EncodingSuperset b, r ~ EncSuperset b, Eq str) => Encoding (Either EncodeEx) b algb () str -> Encoding (Either EncodeEx) r algr () str -> str -> Bool propEncodesInto' encb encr str = - case rb of- Left _ -> True- Right enc -> case runEncoding' @algr encr . toEncoding () $ getPayload enc of- Right _ -> True- Left _ -> False - where- rb = runEncoding' @algb encb . toEncoding () $ str -+ case runEncoding' @algb encb . toEncoding () $ str of+ Right r -> case runEncoding' @algr encr . toEncoding () $ getPayload r of+ Left _ -> False+ Right _ -> True+ Left _ -> True + propEncodesInto_ :: forall b r str algb algr. ( EncodingSuperset b , r ~ EncSuperset b@@ -159,6 +165,19 @@ -> str -> Bool propEncodesInto_ = propEncodesInto' @algb @algr+++-- | Checks if first encoding exceptions less often than second (has bigger domain).+propCompEncoding :: forall algb algr b r str . Encoding (Either EncodeEx) b algb () str -> Encoding (Either EncodeEx) r algr () str -> str -> Bool+propCompEncoding encb encr str = + case rr of+ Left _ -> True+ Right _ -> case runEncoding' @algb encb . toEncoding () $ str of+ Right _ -> True+ Left _ -> False + where+ rr = runEncoding' @algr encr . toEncoding () $ str + -- | -- Aggregate version of 'EncodingSuperset'
src/Data/TypedEncoding/Common/Types/Common.hs view
@@ -26,14 +26,22 @@ type EncAnn = String -- | --- Contraint for "r-" annotations.+-- Constraint for "r-" annotations. -- -- @since 0.3.0.0 type Restriction s = (KnownSymbol s, IsR s ~ 'True) + -- | --- Contraint for algorithm name.+-- Constraint for "r-" annotations. --+-- @since 0.4.1.0+type EncodingAnn s = (KnownSymbol s, IsEnc s ~ 'True)+++-- | +-- Constraint for algorithm name.+-- -- @since 0.3.0.0 type Algorithm nm alg = AlgNm nm ~ alg @@ -87,3 +95,13 @@ type family RemoveRs (s :: [Symbol]) :: [Symbol] where RemoveRs '[] = '[] RemoveRs (x ': xs) = If (OrdBool (CmpSymbol "r-" (Take 2 x))) (RemoveRs xs) (x ': RemoveRs xs) +++-- | +-- >>> :kind! IsEnc "enc-boo"+-- ...+-- = 'True+--+-- @since 0.4.1.0 +type family IsEnc (s :: Symbol) :: Bool where+ IsEnc s = AcceptEq ('Text "Not enc- encoding " ':<>: ShowType s ) (CmpSymbol "enc-" (Take 4 s))
src/Data/TypedEncoding/Common/Types/Decoding.hs view
@@ -52,6 +52,13 @@ 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 +{-# DEPRECATED runDecoding "Use runDecoding'" #-}++-- |+-- @since 0.3.0.0+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@@ -76,11 +83,16 @@ -- | -- @since 0.3.0.0 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 = +runDecodings = runDecodings' @algs @nms++{-# DEPRECATED runDecodings "Use runDecodings'" #-}+++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. --
src/Data/TypedEncoding/Common/Types/UncheckedEnc.hs view
@@ -44,6 +44,10 @@ getUncheckedEncAnn :: UncheckedEnc c str -> [EncAnn] getUncheckedEncAnn (MkUncheckedEnc ann _ _) = ann ++getUncheckedPayload :: forall c str . UncheckedEnc c str -> str+getUncheckedPayload (MkUncheckedEnc _ _ str) = str+ -- | -- @since 0.2.0.0 verifyAnn :: forall xs c str . SymbolList xs => UncheckedEnc c str -> Either String (UncheckedEnc c str)
src/Data/TypedEncoding/Common/Types/Validation.hs view
@@ -54,13 +54,18 @@ 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 +{-# DEPRECATED runValidation "Use runValidation''" #-}++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 -- -- @since 0.3.0.0 _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)+_runValidation = runValidation' @(AlgNm nm) -- | -- Wraps a list of @Validation@ elements.@@ -82,7 +87,7 @@ 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+ let re :: f (Enc _ c str) = runValidation' fn enc in re >>= runValidationChecks xs
src/Data/TypedEncoding/Conv.hs view
@@ -80,6 +80,9 @@ -- @ -- -- @"r-UNICODE.D76"@ and @"r-UTF8"@ is considered redundant for @T.Text@ and can be added or dropped as needed.+--+-- (This library currently assumes that @"r-UTF8"@ includes the UNICODE.D76 restriction. This works well with+-- assumptions made by 'T.Text'). -- -- Corresponding pairs reverse, this should be clear since the types are restricted to what @T.Text@ can store or to -- how @B8.Char@ works.@@ -99,15 +102,16 @@ -- There are many character set encodings that utilize one byte (/CHAR8/) and /UTF8/ is different from all of them -- but it backward compatible only within the /ASCII/ range of chars @ < 127@. So the intersection should be /ASCII/, let us check that: --+-- -- @ -- ghci> :t ETE.encodeUtf8 . ET.pack @'["r-ASCII"] -- EncTe.encodeUtf8 . EncT.pack @'["r-ASCII"] -- :: Enc [Symbol] ((':) Symbol "r-ASCII" ('[] Symbol)) c String--- -> Enc [Symbol] ((':) Symbol "r-ASCII" ('[] Symbol)) c B.ByteString+-- -> Enc [Symbol] ((':) Symbol "r-ASCII" ('[] Symbol)) c B8.ByteString ----- ghci> :t EncB8.pack @'["r-ASCII"]+-- ghci> :t EB8.pack @'["r-ASCII"] -- :: Enc [Symbol] ((':) Symbol "r-ASCII" ('[] Symbol)) c String--- -> Enc [Symbol] ((':) Symbol "r-ASCII" ('[] Symbol)) c B.ByteString+-- -> Enc [Symbol] ((':) Symbol "r-ASCII" ('[] Symbol)) c B8.ByteString -- @ -- -- They both accept that common denominator.
src/Data/TypedEncoding/Conv/ByteString/Char8.hs view
@@ -14,14 +14,16 @@ -- $setup -- >>> :set -XDataKinds -XTypeApplications -XOverloadedStrings+-- >>> import Data.TypedEncoding.Instances.Enc.Base64 () -- | -- Type safer version of 'Data.ByteString.Char8.pack'. -- -- This assumes that each of the encodings in @xs@ work equivalently in @String@ and @ByteString@.+-- See "Examples.TypedEncoding.Conversions". -- -- This function also (currently) does not insist that @xs@ is a valid encoding stack for @ByteString@. --- This will be reexamined in the future, possibly offering alternatives with different safety levels.+-- This will be reexamined in the future, possibly offering stricter overloads of @pack@. -- -- Expected encoding stack @xs@ needs to have @"r-" as last element and it needs to be more restrictive -- than "r-CHAR8" (String cannot have chars @> 255@). Otherwise any encodings other than "r-" need to @@ -47,6 +49,21 @@ ) => Enc xs c String -> Enc xs c B8.ByteString pack = unsafeChangePayload B8.pack +-- |+-- Version of pack that works without first "r-" encoding.+--+-- >>> displ $ pack'' (unsafeSetPayload () "SGVsbG8gV29ybGQ=" :: Enc '["enc-B64"] () String)+-- "Enc '[enc-B64] () (ByteString SGVsbG8gV29ybGQ=)"+--+-- @since 0.4.1.0+pack'' :: (+ Knds.UnSnoc xs ~ '(,) ys y+ , EncodingAnn y+ , encs ~ RemoveRs ys+ , AllEncodeInto "r-CHAR8" encs+ ) => Enc xs c String -> Enc xs c B8.ByteString+pack'' = unsafeChangePayload B8.pack+ -- | @unpack@ on encoded strings. -- -- See 'pack'@@ -65,3 +82,18 @@ ) => Enc xs c B8.ByteString -> Enc xs c String unpack = unsafeChangePayload B8.unpack ++-- |+-- Version of pack that works without first "r-" encoding+--+-- >>> displ $ unpack'' (unsafeSetPayload () "SGVsbG8gV29ybGQ=" :: Enc '["enc-B64"] () B8.ByteString)+-- "Enc '[enc-B64] () (String SGVsbG8gV29ybGQ=)"+--+-- @since 0.4.1.0+unpack'' :: (+ Knds.UnSnoc xs ~ '(,) ys y+ , EncodingAnn y+ , encs ~ RemoveRs ys+ , AllEncodeInto "r-CHAR8" encs+ ) => Enc xs c B8.ByteString -> Enc xs c String+unpack'' = unsafeChangePayload B8.unpack
src/Data/TypedEncoding/Conv/ByteString/Lazy/Char8.hs view
@@ -34,3 +34,23 @@ , AllEncodeInto "r-CHAR8" encs ) => Enc xs c BL8.ByteString -> Enc xs c String unpack = unsafeChangePayload BL8.unpack ++-- | +-- Lazy version of 'Data.TypedEncoding.Conv.ByteString.Char8.pack'''.+pack'' :: (+ Knds.UnSnoc xs ~ '(,) ys y+ , EncodingAnn y+ , encs ~ RemoveRs ys+ , AllEncodeInto "r-CHAR8" encs+ ) => Enc xs c String -> Enc xs c BL8.ByteString+pack'' = unsafeChangePayload BL8.pack++-- | +-- Lazy version of 'Data.TypedEncoding.Conv.ByteString.Char8.unpack'''.+unpack'' :: (+ Knds.UnSnoc xs ~ '(,) ys y+ , EncodingAnn y+ , encs ~ RemoveRs ys+ , AllEncodeInto "r-CHAR8" encs+ ) => Enc xs c BL8.ByteString -> Enc xs c String+unpack'' = unsafeChangePayload BL8.unpack
src/Data/TypedEncoding/Conv/Text.hs view
@@ -16,7 +16,9 @@ -- >>> :set -XDataKinds -XTypeFamilies -XTypeApplications --- | This assumes that each of the encodings in @xs@ work work equivalently in @String@ and @Text@. +-- | This assumes that each of the encodings in @xs@ work work equivalently in @String@ and @Text@.+-- See discussion in "Examples.TypedEncoding.Conversions" and +-- "Data.TypedEncoding.Conv.ByteString.Char8.pack" pack :: ( Knds.UnSnoc xs ~ '(,) ys y , Superset "r-UNICODE.D76" y @@ -25,7 +27,8 @@ ) => Enc xs c String -> Enc xs c T.Text pack = unsafeChangePayload T.pack --- | This assumes that each of the encodings in @xs@ work work equivalently in @String@ and @Text@. +-- | This assumes that each of the encodings in @xs@ work work equivalently in @String@ and @Text@.+-- This is similar to the assumptions made in 'pack'. unpack :: ( Knds.UnSnoc xs ~ '(,) ys y , Superset "r-UNICODE.D76" y
src/Data/TypedEncoding/Conv/Text/Encoding.hs view
@@ -53,6 +53,16 @@ -- | -- With given constraints 'decodeUtf8' and 'encodeUtf8' can be used on subsets of @"r-UTF8"@ --+-- Note: For example, the @ByteString@ encoding of @"\xd800"@ (@11101101 10100000 10000000@ @ed a0 80@) is considered invalid /UTF8/ by the 'T.Text' library+-- To be consistent we make the same assumption of also restricting representable Unicode chars as in /Unicode.D76/.+--+-- >>> TE.decodeUtf8 "\237\160\128"+-- "*** Exception: Cannot decode byte '\xed': Data.Text.Internal.Encoding.decodeUtf8: Invalid UTF-8 stream+-- +-- The "\xdfff" case (@11101101 10111111 10111111@ @ed bf bf@):+-- >>> TE.decodeUtf8 "\237\191\191"+-- "*** Exception: Cannot decode byte '\xed': Data.Text.Internal.Encoding.decodeUtf8: Invalid UTF-8 stream+-- -- >>> displ . decodeUtf8 $ (unsafeSetPayload () "Hello" :: Enc '["r-ASCII"] () B.ByteString) -- "Enc '[r-ASCII] () (Text Hello)" --
src/Data/TypedEncoding/Instances/Enc/Base64.hs view
@@ -32,7 +32,15 @@ -- >>> import Test.QuickCheck -- >>> import Test.QuickCheck.Instances.Text() -- >>> import Test.QuickCheck.Instances.ByteString()+-- >>> :{ +-- instance Arbitrary (UncheckedEnc () B.ByteString) where +-- arbitrary = do+-- payload <- frequency [ (5, fmap (getPayload . encodeAll @'["enc-B64"] @(). toEncoding ()) $ arbitrary) +-- , (1, arbitrary)]+-- pure $ toUncheckedEnc ["enc-B64"] () payload+-- :} + ----------------- -- * Conversions -----------------@@ -92,6 +100,7 @@ encoding = encB64B -- |+-- -- @since 0.3.0.0 encB64B :: Applicative f => Encoding f "enc-B64" "enc-B64" c B.ByteString encB64B = _implEncodingP B64.encode@@ -130,6 +139,10 @@ -- making undetectable changes, but error handling at this stage -- could verify that email was corrupted. --+-- prop> _propSafeDecoding @"enc-B64" @() @B.ByteString encB64B decB64B ()+-- +-- prop> _propSafeValidatedDecoding @"enc-B64" @() @B.ByteString validation decB64B () . getUncheckedPayload @() @B.ByteString+-- -- @since 0.3.0.0 decB64B :: (UnexpectedDecodeErr f, Applicative f) => Decoding f "enc-B64" "enc-B64" c B.ByteString decB64B = _implDecodingF (asUnexpected @"enc-B64" . B64.decode)@@ -140,6 +153,8 @@ decoding = decB64BL -- |+-- prop> _propSafeDecoding @"enc-B64" @() @BL.ByteString encB64BL decB64BL+-- -- @since 0.3.0.0 decB64BL :: (UnexpectedDecodeErr f, Applicative f) => Decoding f "enc-B64" "enc-B64" c BL.ByteString decB64BL = _implDecodingF (asUnexpected @"enc-B64" . BL64.decode)
src/Data/TypedEncoding/Instances/Restriction/BoundedAlphaNums.hs view
@@ -85,7 +85,7 @@ -- * 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+ validation = validRFromEnc' @"r-ban" encFBan -- * Implementation
src/Data/TypedEncoding/Instances/Restriction/D76.hs view
@@ -12,7 +12,7 @@ -- U+D800 to U+DFFF inclusive) -- -- Note, no IsSuperset "r-UNICODE.D76" "r-CHAR8" mapping even though the numeric range of D76 includes all CHAR8 bytes.--- This is more 'nominal' decision that prevents certain unwanted conversions from being possible.+-- This is more /nominal/ decision that prevents certain unwanted conversions from being possible. -- -- Similarly no IsSuperset "r-UNICODE.D76" "r-ByteRep", this annotation acts as a guard to what can go into @Text@. --
src/Data/TypedEncoding/Instances/Restriction/UTF8.hs view
@@ -10,8 +10,11 @@ {-# LANGUAGE PartialTypeSignatures #-} --{-# LANGUAGE TypeApplications #-} --- | 'UTF-8' encoding+-- | 'UTF-8' encoding with additional assumption of conforming to Unicode.D76. --+-- @"r-UTF-8"@ basically defines restriction on @ByteString@ that is needed for+-- conversion to @Text@ to work.+-- -- @since 0.1.0.0 module Data.TypedEncoding.Instances.Restriction.UTF8 where @@ -49,7 +52,6 @@ prxyUtf8 = Proxy :: Proxy "r-UTF8" --- TODO these may need rethinking (performance) -- | UTF8 encodings are defined for ByteString only as that would not make much sense for Text --@@ -57,7 +59,7 @@ -- 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))+-- Left (EncodeEx "r-UTF8" (Cannot decode byte '\xc3': ... -- -- Following test uses 'verEncoding' helper that checks that bytes are encoded as Right iff they are valid UTF8 bytes --@@ -77,14 +79,16 @@ instance Encode (Either EncodeEx) "r-UTF8" "r-UTF8" c BL.ByteString where encoding = encUTF8BL :: Encoding (Either EncodeEx) "r-UTF8" "r-UTF8" c BL.ByteString ++-- using lazy decoding to detect errors seems to be the fastest option that is not super hard to code+-- TODO v0.4.1 test performance+ 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." #-}+encUTF8B = _implEncodingEx (implVerifyR (TEL.decodeUtf8' . BL.fromStrict)) 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." #-}+encUTF8BL = _implEncodingEx (implVerifyR TEL.decodeUtf8') -- * Decoding @@ -106,3 +110,10 @@ verEncoding :: B.ByteString -> Either err B.ByteString -> Bool verEncoding bs (Left _) = isLeft . TE.decodeUtf8' $ bs verEncoding bs (Right _) = isRight . TE.decodeUtf8' $ bs++-- | private implementation helper+implVerifyR :: (a -> Either err b) -> a -> Either err a+implVerifyR fn a = + case fn a of + Left err -> Left err+ Right _ -> Right a
src/Data/TypedEncoding/Instances/Support/Validate.hs view
@@ -42,15 +42,22 @@ -- | -- @since 0.3.0.0 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 +validR = validRFromEnc' @nm @nm ++-- TODO rename for consistency to _+ -- | Can cause slow compilation if used -- -- @since 0.3.0.0 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 +validR' = validRFromEnc' @alg @nm ++ -- |+-- This should be used with "r-" validations only+-- -- @since 0.3.0.0 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) @@ -64,5 +71,17 @@ in UnsafeMkEnc Proxy conf . getPayload <$> re +{-# DEPRECATED validFromEnc' "Use validR' instead (valid for r- encodings only)" #-} ++validRFromEnc' :: 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 +validRFromEnc' (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/Examples/TypedEncoding/Conversions.hs view
@@ -30,7 +30,7 @@ -- -- Conversions aim at providing type safety when moving between encoded string-like types. ----- __The assumption__ made by `typed-encoding` is that encodings work in equivalent way independently of the payload type.+-- __The assumption__ made by `typed-encoding` is that @"enc-"@ encodings work in an equivalent way independently of the payload type. -- For example, if the following instances exist: -- -- @@@ -51,8 +51,14 @@ -- -- (@unpack@ and $decode$ are expected to satisfy similar diagrams, not shown) ----- Basically, it should not matter which type we run the encoding on (other than performance cost).+-- Basically, it should not matter which type we run the encoding (or decoding) on (other than performance cost). --+-- Note that, as a consequence, multi-byte encodings (such as @enc-UTF8@ - available in /typed-encoding-encoding/ package) +-- that encode a Unicode characters into several bytes cannot be decoded+-- in @ByteString@ as this would violate @EncB8.pack@ and @EncB8.unpack@ consistency.+--+-- Also note that this requirement is concerned about @"enc-"@ encodings, @"r-"@ encodings are much simpler to reason about +-- in conversions. -- -- This module also discusses concepts of __Superset__ (for @"r-"@ encodings), __leniency__, and __flattening__. module Examples.TypedEncoding.Conversions where
typed-encoding.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: b0f10958b977104fdf20e2e9d875921fdf79c64e1c92b5243e8ed86b49519deb+-- hash: 401999e1b4cdce2d71a8bd02cb005f7f4632cd9bb8f1629e5d54b0a5be76f2d4 name: typed-encoding-version: 0.4.0.0+version: 0.4.1.0 synopsis: Type safe string transformations description: See README.md in the project github repository. category: Data, Text