diff --git a/CHANGES.md b/CHANGES.md index 3e3cede..4d1ede7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,6 +10,12 @@ To be released. - The `uri` type has completly gone; use `url` instead. [[#126], [#281] by Jonghun Park] + - Support [external_tag][etag] via '@external-tag' annotation. + [[#266], [#316] by JaeHyung Jang] + +[etag]: https://serde.rs/enum-representations.html +[#266]: https://github.com/nirum-lang/nirum/issues/266 +[#316]: https://github.com/nirum-lang/nirum/pull/316 ### Docs target diff --git a/docs/serialization.md b/docs/serialization.md index 5a66b5c..05c174e 100644 --- a/docs/serialization.md +++ b/docs/serialization.md @@ -250,6 +250,27 @@ It's represented in JSON to: In a similar way to a record type, undefined fields in a payload are ignored by deserializer. +If '@external-tag' annotation is given on tag below example: + + union name = + @external-tag + east-asian-name ( text family-name, text given-name ) + | ... + ; + +It's representated in JSON to: + + { + { + "east-asian-name": { + "_type": "name", + "_tag": "east-asian-name", + "family_name": "Hong", + "given_name": "Minhee" + } + } + } + } Option type ----------- diff --git a/examples/shapes.nrm b/examples/shapes.nrm index 9d4197f..1ee1e81 100644 --- a/examples/shapes.nrm +++ b/examples/shapes.nrm @@ -37,7 +37,8 @@ union shape point upper-left, point lower-right ) - | circle (point origin, offset radius) + | @external-tag + circle (point origin, offset radius) ; // This is comment. Note that there are important differences between diff --git a/src/Nirum/Targets/Python.hs b/src/Nirum/Targets/Python.hs index 6d1e5c4..585c01e 100644 --- a/src/Nirum/Targets/Python.hs +++ b/src/Nirum/Targets/Python.hs @@ -436,7 +436,8 @@ class $funcName(object): |] compileUnionTag :: Source -> Name -> Tag -> CodeGen Markup -compileUnionTag source parentname d@(Tag typename' fields' _) = do +compileUnionTag source parentname d@(Tag typename' fields' annotations') = do + let hasExternalTag = M.member "external-tag" $ AI.annotations annotations' abc <- collectionsAbc fieldCodes <- toFieldCodes source (\ f -> [qq|value.get('{toBehindSnakeCaseText (fieldName f)}')|]) @@ -521,7 +522,7 @@ class #{className}(#{parentClass}): %{ endcase } def __nirum_serialize__(self): - return { + t = { '_type': '#{behindParentTypename}', '_tag': '#{behindTagName}', %{ forall fc@FieldCode { fcField = Field { fieldType = fType } } <- fieldCodes } @@ -529,6 +530,10 @@ class #{className}(#{parentClass}): #{compileSerializer' source fType $ T.append "self." $ fcAttributeName fc}, %{ endforall } } +%{ if hasExternalTag } + t = {'#{behindTagName}': t} +%{ endif } + return t @classmethod %{ case pyVer } @@ -544,6 +549,13 @@ class #{className}(#{parentClass}): ) -> typing.Optional['#{className}']: %{ endcase } handle_error = #{defaultErrorHandler}(on_error) +%{ if hasExternalTag } + try: + value = '#{toBehindSnakeCaseText typename'}' + except KeyError: + handle_error('.', 'Expected #{toBehindSnakeCaseText typename'} to exist.') + handle_error.raise_error() +%{ endif } if isinstance(value, #{abc}.Mapping): try: tag = value['_tag'] diff --git a/test/nirum_fixture/fixture/foo.nrm b/test/nirum_fixture/fixture/foo.nrm index 43603fa..1f283b3 100644 --- a/test/nirum_fixture/fixture/foo.nrm +++ b/test/nirum_fixture/fixture/foo.nrm @@ -144,3 +144,8 @@ record name-shadowing-field-record ( union optional-union = foo ( int32? bar ) | baz ( int32 qux ) ; + +union external-tag-union = tag ( int32 tag ) + | @external-tag + etag ( int32 etag) + ; \ No newline at end of file diff --git a/test/serialization/unions/external-tag.json b/test/serialization/unions/external-tag.json new file mode 100644 index 0000000..1950707 --- /dev/null +++ b/test/serialization/unions/external-tag.json @@ -0,0 +1,18 @@ +{ + "description": "Serialize into external tag when gives 'external-tag' annotation", + "type": "fixture.foo.etag", + "input": { + "etag": { + "_type": "external_tag_union", + "_tag": "etag", + "etag": 1 + } + }, + "normal": { + "etag": { + "_type": "external_tag_union", + "_tag": "etag", + "etag": 1 + } + } +}