diff --git a/src/TsJson/Codec.elm b/src/TsJson/Codec.elm index 8cbc75f..46da016 100644 --- a/src/TsJson/Codec.elm +++ b/src/TsJson/Codec.elm @@ -5,7 +5,7 @@ module TsJson.Codec exposing , literal, stringLiteral , maybe, list, array, dict, set, tuple, triple, result , ObjectCodec, object, field, maybeField, nullableField, buildObject - , stringUnion + , stringUnion, intUnion , CustomCodec, custom, buildCustom , variant0 , namedVariant1, namedVariant2, namedVariant3, namedVariant4, namedVariant5, namedVariant6, namedVariant7, namedVariant8 @@ -53,7 +53,7 @@ This module is a port of [`miniBill/elm-codec`](https://package.elm-lang.org/pac # Custom Types -@docs stringUnion +@docs stringUnion, intUnion @docs CustomCodec, custom, buildCustom @@ -497,6 +497,48 @@ stringUnion mappings = } +{-| Simple one-to-one mapping of Elm values to TypeScript strings (no arguments, just like values like an enumeration). + + import TsJson.Codec exposing (Codec) + + type DarkMode + = Dark + | Light + + darkModeCodec : Codec DarkMode + darkModeCodec = + Codec.intUnion [ ( 0, Dark ), ( 1, Light ) ] + +The `TsType` for `darkModeCodec` is the following union: + +```typescript +0 | 1 +``` + +-} +intUnion : List ( Int, value ) -> Codec value +intUnion mappings = + let + unionDecoder : TsDecode.Decoder value + unionDecoder = + TsDecode.intUnion mappings + in + TsJson.Internal.Codec.Codec + { encoder = + Encoder + (\decoded -> + case find mappings decoded of + Just gotValue -> + gotValue |> Encode.int + + Nothing -> + Encode.null + ) + (TsDecode.tsType unionDecoder) + , decoder = unionDecoder + } + + {-| -} literal : value -> Encode.Value -> Codec value literal mappedValue literalValue = @@ -515,7 +557,7 @@ stringLiteral mappedValue literalValue = } -find : List ( String, value ) -> value -> Maybe String +find : List ( comparable, value ) -> value -> Maybe comparable find mappings mappedValue = case mappings of ( key, mapping ) :: rest -> diff --git a/src/TsJson/Decode.elm b/src/TsJson/Decode.elm index fcf3b42..cc1a531 100644 --- a/src/TsJson/Decode.elm +++ b/src/TsJson/Decode.elm @@ -10,6 +10,7 @@ module TsJson.Decode exposing , map2, andMap , literal, null , stringLiteral, stringUnion + , intUnion , discriminatedUnion , andThen, AndThenContinuation, andThenInit, andThenDecoder , value, unknownAndThen, maybe @@ -125,6 +126,8 @@ TypeScript tuples are much like an Elm tuples, except two key differences: @docs stringLiteral, stringUnion +@docs intUnion + ## Discriminated Unions @@ -620,6 +623,59 @@ stringUnion unionMappings = ) +{-| A convenience function for building a union out of int literals. + + import TsJson.Decode as TsDecode + + type Severity + = Info + | Warning + | Error + + TsDecode.intUnion + [ ( 1, Info ) + , ( 2, Warning ) + , ( 3, Error ) + ] + |> TsDecode.runExample """1""" + --> { decoded = Ok Info + --> , tsType = "1 | 2 | 3" + --> } + +-} +intUnion : + List ( Int, value ) + -> Decoder value +intUnion unionMappings = + Decoder + (Decode.int + |> Decode.andThen + (\key -> + case unionMappings |> Dict.fromList |> Dict.get key of + Just mapped -> + Decode.succeed mapped + + Nothing -> + Decode.fail <| + "I was expecting an int union with one of these string values: " + ++ "[ " + ++ (unionMappings + |> List.map (\( mapKey, _ ) -> "\"" ++ String.fromInt mapKey ++ "\"") + |> String.join ", " + ) + ++ " ]" + ) + ) + (TypeReducer.union + (unionMappings + |> List.map + (\( mapKey, _ ) -> + Literal (Encode.int mapKey) + ) + ) + ) + + {-| This type allows you to combine all the possible Decoders you could run in an [`andThen`](#andThen) continuation. This API allows you to define all possible Decoders you might use up front, so that all possible TypeScript types diff --git a/tests/CodecBaseTests.elm b/tests/CodecBaseTests.elm index ed8d1b4..499b136 100644 --- a/tests/CodecBaseTests.elm +++ b/tests/CodecBaseTests.elm @@ -364,6 +364,18 @@ customTests = |> roundtripsTest "dark mode codec" codec "\"dark\" | \"light\"" + , describe "intUnion" <| + let + codec : Codec DarkMode + codec = + TsCodec.intUnion [ ( 0, Dark ), ( 1, Light ) ] + in + [ ( "dark", Fuzz.constant Dark ) + , ( "light", Fuzz.constant Light ) + ] + |> roundtripsTest "dark mode codec" + codec + "0 | 1" , describe "literal" <| let codec : Codec String