diff --git a/extra/Lamdera/Wire3/Decoder.hs b/extra/Lamdera/Wire3/Decoder.hs index b7d056b75..228efa902 100644 --- a/extra/Lamdera/Wire3/Decoder.hs +++ b/extra/Lamdera/Wire3/Decoder.hs @@ -416,7 +416,14 @@ decoderForType ifaces cname tipe = let extendedRecord = TRecord resolved Nothing & resolveTvar tvars_ in decoderForType ifaces cname extendedRecord Nothing -> normalDecoder - otherTypes -> normalDecoder + _ -> + -- Resolve extensible records through TAlias chains, + -- e.g. Color = ColorValue { red, green, blue, alpha } + case resolveTvar tvars_ tipe of + TAlias _ _ _ (Filled (TRecord fieldMap Nothing)) -> + let fields = fieldMap & fieldsToList & List.sortOn (\(name, field) -> name) + in decodeRecord ifaces cname fields + _ -> normalDecoder Filled tipe -> case tipe of TRecord fieldMap extensibleName -> diff --git a/extra/Lamdera/Wire3/Encoder.hs b/extra/Lamdera/Wire3/Encoder.hs index 8786f4bc8..ac0f84e79 100644 --- a/extra/Lamdera/Wire3/Encoder.hs +++ b/extra/Lamdera/Wire3/Encoder.hs @@ -365,7 +365,13 @@ inlineIfRecordOrCall depth ifaces cname tipe tvars aType = in deepEncoderForType depth ifaces cname extendedRecord Nothing -> normalEncoder - otherTypes -> normalEncoder + _ -> + -- Resolve extensible records through TAlias chains, + -- e.g. Color = ColorValue { red, green, blue, alpha } + case resolveTvar tvars tipe of + TAlias _ _ _ (Filled extendedRecord@(TRecord _ Nothing)) -> + deepEncoderForType depth ifaces cname extendedRecord + _ -> normalEncoder Filled _ -> normalEncoder {-| Called for encoding tvar type values, i.e. diff --git a/test/Test/Wire.hs b/test/Test/Wire.hs index b2ca119f4..67e4c9d06 100644 --- a/test/Test/Wire.hs +++ b/test/Test/Wire.hs @@ -130,6 +130,7 @@ wire = do , "src/Test/Wire_Tvar_Recursive_Reference.elm" , "src/Test/Wire_Unsupported.elm" , "src/Test/Wire_Unconstructable.elm" + , "src/Test/Wire_Union_ForeignRecordAlias.elm" ] let diff --git a/test/scenario-alltypes/src/Test/External.elm b/test/scenario-alltypes/src/Test/External.elm index 19e63d5ad..e52cd50ad 100644 --- a/test/scenario-alltypes/src/Test/External.elm +++ b/test/scenario-alltypes/src/Test/External.elm @@ -30,6 +30,14 @@ type alias SubSubRecordAlias threadedTvar = } +type alias ExternalExtensibleBase compatible = + { compatible | base : String } + + +type alias ExternalRecordViaExtensible = + ExternalExtensibleBase { red : Int, green : Int } + + expected_w3_encode_ExternalRecordBasic : ExternalRecordBasic -> Lamdera.Wire3.Encoder expected_w3_encode_ExternalRecordBasic = \w3_rec_var0 -> Lamdera.Wire3.encodeSequenceWithoutLength [ Lamdera.Wire3.encodeInt w3_rec_var0.int ] @@ -113,3 +121,30 @@ expected_w3_decode_ExternalCustomThreaded w3_x_c_threadedTvar w3_x_c_threadedTva _ -> Lamdera.Wire3.failDecode ) + + +expected_w3_encode_ExternalExtensibleBase : ({ compatible | base : String.String } -> Lamdera.Wire3.Encoder) -> ExternalExtensibleBase compatible -> Lamdera.Wire3.Encoder +expected_w3_encode_ExternalExtensibleBase w3_x_c_compatible = + w3_x_c_compatible + + +expected_w3_decode_ExternalExtensibleBase w3_x_c_compatible = + w3_x_c_compatible + + +expected_w3_encode_ExternalRecordViaExtensible : ExternalRecordViaExtensible -> Lamdera.Wire3.Encoder +expected_w3_encode_ExternalRecordViaExtensible = + \w3_rec_var0 -> + Lamdera.Wire3.encodeSequenceWithoutLength + [ Lamdera.Wire3.encodeString w3_rec_var0.base + , Lamdera.Wire3.encodeInt w3_rec_var0.green + , Lamdera.Wire3.encodeInt w3_rec_var0.red + ] + + +expected_w3_decode_ExternalRecordViaExtensible = + Lamdera.Wire3.succeedDecode + (\base0 green0 red0 -> { base = base0, green = green0, red = red0 }) + |> Lamdera.Wire3.andMapDecode Lamdera.Wire3.decodeString + |> Lamdera.Wire3.andMapDecode Lamdera.Wire3.decodeInt + |> Lamdera.Wire3.andMapDecode Lamdera.Wire3.decodeInt diff --git a/test/scenario-alltypes/src/Test/Wire_Union_ForeignRecordAlias.elm b/test/scenario-alltypes/src/Test/Wire_Union_ForeignRecordAlias.elm new file mode 100644 index 000000000..3494828bb --- /dev/null +++ b/test/scenario-alltypes/src/Test/Wire_Union_ForeignRecordAlias.elm @@ -0,0 +1,85 @@ +module Test.Wire_Union_ForeignRecordAlias exposing (..) + +import Bytes.Decode +import Bytes.Encode +import Lamdera.Wire3 +import Test.External exposing (ExternalRecordBasic, ExternalRecordViaExtensible) + + +{-| Regression test: extensible record aliases through alias chains. +See: +-} +type WrapsBasicRecord + = WrapsBasicRecord ExternalRecordBasic + + +type WrapsExtensibleRecord + = WrapsExtensibleRecord ExternalRecordViaExtensible + + +type WrapsInRecord + = WrapsInRecord { field : ExternalRecordViaExtensible } + + +expected_w3_encode_WrapsBasicRecord : WrapsBasicRecord -> Lamdera.Wire3.Encoder +expected_w3_encode_WrapsBasicRecord w3v = + case w3v of + WrapsBasicRecord v0 -> + Lamdera.Wire3.encodeSequenceWithoutLength [ Bytes.Encode.unsignedInt8 0, Test.External.w3_encode_ExternalRecordBasic v0 ] + + +expected_w3_decode_WrapsBasicRecord = + Bytes.Decode.unsignedInt8 + |> Lamdera.Wire3.andThenDecode + (\w3v -> + case w3v of + 0 -> + Lamdera.Wire3.succeedDecode WrapsBasicRecord |> Lamdera.Wire3.andMapDecode Test.External.w3_decode_ExternalRecordBasic + + _ -> + Lamdera.Wire3.failDecode + ) + + +expected_w3_encode_WrapsExtensibleRecord : WrapsExtensibleRecord -> Lamdera.Wire3.Encoder +expected_w3_encode_WrapsExtensibleRecord w3v = + case w3v of + WrapsExtensibleRecord v0 -> + Lamdera.Wire3.encodeSequenceWithoutLength [ Bytes.Encode.unsignedInt8 0, Test.External.w3_encode_ExternalRecordViaExtensible v0 ] + + +expected_w3_decode_WrapsExtensibleRecord = + Bytes.Decode.unsignedInt8 + |> Lamdera.Wire3.andThenDecode + (\w3v -> + case w3v of + 0 -> + Lamdera.Wire3.succeedDecode WrapsExtensibleRecord |> Lamdera.Wire3.andMapDecode Test.External.w3_decode_ExternalRecordViaExtensible + + _ -> + Lamdera.Wire3.failDecode + ) + + +expected_w3_encode_WrapsInRecord : WrapsInRecord -> Lamdera.Wire3.Encoder +expected_w3_encode_WrapsInRecord w3v = + case w3v of + WrapsInRecord v0 -> + Lamdera.Wire3.encodeSequenceWithoutLength [ Bytes.Encode.unsignedInt8 0, Test.External.w3_encode_ExternalRecordViaExtensible v0.field ] + + +expected_w3_decode_WrapsInRecord = + Bytes.Decode.unsignedInt8 + |> Lamdera.Wire3.andThenDecode + (\w3v -> + case w3v of + 0 -> + Lamdera.Wire3.succeedDecode WrapsInRecord + |> Lamdera.Wire3.andMapDecode + (Lamdera.Wire3.succeedDecode (\field0 -> { field = field0 }) + |> Lamdera.Wire3.andMapDecode Test.External.w3_decode_ExternalRecordViaExtensible + ) + + _ -> + Lamdera.Wire3.failDecode + )