autodocodec 0.0.1.1 → 0.1.0.0
raw patch · 4 files changed
+340/−102 lines, 4 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Autodocodec: disjointMatchChoiceCodec :: Codec context input output -> Codec context input' output -> (newInput -> Either input input') -> Codec context newInput output
+ Autodocodec: disjointMatchChoicesCodec :: [(input -> Maybe input, Codec context input output)] -> Codec context input output -> Codec context input output
+ Autodocodec: matchChoiceCodecAs :: Union -> Codec context input output -> Codec context input' output -> (newInput -> Either input input') -> Codec context newInput output
+ Autodocodec: matchChoicesCodecAs :: Union -> [(input -> Maybe input, Codec context input output)] -> Codec context input output -> Codec context input output
+ Autodocodec.Codec: disjointMatchChoiceCodec :: Codec context input output -> Codec context input' output -> (newInput -> Either input input') -> Codec context newInput output
+ Autodocodec.Codec: disjointMatchChoicesCodec :: [(input -> Maybe input, Codec context input output)] -> Codec context input output -> Codec context input output
+ Autodocodec.Codec: matchChoiceCodecAs :: Union -> Codec context input output -> Codec context input' output -> (newInput -> Either input input') -> Codec context newInput output
+ Autodocodec.Codec: matchChoicesCodecAs :: Union -> [(input -> Maybe input, Codec context input output)] -> Codec context input output -> Codec context input output
- Autodocodec: matchChoiceCodec :: Codec context input output -> Codec context input output -> (newInput -> Either input input) -> Codec context newInput output
+ Autodocodec: matchChoiceCodec :: Codec context input output -> Codec context input' output -> (newInput -> Either input input') -> Codec context newInput output
- Autodocodec.Codec: matchChoiceCodec :: Codec context input output -> Codec context input output -> (newInput -> Either input input) -> Codec context newInput output
+ Autodocodec.Codec: matchChoiceCodec :: Codec context input output -> Codec context input' output -> (newInput -> Either input input') -> Codec context newInput output
Files
- CHANGELOG.md +7/−0
- autodocodec.cabal +1/−1
- src/Autodocodec.hs +4/−0
- src/Autodocodec/Codec.hs +328/−101
CHANGELOG.md view
@@ -1,5 +1,12 @@ # Changelog +## [0.1.0.0] - 2022-05-03++### Changed+* Generalise type of `matchChoiceCodec` to allow for two different input types.+* Add disjoint versions of `matchChoiceCodec` and `matchChoicesCodec`.+* Functions `enumCodec`, `stringConstCodec`, and `shownBoundedEnumCodec` now produce disjoint codecs.+ ## [0.0.1.1] - 2022-04-26 ### Added
autodocodec.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack name: autodocodec-version: 0.0.1.1+version: 0.1.0.0 synopsis: Self-documenting encoder and decoder homepage: https://github.com/NorfairKing/autodocodec#readme bug-reports: https://github.com/NorfairKing/autodocodec/issues
src/Autodocodec.hs view
@@ -104,7 +104,11 @@ -- *** Choice matchChoiceCodec,+ disjointMatchChoiceCodec,+ matchChoiceCodecAs, matchChoicesCodec,+ disjointMatchChoicesCodec,+ matchChoicesCodecAs, -- *** Adding documentation to a codec (<?>),
src/Autodocodec/Codec.hs view
@@ -431,14 +431,28 @@ Codec context newInput newOutput dimapCodec f g = bimapCodec (Right . f) g --- | Forward-compatible version of 'PureCodec'+-- | Produce a value without parsing any part of an 'Object'. --+-- This function exists to implement @Applicative (ObjectCodec input)@.+--+--+-- ==== API Note+--+-- This is a forward-compatible version of 'PureCodec'.+-- -- > pureCodec = PureCodec pureCodec :: output -> ObjectCodec input output pureCodec = PureCodec --- | Forward-compatible version of 'ApCodec'+-- | Sequentially apply two codecs that parse part of an 'Object'. --+-- This function exists to implement @Applicative (ObjectCodec input)@.+--+--+-- ==== API Note+--+-- This is a forward-compatible version of 'ApCodec'.+-- -- > apCodec = ApCodec apCodec :: ObjectCodec input (output -> newOutput) -> ObjectCodec input output -> ObjectCodec input newOutput apCodec = ApCodec@@ -447,8 +461,10 @@ pure = pureCodec (<*>) = apCodec --- | Also allow @null@ during decoding of a 'Maybe' value.+-- | Maybe codec --+-- This can be used to also allow @null@ during decoding of a 'Maybe' value.+-- -- During decoding, also accept a @null@ value as 'Nothing'. -- During encoding, encode as usual. --@@ -473,9 +489,10 @@ Nothing -> Left () Just r -> Right r --- | Forward-compatible version of 'EitherCodec'+-- | Either codec ----- > eitherCodec = EitherCodec PossiblyJointUnion+-- During encoding, parse a value according to either codec.+-- During encoding, use the corresponding codec to encode either value. -- -- === 'HasCodec' instance for sum types --@@ -488,6 +505,7 @@ -- -- @No ->@ use 'disjointEitherCodec'. --+-- -- === Example usage -- -- >>> let c = eitherCodec codec codec :: JSONCodec (Either Int String)@@ -497,80 +515,29 @@ -- String "hello" -- >>> JSON.parseMaybe (parseJSONVia c) (String "world") :: Maybe (Either Int String) -- Just (Right "world")-eitherCodec ::- -- |- Codec context input1 output1 ->- -- |- Codec context input2 output2 ->- -- |- Codec context (Either input1 input2) (Either output1 output2)-eitherCodec = possiblyJointEitherCodec---- | Forward-compatible version of 'EitherCodec PossiblyJointUnion' ----- > possiblyJointEitherCodec = EitherCodec PossiblyJointUnion ----- === 'HasCodec' instance for sum types with an encoding that is not disjoint.------ The 'eitherCodec' can be used to implement 'HasCodec' instances for sum types.--- If you just have two codecs that you want to try in order, while parsing, you can do this:------ >>> :{--- data Ainur--- = Valar Text Text--- | Maiar Text--- deriving (Show, Eq)--- :}------ >>> :{--- instance HasCodec Ainur where--- codec =--- dimapCodec f g $--- possiblyJointEitherCodec--- (object "Valar" $--- (,)--- <$> requiredField "domain" "Domain which the Valar rules over" .= fst--- <*> requiredField "name" "Name of the Valar" .= snd)--- (object "Maiar" $ requiredField "name" "Name of the Maiar")--- where--- f = \case--- Left (domain, name) -> Valar domain name--- Right name -> Maiar name--- g = \case--- Valar domain name -> Left (domain, name)--- Maiar name -> Right name--- :}------ Note that this encoding is indeed not disjoint, because a @Valar@ object can--- parse as a @Maiar@ value.------ >>> toJSONViaCodec (Valar "Stars" "Varda")--- Object (fromList [("domain",String "Stars"),("name",String "Varda")])--- >>> toJSONViaCodec (Maiar "Sauron")--- Object (fromList [("name",String "Sauron")])--- >>> JSON.parseMaybe parseJSONViaCodec (Object (Compat.fromList [("name",String "Olorin")])) :: Maybe Ainur--- Just (Maiar "Olorin")------ === WARNING+-- ==== API Note ----- The order of the codecs in a 'possiblyJointEitherCodec' matters.+-- This is a forward-compatible version of 'possiblyJointEitherCodec'. ----- In the above example, decoding works as expected because the @Valar@ case is parsed first.--- If the @Maiar@ case were first in the 'possiblyJointEitherCodec', then--- @Valar@ could never be parsed.-possiblyJointEitherCodec ::+-- > eitherCodec = possiblyJointEitherCodec+eitherCodec :: -- | Codec context input1 output1 -> -- | Codec context input2 output2 -> -- | Codec context (Either input1 input2) (Either output1 output2)-possiblyJointEitherCodec = EitherCodec PossiblyJointUnion+eitherCodec = possiblyJointEitherCodec --- | Forward-compatible version of 'EitherCodec DisjointUnion'+-- | Possibly joint either codec ----- > disjointEitherCodec = EitherCodec DisjointUnion+-- During encoding, parse a value according to either codec.+-- During encoding, use the corresponding codec to encode either value. --+-- This codec is for the case in which parsing must be disjoint.+-- -- === 'HasCodec' instance for sum types with an encoding that is definitely disjoint. -- -- The 'eitherCodec' can be used to implement 'HasCodec' instances sum types@@ -603,6 +570,7 @@ -- >>> JSON.parseMaybe parseJSONViaCodec (String "of the roses") :: Maybe War -- Just (OtherWar "of the roses") --+-- -- === WARNING -- -- If it turns out that the encoding of a value is not disjoint, decoding may@@ -619,12 +587,20 @@ -- >>> toJSONVia c (Right 6) -- Number 6.0 --+-- -- === Example usage -- -- >>> toJSONVia (disjointEitherCodec (codec :: JSONCodec Int) (codec :: JSONCodec String)) (Left 5) -- Number 5.0 -- >>> toJSONVia (disjointEitherCodec (codec :: JSONCodec Int) (codec :: JSONCodec String)) (Right "hello") -- String "hello"+--+--+-- ==== API Note+--+-- This is a forward-compatible version of 'EitherCodec DisjointUnion'.+--+-- > disjointEitherCodec = EitherCodec DisjointUnion disjointEitherCodec :: -- | Codec context input1 output1 ->@@ -634,6 +610,78 @@ Codec context (Either input1 input2) (Either output1 output2) disjointEitherCodec = EitherCodec DisjointUnion +-- | Possibly joint either codec+--+-- During encoding, parse a value according to either codec.+-- During encoding, use the corresponding codec to encode either value.+--+-- This codec is for the case in which parsing may not be disjoint.+--+-- === 'HasCodec' instance for sum types with an encoding that is not disjoint.+--+-- The 'eitherCodec' can be used to implement 'HasCodec' instances for sum types.+-- If you just have two codecs that you want to try in order, while parsing, you can do this:+--+-- >>> :{+-- data Ainur+-- = Valar Text Text+-- | Maiar Text+-- deriving (Show, Eq)+-- :}+--+-- >>> :{+-- instance HasCodec Ainur where+-- codec =+-- dimapCodec f g $+-- possiblyJointEitherCodec+-- (object "Valar" $+-- (,)+-- <$> requiredField "domain" "Domain which the Valar rules over" .= fst+-- <*> requiredField "name" "Name of the Valar" .= snd)+-- (object "Maiar" $ requiredField "name" "Name of the Maiar")+-- where+-- f = \case+-- Left (domain, name) -> Valar domain name+-- Right name -> Maiar name+-- g = \case+-- Valar domain name -> Left (domain, name)+-- Maiar name -> Right name+-- :}+--+-- Note that this encoding is indeed not disjoint, because a @Valar@ object can+-- parse as a @Maiar@ value.+--+-- >>> toJSONViaCodec (Valar "Stars" "Varda")+-- Object (fromList [("domain",String "Stars"),("name",String "Varda")])+-- >>> toJSONViaCodec (Maiar "Sauron")+-- Object (fromList [("name",String "Sauron")])+-- >>> JSON.parseMaybe parseJSONViaCodec (Object (Compat.fromList [("name",String "Olorin")])) :: Maybe Ainur+-- Just (Maiar "Olorin")+--+--+-- === WARNING+--+-- The order of the codecs in a 'possiblyJointEitherCodec' matters.+--+-- In the above example, decoding works as expected because the @Valar@ case is parsed first.+-- If the @Maiar@ case were first in the 'possiblyJointEitherCodec', then+-- @Valar@ could never be parsed.+--+--+-- ==== API Note+--+-- This is a forward-compatible version of 'EitherCodec PossiblyJointUnion'.+--+-- > possiblyJointEitherCodec = EitherCodec PossiblyJointUnion+possiblyJointEitherCodec ::+ -- |+ Codec context input1 output1 ->+ -- |+ Codec context input2 output2 ->+ -- |+ Codec context (Either input1 input2) (Either output1 output2)+possiblyJointEitherCodec = EitherCodec PossiblyJointUnion+ -- | Map a codec's input and output types. -- -- This function allows you to have the parsing fail in a new way.@@ -642,6 +690,7 @@ -- -- This function is like 'BimapCodec' except it also combines one level of a nested 'BimapCodec's. --+-- -- === Example usage -- -- logLevelCodec :: JSONCodec LogLevel@@ -651,36 +700,61 @@ (newInput -> oldInput) -> Codec context oldInput oldOutput -> Codec context newInput newOutput-bimapCodec f g = \case- BimapCodec f' g' c -> BimapCodec (f' >=> f) (g' . g) c- c -> BimapCodec f g c+bimapCodec f g =+ -- We distinguish between a 'BimapCodec' and a non-'BimapCodec' just so that+ -- we don't introduce additional layers that we can already combine anyway.+ \case+ BimapCodec f' g' c -> BimapCodec (f' >=> f) (g' . g) c+ c -> BimapCodec f g c --- | Forward-compatible version of 'ArrayOfCodec' without a name+-- | Vector codec ----- > vectorCodec = ArrayOfCodec Nothing+-- Build a codec for vectors of values from a codec for a single value. --+-- -- === Example usage -- -- >>> toJSONVia (vectorCodec codec) (Vector.fromList ['a','b']) -- Array [String "a",String "b"]+--+--+-- ==== API Note+--+-- This is a forward-compatible version of 'ArrayOfCodec' without a name.+--+-- > vectorCodec = ArrayOfCodec Nothing vectorCodec :: ValueCodec input output -> ValueCodec (Vector input) (Vector output) vectorCodec = ArrayOfCodec Nothing --- | Build a codec for lists of values from a codec for a single value.+-- | List codec --+-- Build a codec for lists of values from a codec for a single value.+--+-- -- === Example usage -- -- >>> toJSONVia (listCodec codec) ['a','b'] -- Array [String "a",String "b"]+--+--+-- ==== API Note+--+-- This is the list version of 'vectorCodec'. listCodec :: ValueCodec input output -> ValueCodec [input] [output] listCodec = dimapCodec V.toList V.fromList . vectorCodec -- | Build a codec for nonempty lists of values from a codec for a single value. --+-- -- === Example usage -- -- >>> toJSONVia (nonEmptyCodec codec) ('a' :| ['b']) -- Array [String "a",String "b"]+--+--+-- ==== API Note+--+-- This is the non-empty list version of 'vectorCodec'. nonEmptyCodec :: ValueCodec input output -> ValueCodec (NonEmpty input) (NonEmpty output) nonEmptyCodec = bimapCodec parseNonEmptyList NE.toList . listCodec where@@ -688,11 +762,15 @@ Nothing -> Left "Expected a nonempty list, but got an empty list." Just ne -> Right ne --- | Like 'listCodec', except the values may also be simplified as a single value.+-- | Single or list codec --+-- This codec behaves like 'listCodec', except the values may also be+-- simplified as a single value.+-- -- During parsing, a single element may be parsed as the list of just that element. -- During rendering, a list with only one element will be rendered as just that element. --+-- -- === Example usage -- -- >>> let c = singleOrListCodec codec :: JSONCodec [Int]@@ -705,6 +783,7 @@ -- >>> JSON.parseMaybe (parseJSONVia c) (Array [Number 5, Number 6]) :: Maybe [Int] -- Just [5,6] --+-- -- === WARNING -- -- If you use nested lists, for example when the given value codec is also a@@ -719,11 +798,15 @@ [v] -> Left v vs -> Right vs --- | Like 'nonEmptyCodec', except the values may also be simplified as a single value.+-- | Single or nonempty list codec --+-- This codec behaves like 'nonEmptyCodec', except the values may also be+-- simplified as a single value.+-- -- During parsing, a single element may be parsed as the list of just that element. -- During rendering, a list with only one element will be rendered as just that element. --+-- -- === Example usage -- -- >>> let c = singleOrNonEmptyCodec codec :: JSONCodec (NonEmpty Int)@@ -736,10 +819,15 @@ -- >>> JSON.parseMaybe (parseJSONVia c) (Array [Number 5, Number 6]) :: Maybe (NonEmpty Int) -- Just (5 :| [6]) --+-- -- === WARNING -- -- If you use nested lists, for example when the given value codec is also a -- 'nonEmptyCodec', you may get in trouble with ambiguities during parsing.+--+-- ==== API Note+--+-- This is a nonempty version of 'singleOrListCodec'. singleOrNonEmptyCodec :: ValueCodec input output -> ValueCodec (NonEmpty input) (NonEmpty output) singleOrNonEmptyCodec c = dimapCodec f g $ eitherCodec c $ nonEmptyCodec c where@@ -860,6 +948,8 @@ ObjectCodec output output optionalFieldWithOmittedDefaultWith' key c defaultValue = OptionalKeyWithOmittedDefaultCodec key c defaultValue Nothing +-- | Like 'optionalFieldWithOmittedDefaultWith', but the value may also be+-- @null@ and that will be interpreted as the default value. optionalFieldOrNullWithOmittedDefaultWith :: Eq output => -- | Key@@ -878,6 +968,8 @@ Nothing -> defaultValue g v = if v == defaultValue then Nothing else Just v +-- | Like 'optionalFieldWithOmittedDefaultWith'', but the value may also be+-- @null@ and that will be interpreted as the default value. optionalFieldOrNullWithOmittedDefaultWith' :: Eq output => -- | Key@@ -942,8 +1034,11 @@ -- | Encode a 'HashMap', and decode any 'HashMap'. ----- Forward-compatible version of 'HashMapCodec' --+-- ==== API Note+--+-- This is a forward-compatible version of 'HashMapCodec'.+-- -- > hashMapCodec = HashMapCodec hashMapCodec :: (Eq k, Hashable k, FromJSONKey k, ToJSONKey k) =>@@ -955,8 +1050,11 @@ -- | Encode a 'Map', and decode any 'Map'. ----- Forward-compatible version of 'MapCodec' --+-- ==== API Note+--+-- This is a forward-compatible version of 'MapCodec'.+-- -- > mapCodec = MapCodec mapCodec :: (Ord k, FromJSONKey k, ToJSONKey k) =>@@ -982,20 +1080,23 @@ Nothing -> dimapCodec KM.fromHashMap KM.toHashMap . hashMapCodec #endif --- | Forward-compatible version of 'ValueCodec'------ > valueCodec = ValueCodec+-- | Codec for a 'JSON.Value' -- -- This is essentially your escape-hatch for when you would normally need a monad instance for 'Codec'. -- You can build monad parsing by using 'valueCodec' together with 'bimapCodec' and supplying your own parsing function. -- -- Note that this _does_ mean that the documentation will just say that you are parsing and rendering a value, so you may want to document the extra parsing further using '<?>'.+--+-- ==== API Note+--+-- This is a forward-compatible version of 'ValueCodec'.+--+-- > valueCodec = ValueCodec valueCodec :: JSONCodec JSON.Value valueCodec = ValueCodec --- | Forward-compatible version of 'NullCodec'+-- | Codec for @null@ ----- > nullCodec = NullCodec -- -- === Example usage --@@ -1005,49 +1106,76 @@ -- Just () -- >>> JSON.parseMaybe (parseJSONVia nullCodec) (Number 5) -- Nothing+--+--+-- ==== API Note+--+-- This is a forward-compatible version of 'NullCodec'.+--+-- > nullCodec = NullCodec nullCodec :: JSONCodec () nullCodec = NullCodec --- | Forward-compatible version of 'BoolCodec' without a name+-- | Codec for boolean values ----- > boolCodec = BoolCodec Nothing -- -- === Example usage -- -- >>> toJSONVia boolCodec True -- Bool True+--+--+-- ==== API Note+--+-- This is a forward-compatible version of 'BoolCodec' without a name.+--+-- > boolCodec = BoolCodec Nothing boolCodec :: JSONCodec Bool boolCodec = BoolCodec Nothing --- | Forward-compatible version of 'StringCodec' without a name+-- | Codec for text values ----- > textCodec = StringCodec Nothing -- -- === Example usage -- -- >>> toJSONVia textCodec "hello" -- String "hello"+--+--+-- ==== API Note+--+-- This is a forward-compatible version of 'StringCodec' without a name.+--+-- > textCodec = StringCodec Nothing textCodec :: JSONCodec Text textCodec = StringCodec Nothing --- | A 'String' version of 'textCodec'.+-- | Codec for 'String' values --+-- -- === Example usage -- -- >>> toJSONVia stringCodec "hello" -- String "hello" --+-- -- === WARNING -- -- This codec uses 'T.unpack' and 'T.pack' to dimap a 'textCodec', so it __does not roundtrip__. -- -- >>> toJSONVia stringCodec "\55296" -- String "\65533"+--+--+-- ==== API Note+--+-- This is a 'String' version of 'textCodec'. stringCodec :: JSONCodec String stringCodec = dimapCodec T.unpack T.pack textCodec --- | Forward-compatible version of 'NumberCodec' without a name+-- | Codec for 'Scientific' values --+-- -- === Example usage -- -- > scientificCodec = NumberCodec Nothing Nothing@@ -1055,6 +1183,7 @@ -- >>> toJSONVia scientificCodec 5 -- Number 5.0 --+-- -- === WARNING -- -- 'Scientific' is a type that is only for JSON parsing and rendering.@@ -1065,12 +1194,18 @@ -- λ> (1 / 3) :: Scientific -- *** Exception: fromRational has been applied to a repeating decimal which can't be represented as a Scientific! It's better to avoid performing fractional operations on Scientifics and convert them to other fractional types like Double as early as possible. -- @+--+--+-- ==== API Note+--+-- This is a forward-compatible version of 'NumberCodec' without a name.+--+-- > scientificCodec = NumberCodec Nothing Nothing scientificCodec :: JSONCodec Scientific scientificCodec = NumberCodec Nothing Nothing -- | An object codec with a given name ----- > object name = ObjectOfCodec (Just name) -- -- === Example usage --@@ -1085,6 +1220,13 @@ -- > Example -- > <$> requiredField "text" "a text" .= exampleText -- > <*> requiredField "bool" "a bool" .= exampleBool+--+--+-- ==== API Note+--+-- This is a forward-compatible version 'ObjectOfCodec' with a name.+--+-- > object name = ObjectOfCodec (Just name) object :: Text -> ObjectCodec input output -> ValueCodec input output object name = ObjectOfCodec (Just name) @@ -1127,6 +1269,7 @@ -- -- During rendering, the given 'Text' is always output. --+-- -- === Example usage -- -- >>> let c = literalTextCodec "hello"@@ -1147,6 +1290,7 @@ -- -- During rendering, the given @value@ is always output. --+-- -- === Example usage -- -- >>> let c = literalTextValueCodec True "yes"@@ -1161,7 +1305,7 @@ literalTextValueCodec :: value -> Text -> JSONCodec value literalTextValueCodec value text = dimapCodec (const value) (const text) (literalTextCodec text) --- | A choice codec, but unlike 'eitherCodec', it's for the same type instead of different ones.+-- | A choice codec, but unlike 'eitherCodec', it's for the same output type instead of different ones. -- -- While parsing, this codec will first try the left codec, then the right if that fails. --@@ -1170,6 +1314,7 @@ -- Note: The reason this is less primitive than the 'eitherCodec' is that 'Either' makes it clear which codec you want to use for rendering. -- In this case, we need to provide our own function for choosing which codec we want to use for rendering. --+-- -- === Example usage -- -- >>> :{@@ -1188,29 +1333,66 @@ -- Just "even" -- >>> JSON.parseMaybe (parseJSONVia c) (String "odd") :: Maybe Text -- Just "odd"+--+--+-- ==== API Note+--+-- This is a forward-compatible version of 'matchChoiceCodecAs PossiblyJointUnion':+--+-- > disjointMatchChoiceCodec = matchChoiceCodecAs PossiblyJointUnion matchChoiceCodec :: -- | First codec Codec context input output -> -- | Second codec+ Codec context input' output ->+ -- | Rendering chooser+ (newInput -> Either input input') ->+ -- |+ Codec context newInput output+matchChoiceCodec = matchChoiceCodecAs PossiblyJointUnion++-- | Disjoint version of 'matchChoiceCodec'+--+--+-- ==== API Note+--+-- This is a forward-compatible version of 'matchChoiceCodecAs DisjointUnion':+--+-- > disjointMatchChoiceCodec = matchChoiceCodecAs DisjointUnion+disjointMatchChoiceCodec ::+ -- | First codec Codec context input output ->+ -- | Second codec+ Codec context input' output -> -- | Rendering chooser- (newInput -> Either input input) ->+ (newInput -> Either input input') -> -- | Codec context newInput output-matchChoiceCodec c1 c2 renderingChooser =- dimapCodec f renderingChooser $- eitherCodec c1 c2- where- f = \case- Left a -> a- Right a -> a+disjointMatchChoiceCodec = matchChoiceCodecAs DisjointUnion +-- | An even more general version of 'matchChoiceCodec' and 'disjointMatchChoiceCodec'.+matchChoiceCodecAs ::+ -- | Is the union DisjointUnion or PossiblyJointUnion+ Union ->+ -- | First codec+ Codec context input output ->+ -- | Second codec+ Codec context input' output ->+ -- | Rendering chooser+ (newInput -> Either input input') ->+ -- |+ Codec context newInput output+matchChoiceCodecAs union c1 c2 renderingChooser =+ dimapCodec (either id id) renderingChooser $+ EitherCodec union c1 c2+ -- | A choice codec for a list of options, each with their own rendering matcher. -- -- During parsing, each of the codecs are tried from first to last until one succeeds. -- -- During rendering, each matching function is tried until either one succeeds and the corresponding codec is used, or none succeed and the fallback codec is used. --+-- -- === Example usage -- -- >>> :{@@ -1235,6 +1417,13 @@ -- Nothing -- >>> JSON.parseMaybe (parseJSONVia c) (String "fallback") :: Maybe Text -- Just "fallback"+--+--+-- ==== API Note+--+-- This is a forward-compatible version of 'matchChoicesCodecAs DisjointUnion'.+--+-- > disjointMatchChoiceCodec = matchChoicesCodecAs DisjointUnion matchChoicesCodec :: -- | Codecs, each which their own rendering matcher [(input -> Maybe input, Codec context input output)] ->@@ -1242,11 +1431,39 @@ Codec context input output -> -- | Codec context input output-matchChoicesCodec l fallback = go l+matchChoicesCodec = matchChoicesCodecAs PossiblyJointUnion++-- | Disjoint version of 'matchChoicesCodec'+--+--+-- ==== API Note+--+-- This is a forward-compatible version of 'matchChoicesCodecAs DisjointUnion'.+--+-- > disjointMatchChoiceCodec = matchChoicesCodecAs DisjointUnion+disjointMatchChoicesCodec ::+ -- | Codecs, each which their own rendering matcher+ [(input -> Maybe input, Codec context input output)] ->+ -- | Fallback codec, in case none of the matchers in the list match+ Codec context input output ->+ -- |+ Codec context input output+disjointMatchChoicesCodec = matchChoicesCodecAs DisjointUnion++-- | An even more general version of 'matchChoicesCodec' and 'disjointMatchChoicesCodec'+matchChoicesCodecAs ::+ Union ->+ -- | Codecs, each which their own rendering matcher+ [(input -> Maybe input, Codec context input output)] ->+ -- | Fallback codec, in case none of the matchers in the list match+ Codec context input output ->+ -- |+ Codec context input output+matchChoicesCodecAs union l fallback = go l where go = \case [] -> fallback- ((m, c) : rest) -> matchChoiceCodec c (go rest) $ \i -> case m i of+ ((m, c) : rest) -> matchChoiceCodecAs union c (go rest) $ \i -> case m i of Just j -> Left j Nothing -> Right i @@ -1255,6 +1472,7 @@ -- -- You can use this for keeping old ways of parsing intact while already rendering in the new way. --+-- -- === Example usage -- -- >>> data Fruit = Apple | Orange deriving (Show, Eq, Bounded, Enum)@@ -1281,6 +1499,7 @@ -- | Like 'parseAlternatives', but with only one alternative codec --+-- -- === Example usage -- -- >>> data Fruit = Apple | Orange deriving (Show, Eq, Bounded, Enum)@@ -1301,6 +1520,7 @@ -- | A codec for an enum that can be written each with their own codec. --+-- -- === WARNING -- -- If you don't provide a string for one of the type's constructors, the last codec in the list will be used instead.@@ -1316,13 +1536,14 @@ go :: NonEmpty (enum, Codec context enum enum) -> Codec context enum enum go ((e, c) :| rest) = case NE.nonEmpty rest of Nothing -> c- Just ne -> matchChoiceCodec c (go ne) $ \i ->+ Just ne -> disjointMatchChoiceCodec c (go ne) $ \i -> if e == i then Left e else Right i -- | A codec for an enum that can be written as constant string values --+-- -- === Example usage -- -- >>> data Fruit = Apple | Orange deriving (Show, Eq)@@ -1332,6 +1553,7 @@ -- >>> JSON.parseMaybe (parseJSONVia c) (String "foo") :: Maybe Fruit -- Just Apple --+-- -- === WARNING -- -- If you don't provide a string for one of the type's constructors, the last string in the list will be used instead:@@ -1357,6 +1579,7 @@ -- | A codec for a 'Bounded' 'Enum' that uses its 'Show' instance to have the values correspond to literal 'Text' values. --+-- -- === Example usage -- -- >>> data Fruit = Apple | Orange deriving (Show, Eq, Enum, Bounded)@@ -1399,8 +1622,11 @@ -- This is used to allow for references to the codec, and that's necessary -- to produce finite documentation for recursive codecs. ----- Forward-compatible version of 'ReferenceCodec' --+-- ==== API Note+--+-- This is a forward-compatible version of 'ReferenceCodec'.+-- -- > named = ReferenceCodec named :: Text -> ValueCodec input output -> ValueCodec input output named = ReferenceCodec@@ -1412,6 +1638,7 @@ -- -- Note that this will not have good documentation because, at a codec level, -- it's just parsing and rendering a 'JSON.Value'.+-- -- -- === Example usage --