From 848d83a18e8e3acb1c30808abdc38cb0c1e3068d Mon Sep 17 00:00:00 2001 From: Bram Date: Sun, 17 Dec 2023 21:29:45 +0100 Subject: [PATCH] Add JSON coder helper files --- elm.json | 2 + src/Internal/Tools/Decode.elm | 155 ++++++++++++++++++++++++++++++++++ src/Internal/Tools/Encode.elm | 52 ++++++++++++ 3 files changed, 209 insertions(+) create mode 100644 src/Internal/Tools/Decode.elm create mode 100644 src/Internal/Tools/Encode.elm diff --git a/elm.json b/elm.json index 3e1edc0..0ab26bd 100644 --- a/elm.json +++ b/elm.json @@ -8,6 +8,8 @@ "Matrix", "Internal.Config.Default", "Internal.Config.Text", + "Internal.Tools.Decode", + "Internal.Tools.Encode", "Internal.Tools.Hashdict", "Internal.Tools.Iddict", "Internal.Tools.Timestamp", diff --git a/src/Internal/Tools/Decode.elm b/src/Internal/Tools/Decode.elm new file mode 100644 index 0000000..c0ea7b2 --- /dev/null +++ b/src/Internal/Tools/Decode.elm @@ -0,0 +1,155 @@ +module Internal.Tools.Decode exposing + ( opField, opFieldWithDefault + , map9, map10, map11 + ) + +{-| + + +# Decode module + +This module contains helper functions that help decode JSON. + + +## Optional field decoders + +@docs opField, opFieldWithDefault + + +## Extended map functions + +@docs map9, map10, map11 + +-} + +import Json.Decode as D + + +{-| Add an optional field decoder. If the field exists, the decoder will fail +if the field doesn't decode properly. + +This decoder standard out from `D.maybe <| D.field fieldName decoder` because +that will decode into a `Nothing` if the `decoder` fails. This function will +only decode into a `Nothing` if the field doesn't exist, and will fail if +`decoder` fails. + +The function also returns Nothing if the field exists but it is null. + +-} +opField : String -> D.Decoder a -> D.Decoder (Maybe a) +opField fieldName decoder = + D.value + |> D.field fieldName + |> D.maybe + |> D.andThen + (\v -> + case v of + Just _ -> + D.oneOf + [ D.null Nothing + , D.map Just decoder + ] + |> D.field fieldName + + Nothing -> + D.succeed Nothing + ) + + +{-| Add an optional field decoder. If the field is not given, the decoder will +return a default value. If the field exists, the decoder will fail if the field +doesn't decode properly. +-} +opFieldWithDefault : String -> a -> D.Decoder a -> D.Decoder a +opFieldWithDefault fieldName default decoder = + opField fieldName decoder |> D.map (Maybe.withDefault default) + + +{-| Try 9 decoders and combine the result. +-} +map9 : + (a -> b -> c -> d -> e -> f -> g -> h -> i -> value) + -> D.Decoder a + -> D.Decoder b + -> D.Decoder c + -> D.Decoder d + -> D.Decoder e + -> D.Decoder f + -> D.Decoder g + -> D.Decoder h + -> D.Decoder i + -> D.Decoder value +map9 func da db dc dd de df dg dh di = + D.map8 + (\a b c d e f g ( h, i ) -> + func a b c d e f g h i + ) + da + db + dc + dd + de + df + dg + (D.map2 Tuple.pair dh di) + + +{-| Try 10 decoders and combine the result. +-} +map10 : + (a -> b -> c -> d -> e -> f -> g -> h -> i -> j -> value) + -> D.Decoder a + -> D.Decoder b + -> D.Decoder c + -> D.Decoder d + -> D.Decoder e + -> D.Decoder f + -> D.Decoder g + -> D.Decoder h + -> D.Decoder i + -> D.Decoder j + -> D.Decoder value +map10 func da db dc dd de df dg dh di dj = + D.map8 + (\a b c d e f ( g, h ) ( i, j ) -> + func a b c d e f g h i j + ) + da + db + dc + dd + de + df + (D.map2 Tuple.pair dg dh) + (D.map2 Tuple.pair di dj) + + +{-| Try 11 decoders and combine the result. +-} +map11 : + (a -> b -> c -> d -> e -> f -> g -> h -> i -> j -> k -> value) + -> D.Decoder a + -> D.Decoder b + -> D.Decoder c + -> D.Decoder d + -> D.Decoder e + -> D.Decoder f + -> D.Decoder g + -> D.Decoder h + -> D.Decoder i + -> D.Decoder j + -> D.Decoder k + -> D.Decoder value +map11 func da db dc dd de df dg dh di dj dk = + D.map8 + (\a b c d e ( f, g ) ( h, i ) ( j, k ) -> + func a b c d e f g h i j k + ) + da + db + dc + dd + de + (D.map2 Tuple.pair df dg) + (D.map2 Tuple.pair dh di) + (D.map2 Tuple.pair dj dk) diff --git a/src/Internal/Tools/Encode.elm b/src/Internal/Tools/Encode.elm new file mode 100644 index 0000000..53649d9 --- /dev/null +++ b/src/Internal/Tools/Encode.elm @@ -0,0 +1,52 @@ +module Internal.Tools.Encode exposing (maybeObject) + +{-| + + +# Encode module + +This module contains helper functions that help decode JSON. + + +# Optional body object + +@docs maybeObject + +-} + +import Json.Encode as E + + +{-| Create a body object based on optionally provided values. + +In other words, the following two variables create the same JSON value: + + value1 : Json.Encode.Value + value1 = + maybeObject + [ ( "name", Just (Json.Encode.string "Alice") ) + , ( "age", Nothing ) + , ( "height", Just (Json.Encode.float 1.61) ) + , ( "weight", Nothing ) + ] + + value2 : Json.Encode.Value + value2 = + Json.Encode.object + [ ( "name", Json.Encode.string "Alice" ) + , ( "height", Json.Encode.float 1.61 ) + ] + +-} +maybeObject : List ( String, Maybe E.Value ) -> E.Value +maybeObject = + List.filterMap + (\( name, value ) -> + case value of + Just v -> + Just ( name, v ) + + _ -> + Nothing + ) + >> E.object