Skip to content

Commit

Permalink
Support custom type tags with multiple parameters
Browse files Browse the repository at this point in the history
+ Fix a bug in parsing custom type text: Instances in tag parameters need to be enclosed in parentheses.
+ Expand generation of the JSON coding functions to support tags with multiple parameters. Expand the decoders to support the automatic migration of applications from previous versions.
+ Automate tests for the new features, also specifically covering the core types `Maybe` and `Result`.
+ Expand the full-stack demo app to illustrate the new functionality.

+ Also, fix a path in the `demo-backend-state.ps1` script.
  • Loading branch information
Viir committed Mar 7, 2020
1 parent e7761ef commit 1cd3f00
Show file tree
Hide file tree
Showing 8 changed files with 212 additions and 45 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
docker build --tag elm-fullstack-test ./../../
docker build --tag elm-fullstack-test ./../../../

docker stop fullstack-test-container

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ type alias Tuple3 =

type CustomType
= CustomTagWithoutParameter
| CustomTagWithParameter Int
| CustomTagWithOneParameter Int
| CustomTagWithTwoParameters String Int
| CustomTagWithMaybeInstance (Maybe Int)
| CustomTagWithResultInstance (Result String Int)


type CustomTypeWithTypeParameter a
Expand Down Expand Up @@ -104,7 +107,15 @@ interfaceToHost_initState =
, lastHttpRequests = []
, tuple2 = ( 123, "second element in tuple A" )
, tuple3 = ( 456, "second element in tuple B", 789 )
, list_custom_type = [ CustomTagWithoutParameter, CustomTagWithParameter 4 ]
, list_custom_type =
[ CustomTagWithoutParameter
, CustomTagWithOneParameter 4
, CustomTagWithTwoParameters "test" 11
, CustomTagWithMaybeInstance Nothing
, CustomTagWithMaybeInstance (Just 123)
, CustomTagWithResultInstance (Err "error string")
, CustomTagWithResultInstance (Ok 678)
]
, opaque_custom_type = OpaqueCustomType "content"
, recursive_type = TagRecurse (TagRecurse (TagRecurse (TagTerminate 4)))
, bool = True
Expand Down
2 changes: 1 addition & 1 deletion implement/elm-fullstack/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace elm_fullstack
{
class Program
{
static string AppVersionId => "2020-03-02";
static string AppVersionId => "2020-03-07";

static int Main(string[] args)
{
Expand Down
4 changes: 2 additions & 2 deletions implement/elm-fullstack/elm-fullstack.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>elm_fullstack</RootNamespace>
<AssemblyName>elm-fullstack</AssemblyName>
<AssemblyVersion>2020.0220.0.0</AssemblyVersion>
<FileVersion>2020.0220.0.0</FileVersion>
<AssemblyVersion>2020.0307.0.0</AssemblyVersion>
<FileVersion>2020.0307.0.0</FileVersion>
</PropertyGroup>

<ItemGroup>
Expand Down
3 changes: 2 additions & 1 deletion implement/test/modeled-in-elm/generate-json-coders/elm.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"elm/browser": "1.0.2",
"elm/core": "1.0.5",
"elm/html": "1.0.0",
"elm/json": "1.1.3"
"elm/json": "1.1.3",
"elm-community/result-extra": "2.3.0"
},
"indirect": {
"elm/time": "1.0.0",
Expand Down
93 changes: 92 additions & 1 deletion implement/test/modeled-in-elm/generate-json-coders/src/Main.elm
Original file line number Diff line number Diff line change
@@ -1,21 +1,112 @@
module Main exposing (main)

import ElmFullstackLoweringInterface.GenerateJsonCoders as GenerateJsonCoders
import Json.Decode
import Json.Encode
import OpaqueCustomType
import Result.Extra
import Structures


tests : String
tests =
[ { testName = "serialize non-exposed tag of custom type"
, expected = """{"int":4,"opaqueCustomType":{"TagA":{}}}"""
, expected = """{"int":4,"opaqueCustomType":{"TagA":[]},"list_custom_type":[]}"""
, derived =
{ int = 4
, opaqueCustomType = OpaqueCustomType.constructTagA
, list_custom_type = []
}
|> GenerateJsonCoders.encodeMixedRecord
|> Json.Encode.encode 0
}
, { testName = "Custom type tag with one parameter"
, expected = """{"int":4,"opaqueCustomType":{"TagA":[]},"list_custom_type":[{"CustomTagWithOneParameter":[13]}]}"""
, derived =
{ int = 4
, opaqueCustomType = OpaqueCustomType.constructTagA
, list_custom_type = [ Structures.CustomTagWithOneParameter 13 ]
}
|> GenerateJsonCoders.encodeMixedRecord
|> Json.Encode.encode 0
}
, { testName = "Custom type tag with two parameters"
, expected = """{"int":4,"opaqueCustomType":{"TagA":[]},"list_custom_type":[{"CustomTagWithTwoParameters":["first arg",17]}]}"""
, derived =
{ int = 4
, opaqueCustomType = OpaqueCustomType.constructTagA
, list_custom_type = [ Structures.CustomTagWithTwoParameters "first arg" 17 ]
}
|> GenerateJsonCoders.encodeMixedRecord
|> Json.Encode.encode 0
}
, { testName = "Custom type tag with two parameters - roundtrip"
, expected = """{"int":7,"opaqueCustomType":{"TagA":[]},"list_custom_type":[{"CustomTagWithTwoParameters":["first arg",19]}]}"""
, derived =
"""{"int" : 7 , "opaqueCustomType" : { "TagA" : []}, "list_custom_type":[{"CustomTagWithTwoParameters":["first arg",19]}]}"""
|> Json.Decode.decodeString GenerateJsonCoders.decodeMixedRecord
|> Result.map (GenerateJsonCoders.encodeMixedRecord >> Json.Encode.encode 0)
|> Result.mapError Json.Decode.errorToString
|> Result.Extra.merge
}
, { testName = "Maybe - Just roundtrip"
, expected = """{"int":7,"opaqueCustomType":{"TagA":[]},"list_custom_type":[{"CustomTagWithMaybeInstance":[{"Just":[37]}]}]}"""
, derived =
"""{"int" : 7, "opaqueCustomType":{"TagA":[]},"list_custom_type":[{"CustomTagWithMaybeInstance":[{"Just":[37]}]}]}"""
|> Json.Decode.decodeString GenerateJsonCoders.decodeMixedRecord
|> Result.map (GenerateJsonCoders.encodeMixedRecord >> Json.Encode.encode 0)
|> Result.mapError Json.Decode.errorToString
|> Result.Extra.merge
}
, { testName = "Maybe - Nothing roundtrip"
, expected = """{"int":7,"opaqueCustomType":{"TagA":[]},"list_custom_type":[{"CustomTagWithMaybeInstance":[{"Nothing":[]}]}]}"""
, derived =
"""{"int" : 7, "opaqueCustomType":{"TagA":[]},"list_custom_type":[{"CustomTagWithMaybeInstance":[{"Nothing":[]}]}]}"""
|> Json.Decode.decodeString GenerateJsonCoders.decodeMixedRecord
|> Result.map (GenerateJsonCoders.encodeMixedRecord >> Json.Encode.encode 0)
|> Result.mapError Json.Decode.errorToString
|> Result.Extra.merge
}
, { testName = "Result - Ok roundtrip"
, expected = """{"int":7,"opaqueCustomType":{"TagA":[]},"list_custom_type":[{"CustomTagWithResultInstance":[{"Ok":[37]}]}]}"""
, derived =
"""{"int" : 7, "opaqueCustomType":{"TagA":[]},"list_custom_type":[{"CustomTagWithResultInstance":[{"Ok":[37]}]}]}"""
|> Json.Decode.decodeString GenerateJsonCoders.decodeMixedRecord
|> Result.map (GenerateJsonCoders.encodeMixedRecord >> Json.Encode.encode 0)
|> Result.mapError Json.Decode.errorToString
|> Result.Extra.merge
}
, { testName = "Result - Err roundtrip"
, expected = """{"int":7,"opaqueCustomType":{"TagA":[]},"list_custom_type":[{"CustomTagWithResultInstance":[{"Err":["error string"]}]}]}"""
, derived =
"""{"int" : 7, "opaqueCustomType":{"TagA":[]},"list_custom_type":[{"CustomTagWithResultInstance":[{"Err":["error string"]}]}]}"""
|> Json.Decode.decodeString GenerateJsonCoders.decodeMixedRecord
|> Result.map (GenerateJsonCoders.encodeMixedRecord >> Json.Encode.encode 0)
|> Result.mapError Json.Decode.errorToString
|> Result.Extra.merge
}
, { testName = "Maybe - support easy migration from older format."

-- 2020-03-07 Support easy migration of apps: Support decode from older JSON format for now. This case can be removed when the apps have been migrated.
, expected = """{"int":7,"opaqueCustomType":{"TagA":[]},"list_custom_type":[{"CustomTagWithMaybeInstance":[{"Just":[37]}]}]}"""
, derived =
"""{"int" : 7, "opaqueCustomType":{"TagA":[]},"list_custom_type":[{"CustomTagWithMaybeInstance":[{"Just":37}]}]}"""
|> Json.Decode.decodeString GenerateJsonCoders.decodeMixedRecord
|> Result.map (GenerateJsonCoders.encodeMixedRecord >> Json.Encode.encode 0)
|> Result.mapError Json.Decode.errorToString
|> Result.Extra.merge
}
, { testName = "Result - support easy migration from older format."

-- 2020-03-07 Support easy migration of apps: Support decode from older JSON format for now. This case can be removed when the apps have been migrated.
, expected = """{"int":7,"opaqueCustomType":{"TagA":[]},"list_custom_type":[{"CustomTagWithResultInstance":[{"Ok":[37]}]}]}"""
, derived =
"""{"int" : 7, "opaqueCustomType":{"TagA":[]},"list_custom_type":[{"CustomTagWithResultInstance":[{"Ok":37}]}]}"""
|> Json.Decode.decodeString GenerateJsonCoders.decodeMixedRecord
|> Result.map (GenerateJsonCoders.encodeMixedRecord >> Json.Encode.encode 0)
|> Result.mapError Json.Decode.errorToString
|> Result.Extra.merge
}
]
|> GenerateJsonCoders.testsValueToInterface
|> Json.Encode.encode 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,13 @@ import OpaqueCustomType exposing (OpaqueCustomType)
type alias MixedRecord =
{ int : Int
, opaqueCustomType : OpaqueCustomType
, list_custom_type : List CustomType
}


type CustomType
= CustomTagWithoutParameter
| CustomTagWithOneParameter Int
| CustomTagWithTwoParameters String Int
| CustomTagWithMaybeInstance (Maybe Int)
| CustomTagWithResultInstance (Result String Int)

0 comments on commit 1cd3f00

Please sign in to comment.