Skip to content

Commit

Permalink
Merge pull request #1 from lifebox-healthcare/CI-1-extension-value-uri
Browse files Browse the repository at this point in the history
Merge support for polymorphic types
  • Loading branch information
frankoid authored Nov 3, 2021
2 parents a1405f2 + 70f00b2 commit 89a028a
Show file tree
Hide file tree
Showing 455 changed files with 4,855 additions and 1,569 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ This repository contains a FHIR® R4 models for Go. The models consist of Go str

## Usage

In your project, import `github.com/samply/golang-fhir-models/fhir-models/fhir` and you are done.
In your project, import `github.com/lifebox-healthcare/golang-fhir-models/fhir-models/fhir` and you are done.

## TODOs

Expand Down
197 changes: 118 additions & 79 deletions fhir-models-gen/cmd/genResources.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import (
"unicode"

"github.com/dave/jennifer/jen"
"github.com/samply/golang-fhir-models/fhir-models-gen/fhir"
"github.com/lifebox-healthcare/golang-fhir-models/fhir-models-gen/fhir"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -315,11 +315,12 @@ func appendLicenseComment(file *jen.File) {
}

func appendGeneratorComment(file *jen.File) {
file.Comment("// THIS FILE IS GENERATED BY https://github.com/samply/golang-fhir-models\n// PLEASE DO NOT EDIT BY HAND\n")
file.Comment("// THIS FILE IS GENERATED BY https://github.com/lifebox-healthcare/golang-fhir-models\n// PLEASE DO NOT EDIT BY HAND\n")
}

func appendFields(resources ResourceMap, requiredTypes map[string]bool, requiredValueSetBindings map[string]bool, file *jen.File, fields *jen.Group, parentName string, elementDefinitions []fhir.ElementDefinition, start, level int) (int, error) {
//fmt.Printf("appendFields parentName=%s, start=%d, level=%d\n", parentName, start, level)
// fmt.Printf("appendFields parentName=%s, start=%d, level=%d\n", parentName, start, level)

for i := start; i < len(elementDefinitions); i++ {
element := elementDefinitions[i]
pathParts := Split(element.Path, ".")
Expand All @@ -346,89 +347,29 @@ func appendFields(resources ResourceMap, requiredTypes map[string]bool, required
}
statement.Id(typeIdentifier).Tag(map[string]string{"json": pathParts[level] + ",omitempty", "bson": pathParts[level] + ",omitempty"})
}
// support polymorphic elements later
case 1:
statement := fields.Id(name)
var err error
i, err = addFieldStatement(resources, requiredTypes, requiredValueSetBindings, file, fields,
pathParts[level], parentName, elementDefinitions, i, level, element.Type[0])

switch element.Type[0].Code {
case "code":
if *element.Max == "*" {
statement.Op("[]")
} else if *element.Min == 0 {
statement.Op("*")
}
if err != nil {
return 0, err
}
default: //polymorphic type
name = Replace(pathParts[level], "[x]", "", -1)
for _, eleType := range element.Type {
name := name + Title(eleType.Code)

if url := requiredValueSetBinding(element); url != nil {
if bytes := resources["ValueSet"][*url]; bytes != nil {
valueSet, err := fhir.UnmarshalValueSet(bytes)
if err != nil {
return 0, err
}
if name := valueSet.Name; name != nil {
if !namePattern.MatchString(*name) {
fmt.Printf("Skip generating an enum for a ValueSet binding to `%s` because the ValueSet has a non-conforming name.\n", *name)
statement.Id("string")
} else if len(valueSet.Compose.Include) > 1 {
fmt.Printf("Skip generating an enum for a ValueSet binding to `%s` because the ValueSet includes more than one CodeSystem.\n", *valueSet.Name)
statement.Id("string")
} else if codeSystemUrl := canonical(valueSet.Compose.Include[0]); resources["CodeSystem"][codeSystemUrl] == nil {
fmt.Printf("Skip generating an enum for a ValueSet binding to `%s` because the ValueSet includes the non-existing CodeSystem with canonical URL `%s`.\n", *valueSet.Name, codeSystemUrl)
statement.Id("string")
} else {
requiredValueSetBindings[*url] = true
statement.Id(*name)
}
} else {
return 0, fmt.Errorf("missing name in ValueSet with canonical URL `%s`", *url)
}
} else {
statement.Id("string")
}
} else {
statement.Id("string")
}
case "Resource":
statement.Qual("encoding/json", "RawMessage")
default:
if *element.Max == "*" {
statement.Op("[]")
} else if *element.Min == 0 {
statement.Op("*")
}
var err error
i, err = addFieldStatement(resources, requiredTypes, requiredValueSetBindings, file, fields,
name, parentName, elementDefinitions, i, level, eleType)

var typeIdentifier string
if parentName == "Element" && name == "Id" ||
parentName == "Extension" && name == "Url" {
typeIdentifier = "string"
} else {
typeIdentifier = typeCodeToTypeIdentifier(element.Type[0].Code)
}
if typeIdentifier == "Element" || typeIdentifier == "BackboneElement" {
backboneElementName := parentName + name
statement.Id(backboneElementName)
var err error
file.Type().Id(backboneElementName).StructFunc(func(childFields *jen.Group) {
//var err error
i, err = appendFields(resources, requiredTypes, requiredValueSetBindings, file, childFields, backboneElementName, elementDefinitions, i+1, level+1)
})
if err != nil {
return 0, err
}
i--
} else {
if unicode.IsUpper(rune(typeIdentifier[0])) {
requiredTypes[typeIdentifier] = true
}
statement.Id(typeIdentifier)
if err != nil {
return 0, err
}
}

if *element.Min == 0 {
statement.Tag(map[string]string{"json": pathParts[level] + ",omitempty", "bson": pathParts[level] + ",omitempty"})
} else {
statement.Tag(map[string]string{"json": pathParts[level], "bson": pathParts[level]})
}
}
} // end of case statement
}
} else {
// index of the next parent sibling
Expand All @@ -438,6 +379,104 @@ func appendFields(resources ResourceMap, requiredTypes map[string]bool, required
return 0, nil
}

func addFieldStatement(
resources ResourceMap,
requiredTypes map[string]bool,
requiredValueSetBindings map[string]bool,
file *jen.File,
fields *jen.Group,
name string,
parentName string,
elementDefinitions []fhir.ElementDefinition,
elementIndex, level int,
elementType fhir.ElementDefinitionType,
) (idx int, err error) {
fieldName := Title(name)
element := elementDefinitions[elementIndex]
statement := fields.Id(fieldName)

switch elementType.Code {
case "code":
if *element.Max == "*" {
statement.Op("[]")
} else if *element.Min == 0 {
statement.Op("*")
}

if url := requiredValueSetBinding(element); url != nil {
if bytes := resources["ValueSet"][*url]; bytes != nil {
valueSet, err := fhir.UnmarshalValueSet(bytes)
if err != nil {
return 0, err
}
if name := valueSet.Name; name != nil {
if !namePattern.MatchString(*name) {
fmt.Printf("Skip generating an enum for a ValueSet binding to `%s` because the ValueSet has a non-conforming name.\n", *name)
statement.Id("string")
} else if len(valueSet.Compose.Include) > 1 {
fmt.Printf("Skip generating an enum for a ValueSet binding to `%s` because the ValueSet includes more than one CodeSystem.\n", *valueSet.Name)
statement.Id("string")
} else if codeSystemUrl := canonical(valueSet.Compose.Include[0]); resources["CodeSystem"][codeSystemUrl] == nil {
fmt.Printf("Skip generating an enum for a ValueSet binding to `%s` because the ValueSet includes the non-existing CodeSystem with canonical URL `%s`.\n", *valueSet.Name, codeSystemUrl)
statement.Id("string")
} else {
requiredValueSetBindings[*url] = true
statement.Id(*name)
}
} else {
return 0, fmt.Errorf("missing name in ValueSet with canonical URL `%s`", *url)
}
} else {
statement.Id("string")
}
} else {
statement.Id("string")
}
case "Resource":
statement.Qual("encoding/json", "RawMessage")
default:
if *element.Max == "*" {
statement.Op("[]")
} else if *element.Min == 0 {
statement.Op("*")
}

var typeIdentifier string
if parentName == "Element" && fieldName == "Id" ||
parentName == "Extension" && fieldName == "Url" {
typeIdentifier = "string"
} else {
typeIdentifier = typeCodeToTypeIdentifier(elementType.Code)
}
if typeIdentifier == "Element" || typeIdentifier == "BackboneElement" {
backboneElementName := parentName + fieldName
statement.Id(backboneElementName)
var err error
file.Type().Id(backboneElementName).StructFunc(func(childFields *jen.Group) {
//var err error
elementIndex, err = appendFields(resources, requiredTypes, requiredValueSetBindings, file, childFields, backboneElementName, elementDefinitions, elementIndex+1, level+1)
})
if err != nil {
return 0, err
}
elementIndex--
} else {
if unicode.IsUpper(rune(typeIdentifier[0])) {
requiredTypes[typeIdentifier] = true
}
statement.Id(typeIdentifier)
}
}

if *element.Min == 0 {
statement.Tag(map[string]string{"json": name + ",omitempty", "bson": name + ",omitempty"})
} else {
statement.Tag(map[string]string{"json": name, "bson": name})
}

return elementIndex, err
}

func requiredValueSetBinding(elementDefinition fhir.ElementDefinition) *string {
if elementDefinition.Binding != nil {
binding := *elementDefinition.Binding
Expand Down
2 changes: 1 addition & 1 deletion fhir-models-gen/cmd/valueSet.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"errors"
"fmt"
"github.com/dave/jennifer/jen"
"github.com/samply/golang-fhir-models/fhir-models-gen/fhir"
"github.com/lifebox-healthcare/golang-fhir-models/fhir-models-gen/fhir"
"strings"
)

Expand Down
34 changes: 34 additions & 0 deletions fhir-models-gen/fhir/address.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2019 The Samply Development Community
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package fhir

// THIS FILE IS GENERATED BY https://github.com/lifebox-healthcare/golang-fhir-models
// PLEASE DO NOT EDIT BY HAND

// Address is documented here http://hl7.org/fhir/StructureDefinition/Address
type Address struct {
Id *string `bson:"id,omitempty" json:"id,omitempty"`
Extension []Extension `bson:"extension,omitempty" json:"extension,omitempty"`
Use *AddressUse `bson:"use,omitempty" json:"use,omitempty"`
Type *AddressType `bson:"type,omitempty" json:"type,omitempty"`
Text *string `bson:"text,omitempty" json:"text,omitempty"`
Line []string `bson:"line,omitempty" json:"line,omitempty"`
City *string `bson:"city,omitempty" json:"city,omitempty"`
District *string `bson:"district,omitempty" json:"district,omitempty"`
State *string `bson:"state,omitempty" json:"state,omitempty"`
PostalCode *string `bson:"postalCode,omitempty" json:"postalCode,omitempty"`
Country *string `bson:"country,omitempty" json:"country,omitempty"`
Period *Period `bson:"period,omitempty" json:"period,omitempty"`
}
87 changes: 87 additions & 0 deletions fhir-models-gen/fhir/addressType.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright 2019 The Samply Development Community
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package fhir

import (
"encoding/json"
"fmt"
"strings"
)

// THIS FILE IS GENERATED BY https://github.com/lifebox-healthcare/golang-fhir-models
// PLEASE DO NOT EDIT BY HAND

// AddressType is documented here http://hl7.org/fhir/ValueSet/address-type
type AddressType int

const (
AddressTypePostal AddressType = iota
AddressTypePhysical
AddressTypeBoth
)

func (code AddressType) MarshalJSON() ([]byte, error) {
return json.Marshal(code.Code())
}
func (code *AddressType) UnmarshalJSON(json []byte) error {
s := strings.Trim(string(json), "\"")
switch s {
case "postal":
*code = AddressTypePostal
case "physical":
*code = AddressTypePhysical
case "both":
*code = AddressTypeBoth
default:
return fmt.Errorf("unknown AddressType code `%s`", s)
}
return nil
}
func (code AddressType) String() string {
return code.Code()
}
func (code AddressType) Code() string {
switch code {
case AddressTypePostal:
return "postal"
case AddressTypePhysical:
return "physical"
case AddressTypeBoth:
return "both"
}
return "<unknown>"
}
func (code AddressType) Display() string {
switch code {
case AddressTypePostal:
return "Postal"
case AddressTypePhysical:
return "Physical"
case AddressTypeBoth:
return "Postal & Physical"
}
return "<unknown>"
}
func (code AddressType) Definition() string {
switch code {
case AddressTypePostal:
return "Mailing addresses - PO Boxes and care-of addresses."
case AddressTypePhysical:
return "A physical address that can be visited."
case AddressTypeBoth:
return "An address that is both physical and postal."
}
return "<unknown>"
}
Loading

0 comments on commit 89a028a

Please sign in to comment.