Skip to content

Commit

Permalink
Add new non-macro-based update and set methods (#24)
Browse files Browse the repository at this point in the history
* experiment

* sugar seems to work

* improvements

* bump bunch

* fix type ascencion in update method ; ignore test

* tidy naming

* we don't need macros for the extension/id methods now

* rm old primitive element macros
  • Loading branch information
hughsimpson authored Jun 14, 2021
1 parent 18c461f commit 67c076a
Show file tree
Hide file tree
Showing 665 changed files with 5,271 additions and 151 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ object RegressionBenchmarks6 extends RegressionBenchmark {
object RegressionBenchmarks7 extends RegressionBenchmark {
performance of "lit_resource" in {
measure method "update" in {
using(singleTest) in (_ => sampleResource.updateType(_ => BUNDLE_TYPE.SEARCHSET))
using(singleTest) in (_ => sampleResource.update(_.`type`)(_ => BUNDLE_TYPE.SEARCHSET))
}
}

Expand Down
35 changes: 33 additions & 2 deletions core/src/main/scala/com/babylonhealth/lit/core/CompanionFor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import scala.util.{ Failure, Success, Try }
import io.circe.{ Decoder, DecodingFailure, HCursor }
import izumi.reflect.macrortti.LTag
import org.slf4j.{ Logger, LoggerFactory }

import com.babylonhealth.lit.core.model.{ Element, Resource, resourceTypeLookup, urlLookup }
import com.babylonhealth.lit.core.model.{ Element, Extension, Resource, resourceTypeLookup, urlLookup }

trait OptionSugar {
implicit class RichT[T](val t: T) {
Expand All @@ -22,14 +21,44 @@ trait OptionSugar {
}
}

case class ObjectAndCompanion[O <: FHIRObject: LTag: ClassTag, C <: CompanionFor[_]](o: O, c: C) {
def update[T](fieldSelection: C => FHIRComponentFieldMeta[T])(fn: T => T): O =
o.updateFromField[T, O](fieldSelection(c))(fn)
def set[T](fieldSelection: C => FHIRComponentFieldMeta[T])(value: T): O =
o.setFromField[T, O](fieldSelection(c))(value)
def updateIfExists[T](fieldSelection: C => FHIRComponentFieldMeta[Option[T]])(fn: T => T): O =
o.updateFromField[Option[T], O](fieldSelection(c))(_ map fn)
def updateAll[T](fieldSelection: C => FHIRComponentFieldMeta[LitSeq[T]])(fn: T => T): O =
o.updateFromField[LitSeq[T], O](fieldSelection(c))(_ map fn)
def updateExtensions(field: C => FHIRComponentFieldMeta[_])(update: LitSeq[Extension] => LitSeq[Extension]): O =
o.extensions.update(field(c))(update)
def updateIds(field: C => FHIRComponentFieldMeta[_])(update: Option[String] => Option[String]): O =
o.ids.update(field(c))(update)
def setExtensions(field: C => FHIRComponentFieldMeta[_])(extension: LitSeq[Extension]): O =
o.extensions.set(field(c))(extension)
def setIds(field: C => FHIRComponentFieldMeta[_])(id: Option[String]): O = o.ids.set(field(c))(id)
def getExtensions(field: C => FHIRComponentFieldMeta[_]): LitSeq[Extension] = o.extensions.get(field(c))
def getIds(field: C => FHIRComponentFieldMeta[_]): Option[String] = o.ids.get(field(c))
}

abstract class CompanionFor[-T <: FHIRObject: LTag](implicit val thisClassTag: ClassTag[T @uncheckedVariance])
extends JsonDecoderHelpers
with OptionSugar {
type ResourceType >: T <: FHIRObject
type ParentType >: T <: FHIRObject
private val log: Logger = LoggerFactory.getLogger(getClass)
val thisName: String
val profileUrl: Option[String] = None

final private[core] def leastParentWithField(
f: FHIRComponentFieldMeta[_],
chain: List[CompanionFor[_]] = Nil
): CompanionFor[_ <: ResourceType] = {
if (fieldsMeta.contains(f)) this.asInstanceOf[CompanionFor[_ <: ResourceType]]
else if (parentType eq this)
throw new RuntimeException(s"Unable to find field matching $f in ${chain.map(_.thisName).mkString(" <: ")}")
else parentType.leastParentWithField(f, chain :+ this)
}
// private val m = runtimeMirror(getClass.getClassLoader)
// lazy val classConstructor: Constructor[_] =
// thisClassTag.runtimeClass.getDeclaredConstructor(fieldsMeta.map(f => m.runtimeClass(f.tt.tag.typeSymbol.asClass)): _*)
Expand Down Expand Up @@ -81,6 +110,8 @@ abstract class CompanionFor[-T <: FHIRObject: LTag](implicit val thisClassTag: C

def fields(t: T): Seq[FHIRComponentField[_]]

val parentType: CompanionFor[ParentType]

val baseType: CompanionFor[T]

protected lazy val (refMetas, otherMetas) = fieldsMeta.partition(_.isRef) match {
Expand Down
33 changes: 30 additions & 3 deletions core/src/main/scala/com/babylonhealth/lit/core/FHIRObject.scala
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package com.babylonhealth.lit.core

import scala.collection.immutable.TreeMap
import scala.reflect.ClassTag
import scala.reflect.{ ClassTag, classTag }
import scala.reflect.runtime.universe.{ RuntimeClass, typeOf }

import cats.Monad
import com.babylonhealth.lit.core
import izumi.reflect.macrortti.LTag

import com.babylonhealth.lit.core.TagSummoners.lTypeOf
import com.babylonhealth.lit.core.model.{ Element, Extension, intSubSuffixes, stringSubSuffixes, typeSuffixMap }

Expand Down Expand Up @@ -34,6 +34,33 @@ abstract class FHIRObject(
def updatePrimitiveAttributes(fn: FieldToElementLookup => FieldToElementLookup): this.type =
withPrimitiveAttributes(fn(primitiveAttributes))

// This form is not typesafe (one can call it on any resource with any meta field), but it should either
// always or never throw on a given resourceType at a particular call-site.
// Prefer update/set/updateIfExists/updateAll from the implicitly-summoned `ObjectAndCompanion`
def updateFromField[T, UpType >: this.type <: FHIRObject: ClassTag: LTag](
field: FHIRComponentFieldMeta[T]
)(fn: T => T): UpType = {
val parent: CompanionFor[_ <: companion.ResourceType] = companion.leastParentWithField(field)
val currentValue = parent
.fieldsFromParent(this.asInstanceOf[parent.ResourceType])
.get // fieldsFromParent shouldn't be able to fail if upcasting...
.find(_.meta == field)
.get // leastParentWithField call should fail if this isn't found; this indicates an error in the value provided in `field` param
.value
.asInstanceOf[T]
val newVal = fn(currentValue)
withFieldUnsafe[T, companion.ResourceType](field.name, newVal)(
parent.thisClassTag.asInstanceOf[ClassTag[companion.ResourceType]],
parent.thisTypeTag.asInstanceOf[LTag[companion.ResourceType]]).asInstanceOf[UpType]
}
def setFromField[T, UpType >: this.type <: FHIRObject: ClassTag: LTag](
field: FHIRComponentFieldMeta[T]
)(newVal: T): UpType = {
val parent: CompanionFor[_ <: companion.ResourceType] = companion.leastParentWithField(field)
withFieldUnsafe[T, companion.ResourceType](field.name, newVal)(
parent.thisClassTag.asInstanceOf[ClassTag[companion.ResourceType]],
parent.thisTypeTag.asInstanceOf[LTag[companion.ResourceType]]).asInstanceOf[UpType]
}
val extensions = new {
def update(field: FHIRComponentFieldMeta[_])(
update: LitSeq[Extension] => LitSeq[Extension]): FHIRObject.this.type = {
Expand Down Expand Up @@ -70,7 +97,7 @@ abstract class FHIRObject(

def fields: Seq[FHIRComponentField[_]] = companion.fields(this)

def companion: CompanionFor[this.type]
val companion: CompanionFor[this.type]

def thisClassName: String // For HL7 classes, this should be the same as `thisTypeName`, but will differ for domain-specific subclasses
def thisTypeName: String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@ import com.babylonhealth.lit.{ core }
import com.babylonhealth.lit.macros.POJOBoilerplate

object Address extends CompanionFor[Address] {
implicit def summonObjectAndCompanionAddress2002168585(o: Address): ObjectAndCompanion[Address, Address.type] =
ObjectAndCompanion(o, this)
override type ResourceType = Address
override type ParentType = Address
override val baseType: CompanionFor[ResourceType] = Address
override val parentType: CompanionFor[ParentType] = Address
override val profileUrl: Option[String] = Some("http://hl7.org/fhir/StructureDefinition/Address")
def apply(
id: Option[String] = None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@ import com.babylonhealth.lit.{ core }
import com.babylonhealth.lit.macros.POJOBoilerplate

object Age extends CompanionFor[Age] {
implicit def summonObjectAndCompanionAge_1092090844(o: Age): ObjectAndCompanion[Age, Age.type] =
ObjectAndCompanion(o, this)
override type ResourceType = Age
override type ParentType = Age
override val baseType: CompanionFor[ResourceType] = Age
override val parentType: CompanionFor[ParentType] = Age
override val profileUrl: Option[String] = Some("http://hl7.org/fhir/StructureDefinition/Age")
def apply(
id: Option[String] = None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@ import com.babylonhealth.lit.{ core }
import com.babylonhealth.lit.macros.POJOBoilerplate

object Annotation extends CompanionFor[Annotation] {
implicit def summonObjectAndCompanionAnnotation_1299111937(
o: Annotation): ObjectAndCompanion[Annotation, Annotation.type] = ObjectAndCompanion(o, this)
override type ResourceType = Annotation
override type ParentType = Annotation
override val baseType: CompanionFor[ResourceType] = Annotation
override val parentType: CompanionFor[ParentType] = Annotation
override val profileUrl: Option[String] = Some("http://hl7.org/fhir/StructureDefinition/Annotation")
type AuthorChoice = Choice[Union_1128709984]
def apply(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@ import com.babylonhealth.lit.{ core }
import com.babylonhealth.lit.macros.POJOBoilerplate

object Attachment extends CompanionFor[Attachment] {
implicit def summonObjectAndCompanionAttachment1501498888(
o: Attachment): ObjectAndCompanion[Attachment, Attachment.type] = ObjectAndCompanion(o, this)
override type ResourceType = Attachment
override type ParentType = Attachment
override val baseType: CompanionFor[ResourceType] = Attachment
override val parentType: CompanionFor[ParentType] = Attachment
override val profileUrl: Option[String] = Some("http://hl7.org/fhir/StructureDefinition/Attachment")
def apply(
id: Option[String] = None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@ import com.babylonhealth.lit.{ core }
import com.babylonhealth.lit.macros.POJOBoilerplate

object BackboneElement extends CompanionFor[BackboneElement] {
implicit def summonObjectAndCompanionBackboneElement_17315358(
o: BackboneElement): ObjectAndCompanion[BackboneElement, BackboneElement.type] = ObjectAndCompanion(o, this)
override type ResourceType = BackboneElement
override type ParentType = BackboneElement
override val baseType: CompanionFor[ResourceType] = BackboneElement
override val parentType: CompanionFor[ParentType] = BackboneElement
override val profileUrl: Option[String] = Some("http://hl7.org/fhir/StructureDefinition/BackboneElement")
def apply(
id: Option[String] = None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@ import com.babylonhealth.lit.{ core }
import com.babylonhealth.lit.macros.POJOBoilerplate

object CodeableConcept extends CompanionFor[CodeableConcept] {
implicit def summonObjectAndCompanionCodeableConcept1231013185(
o: CodeableConcept): ObjectAndCompanion[CodeableConcept, CodeableConcept.type] = ObjectAndCompanion(o, this)
override type ResourceType = CodeableConcept
override type ParentType = CodeableConcept
override val baseType: CompanionFor[ResourceType] = CodeableConcept
override val parentType: CompanionFor[ParentType] = CodeableConcept
override val profileUrl: Option[String] = Some("http://hl7.org/fhir/StructureDefinition/CodeableConcept")
def apply(
id: Option[String] = None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@ import com.babylonhealth.lit.{ core }
import com.babylonhealth.lit.macros.POJOBoilerplate

object Coding extends CompanionFor[Coding] {
implicit def summonObjectAndCompanionCoding_462683970(o: Coding): ObjectAndCompanion[Coding, Coding.type] =
ObjectAndCompanion(o, this)
override type ResourceType = Coding
override type ParentType = Coding
override val baseType: CompanionFor[ResourceType] = Coding
override val parentType: CompanionFor[ParentType] = Coding
override val profileUrl: Option[String] = Some("http://hl7.org/fhir/StructureDefinition/Coding")
def apply(
id: Option[String] = None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@ import com.babylonhealth.lit.{ core }
import com.babylonhealth.lit.macros.POJOBoilerplate

object ContactDetail extends CompanionFor[ContactDetail] {
implicit def summonObjectAndCompanionContactDetail_1828331805(
o: ContactDetail): ObjectAndCompanion[ContactDetail, ContactDetail.type] = ObjectAndCompanion(o, this)
override type ResourceType = ContactDetail
override type ParentType = ContactDetail
override val baseType: CompanionFor[ResourceType] = ContactDetail
override val parentType: CompanionFor[ParentType] = ContactDetail
override val profileUrl: Option[String] = Some("http://hl7.org/fhir/StructureDefinition/ContactDetail")
def apply(
id: Option[String] = None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@ import com.babylonhealth.lit.{ core }
import com.babylonhealth.lit.macros.POJOBoilerplate

object ContactPoint extends CompanionFor[ContactPoint] {
implicit def summonObjectAndCompanionContactPoint_1318533811(
o: ContactPoint): ObjectAndCompanion[ContactPoint, ContactPoint.type] = ObjectAndCompanion(o, this)
override type ResourceType = ContactPoint
override type ParentType = ContactPoint
override val baseType: CompanionFor[ResourceType] = ContactPoint
override val parentType: CompanionFor[ParentType] = ContactPoint
override val profileUrl: Option[String] = Some("http://hl7.org/fhir/StructureDefinition/ContactPoint")
def apply(
id: Option[String] = None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@ import com.babylonhealth.lit.{ core }
import com.babylonhealth.lit.macros.POJOBoilerplate

object Contributor extends CompanionFor[Contributor] {
implicit def summonObjectAndCompanionContributor_1606151921(
o: Contributor): ObjectAndCompanion[Contributor, Contributor.type] = ObjectAndCompanion(o, this)
override type ResourceType = Contributor
override type ParentType = Contributor
override val baseType: CompanionFor[ResourceType] = Contributor
override val parentType: CompanionFor[ParentType] = Contributor
override val profileUrl: Option[String] = Some("http://hl7.org/fhir/StructureDefinition/Contributor")
def apply(
id: Option[String] = None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@ import com.babylonhealth.lit.{ core }
import com.babylonhealth.lit.macros.POJOBoilerplate

object Count extends CompanionFor[Count] {
implicit def summonObjectAndCompanionCount195454229(o: Count): ObjectAndCompanion[Count, Count.type] =
ObjectAndCompanion(o, this)
override type ResourceType = Count
override type ParentType = Count
override val baseType: CompanionFor[ResourceType] = Count
override val parentType: CompanionFor[ParentType] = Count
override val profileUrl: Option[String] = Some("http://hl7.org/fhir/StructureDefinition/Count")
def apply(
id: Option[String] = None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,20 @@ import com.babylonhealth.lit.{ core }
import com.babylonhealth.lit.macros.POJOBoilerplate

object DataRequirement extends CompanionFor[DataRequirement] {
implicit def summonObjectAndCompanionDataRequirement1813028335(
o: DataRequirement): ObjectAndCompanion[DataRequirement, DataRequirement.type] = ObjectAndCompanion(o, this)
override type ResourceType = DataRequirement
override type ParentType = DataRequirement
override val baseType: CompanionFor[ResourceType] = DataRequirement
override val parentType: CompanionFor[ParentType] = DataRequirement
override val profileUrl: Option[String] = Some("http://hl7.org/fhir/StructureDefinition/DataRequirement")
object DateFilter extends CompanionFor[DateFilter] {
implicit def summonObjectAndCompanionDateFilter167096238(
o: DateFilter): ObjectAndCompanion[DateFilter, DateFilter.type] = ObjectAndCompanion(o, this)
override type ResourceType = DateFilter
type ValueChoice = Choice[Union_1947777294]
override type ParentType = DateFilter
override val parentType: CompanionFor[ResourceType] = DateFilter
type ValueChoice = Choice[Union_1947777294]
def apply(
id: Option[String] = None,
path: Option[String] = None,
Expand Down Expand Up @@ -89,7 +97,11 @@ object DataRequirement extends CompanionFor[DataRequirement] {
override val primitiveAttributes: TreeMap[FHIRComponentFieldMeta[_], PrimitiveElementInfo] = FHIRObject.emptyAtts)
extends Element(id = id, extension = extension)
object CodeFilter extends CompanionFor[CodeFilter] {
implicit def summonObjectAndCompanionCodeFilter540025153(
o: CodeFilter): ObjectAndCompanion[CodeFilter, CodeFilter.type] = ObjectAndCompanion(o, this)
override type ResourceType = CodeFilter
override type ParentType = CodeFilter
override val parentType: CompanionFor[ResourceType] = CodeFilter
def apply(
id: Option[String] = None,
path: Option[String] = None,
Expand Down Expand Up @@ -159,7 +171,11 @@ object DataRequirement extends CompanionFor[DataRequirement] {
override val primitiveAttributes: TreeMap[FHIRComponentFieldMeta[_], PrimitiveElementInfo] = FHIRObject.emptyAtts)
extends Element(id = id, extension = extension)
object Sort extends CompanionFor[Sort] {
implicit def summonObjectAndCompanionSort725645701(o: Sort): ObjectAndCompanion[Sort, Sort.type] =
ObjectAndCompanion(o, this)
override type ResourceType = Sort
override type ParentType = Sort
override val parentType: CompanionFor[ResourceType] = Sort
def apply(
id: Option[String] = None,
path: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@ import com.babylonhealth.lit.{ core }
import com.babylonhealth.lit.macros.POJOBoilerplate

object Distance extends CompanionFor[Distance] {
implicit def summonObjectAndCompanionDistance952422758(o: Distance): ObjectAndCompanion[Distance, Distance.type] =
ObjectAndCompanion(o, this)
override type ResourceType = Distance
override type ParentType = Distance
override val baseType: CompanionFor[ResourceType] = Distance
override val parentType: CompanionFor[ParentType] = Distance
override val profileUrl: Option[String] = Some("http://hl7.org/fhir/StructureDefinition/Distance")
def apply(
id: Option[String] = None,
Expand Down
12 changes: 10 additions & 2 deletions core/src/main/scala/com/babylonhealth/lit/core/model/Dosage.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,21 @@ import com.babylonhealth.lit.{ core }
import com.babylonhealth.lit.macros.POJOBoilerplate

object Dosage extends CompanionFor[Dosage] {
implicit def summonObjectAndCompanionDosage_588073389(o: Dosage): ObjectAndCompanion[Dosage, Dosage.type] =
ObjectAndCompanion(o, this)
override type ResourceType = Dosage
override type ParentType = Dosage
override val baseType: CompanionFor[ResourceType] = Dosage
override val parentType: CompanionFor[ParentType] = Dosage
override val profileUrl: Option[String] = Some("http://hl7.org/fhir/StructureDefinition/Dosage")
object DoseAndRate extends CompanionFor[DoseAndRate] {
implicit def summonObjectAndCompanionDoseAndRate_228242847(
o: DoseAndRate): ObjectAndCompanion[DoseAndRate, DoseAndRate.type] = ObjectAndCompanion(o, this)
override type ResourceType = DoseAndRate
type DoseChoice = Choice[Union01639511888]
type RateChoice = Choice[Union_0575082635]
override type ParentType = DoseAndRate
override val parentType: CompanionFor[ResourceType] = DoseAndRate
type DoseChoice = Choice[Union01639511888]
type RateChoice = Choice[Union_0575082635]
def apply(
id: Option[String] = None,
`type`: Option[CodeableConcept] = None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@ import com.babylonhealth.lit.{ core }
import com.babylonhealth.lit.macros.POJOBoilerplate

object Duration extends CompanionFor[Duration] {
implicit def summonObjectAndCompanionDuration1993583550(o: Duration): ObjectAndCompanion[Duration, Duration.type] =
ObjectAndCompanion(o, this)
override type ResourceType = Duration
override type ParentType = Duration
override val baseType: CompanionFor[ResourceType] = Duration
override val parentType: CompanionFor[ParentType] = Duration
override val profileUrl: Option[String] = Some("http://hl7.org/fhir/StructureDefinition/Duration")
def apply(
id: Option[String] = None,
Expand Down
Loading

0 comments on commit 67c076a

Please sign in to comment.