Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Experiment] Generate Rust models #174

Draft
wants to merge 29 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,18 @@ build-all-class-models:
sbt $(foreach i,$(ALL_MODULES),+$i/scalafmtAll)
sbt scalafmtAll

build-hl7-typescript-models:
sbt 'project generator' 'run "generate" \
--javaPackageSuffix=_java \
--excludeJVM \
--typescriptDir="./generated_typescript"'

build-hl7-rust-models:
sbt 'project generator' 'run "generate" \
--javaPackageSuffix=_java \
--excludeJVM \
--rustDir="./rust"'

clean-target:
rm -rf target/ */target

Expand Down Expand Up @@ -153,3 +165,6 @@ find-weird-ones:

gen-uk-snapshots:
docker run --rm -v $${PWD}/fhir/fhir.r4.ukcore.stu1:/gen -v $${PWD}/fhir/hl7.fhir.r4.core/:/hl7 $(HYDRA_DOCKER) snapshot -s /gen -h hl7

test-rust:
cd rust && cargo test -- --nocapture
73 changes: 48 additions & 25 deletions generator/src/main/scala/com/babylonhealth/lit/Autogenerator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import com.babylonhealth.lit.ElementTreee._
import com.babylonhealth.lit.core.serdes.objectDecoder
import com.babylonhealth.lit.hl7.BINDING_STRENGTH
import com.babylonhealth.lit.hl7.model.{ ElementDefinition, StructureDefinition }
import com.babylonhealth.lit.languages.{ JavaGenerator, ScalaCodegen, TypescriptCodegen }
import com.babylonhealth.lit.languages.{ JavaGenerator, Rust, ScalaCodegen, TypescriptCodegen }

trait FHIRFetcher {}

Expand Down Expand Up @@ -264,48 +264,71 @@ object Autogenerator extends Commonish with Logging with FileUtils with JavaGene
}
}

AllGeneratedFiles(scalaClassGenInfo, javaClassGenInfo, typescriptClassGenInfo)
val rustClassGenInfo: Option[Seq[ClassGenInfo]] = args.rustDir.map { o =>
def getDeclaringPkgForType: Map[String, String] = topLevelClasses.classes.map { case (name, ps) => name -> ps.keys.head }
topLevelClasses.classes.toSeq.flatMap { case (o, m) =>
m.flatMap { case (p, k) =>
try Rust.genRustForClass(k, getDeclaringPkgForType)
catch {
case NonFatal(ex) =>
log.error(s"Unable to gen Rust file for $p.$o", ex)
Nil
}
}.toSeq
} ++
Rust.genUnionFiles(args.moduleDependencies, ElementTreee.getUnionTypes) ++
getDeclaringPkgForType.groupMap(_._2)(_._1).flatMap((Rust.genModRS _).tupled)
}

AllGeneratedFiles(scalaClassGenInfo, javaClassGenInfo, typescriptClassGenInfo, rustClassGenInfo)
}
def generateAndWriteOutput(
args: MainArgs,
extensions: Map[String, Seq[ClassGenInfo]],
fetchValueSet: (String, BINDING_STRENGTH) => Option[CodeValueSet]): Unit = {
println("Trying to generate files")
val AllGeneratedFiles(scalaClassGenInfo, javaClassGenInfo, typescriptClassGenInfo) =
val AllGeneratedFiles(scalaClassGenInfo, javaClassGenInfo, typescriptClassGenInfo, rustClassGenInfo) =
getStrings(args, extensions, fetchValueSet)
def javaOutputLocation(pkg: String): Option[String] =
args.javaPackageSuffix.map(j => s"./$pkg$j/src/main/java/com/babylonhealth/lit/$pkg$j")
def javaOutputLocations(pkg: String): Seq[String] =
javaOutputLocation(pkg).toSeq.flatMap(dir => Seq(s"$dir/builders", s"$dir/codes", s"$dir/model"))
println("Successfully generated files")
if (!args.dryRun) { // create the directories fresh
scalaClassGenInfo
.map(_.pkg)
.distinct
.foreach { p =>
emptyCreate(s"$p/src/main/scala/com/babylonhealth/lit/$p/model")
emptyCreate(s"$p/src/main/scala-2/com/babylonhealth/lit/$p/model")
emptyCreate(s"$p/src/main/scala-3/com/babylonhealth/lit/$p/model")
javaOutputLocations(p).foreach(emptyCreate)
if (!args.excludeJVM) {
scalaClassGenInfo
.map(_.pkg)
.distinct
.foreach { p =>
emptyCreate(s"$p/src/main/scala/com/babylonhealth/lit/$p/model")
emptyCreate(s"$p/src/main/scala-2/com/babylonhealth/lit/$p/model")
emptyCreate(s"$p/src/main/scala-3/com/babylonhealth/lit/$p/model")
javaOutputLocations(p).foreach(emptyCreate)
}
scalaClassGenInfo foreach { case ClassGenInfo(fc, fileName, pkg, targetVersion) =>
val scalaDir = targetVersion
.map { case ScalaTarget.Scala2 => "scala-2"; case ScalaTarget.Scala3 => "scala-3" }
.getOrElse("scala")
write(s"$pkg/src/main/$scalaDir/com/babylonhealth/lit/$pkg/model/$fileName.scala", fc)
}
javaClassGenInfo.toSeq.flatMap(_.builders) foreach { case ClassGenInfo(fc, fileName, pkg, _) =>
write(s"${javaOutputLocation(pkg).get}/builders/$fileName.java", fc)
}
javaClassGenInfo.toSeq.flatMap(_.codes) foreach { case ClassGenInfo(fc, fileName, pkg, _) =>
write(s"${javaOutputLocation(pkg).get}/codes/$fileName.java", fc)
}
javaClassGenInfo.toSeq.flatMap(_.model) foreach { case ClassGenInfo(fc, fileName, pkg, _) =>
write(s"${javaOutputLocation(pkg).get}/model/$fileName.java", fc)
}
scalaClassGenInfo foreach { case ClassGenInfo(fc, fileName, pkg, targetVersion) =>
val scalaDir = targetVersion
.map { case ScalaTarget.Scala2 => "scala-2"; case ScalaTarget.Scala3 => "scala-3" }
.getOrElse("scala")
write(s"$pkg/src/main/$scalaDir/com/babylonhealth/lit/$pkg/model/$fileName.scala", fc)
}
javaClassGenInfo.toSeq.flatMap(_.builders) foreach { case ClassGenInfo(fc, fileName, pkg, _) =>
write(s"${javaOutputLocation(pkg).get}/builders/$fileName.java", fc)
}
javaClassGenInfo.toSeq.flatMap(_.codes) foreach { case ClassGenInfo(fc, fileName, pkg, _) =>
write(s"${javaOutputLocation(pkg).get}/codes/$fileName.java", fc)
}
javaClassGenInfo.toSeq.flatMap(_.model) foreach { case ClassGenInfo(fc, fileName, pkg, _) =>
write(s"${javaOutputLocation(pkg).get}/model/$fileName.java", fc)
}
if (typescriptClassGenInfo.nonEmpty) new File(args.typescriptDir.get).mkdirs()
typescriptClassGenInfo.foreach(c =>
write(s"${args.typescriptDir.get}/DomainModel.ts", c.map(_.fileContents).mkString("\n\n")))
if (rustClassGenInfo.nonEmpty)
rustClassGenInfo.toSeq
.flatMap(_.map(_.pkg).distinct)
.foreach(p => new File(s"${args.rustDir.get}/src/$p/model").mkdirs())
rustClassGenInfo.foreach(_.foreach(c => write(s"${args.rustDir.get}/src/${c.pkg}/model/${c.fileName}.rs", c.fileContents)))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ object ElementTreee extends Commonish with Logging {

private val unionTypes = TrieMap[String, (Seq[String], Seq[String])]()
def getUnionTypes: Map[String, (Seq[String], Seq[String])] = unionTypes.toMap
lazy val unionDeclaringPackages: Map[String, String] = unionTypes.view.mapValues { case (pkgs, _) => pkgs.head }.toMap

def lookupSuffixByType(t: String): String = inverseTypeLookup(t).capitalize
def getUnionAlias(pkg: String, s: Seq[String], field: BaseField): String = getUnionAlias(pkg, s, field.className, field.name)
Expand Down
7 changes: 6 additions & 1 deletion generator/src/main/scala/com/babylonhealth/lit/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,10 @@ case class MainArgs(
models: Seq[SourceFile] = Nil,
javaPackageSuffix: Option[String] = None,
typescriptDir: Option[String] = None,
rustDir: Option[String] = None,
moduleDependencies: ModuleDependencies = ModuleDependencies(Nil),
dryRun: Boolean = false)
dryRun: Boolean = false,
excludeJVM: Boolean = false)

object ArgParser extends ArgParser
trait ArgParser {
Expand Down Expand Up @@ -143,9 +145,12 @@ trait ArgParser {
case argRegex("models", s) => args.copy(models = modelsFromStringUnsafe(s))
case argRegex("javaPackageSuffix", s) => args.copy(javaPackageSuffix = Some(s))
case argRegex("typescriptDir", s) => args.copy(typescriptDir = Some(s))
case argRegex("rustDir", s) => args.copy(rustDir = Some(s))
case argRegex("moduleDependencies", s) => args.copy(moduleDependencies = moduleDependenciesFromString(s))
case argRegex("dryRun", b) => args.copy(dryRun = b.toBoolean)
case noArgRegex("dryRun") => args.copy(dryRun = true)
case argRegex("excludeJVM", b) => args.copy(excludeJVM = b.toBoolean)
case noArgRegex("excludeJVM") => args.copy(excludeJVM = true)
case x => println(s"Could not parse arg $x"); sys.exit(1)
}
}
Expand Down
3 changes: 2 additions & 1 deletion generator/src/main/scala/com/babylonhealth/lit/Models.scala
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,8 @@ case class JavaClassGenInfo(builders: Seq[ClassGenInfo], codes: Seq[ClassGenInfo
case class AllGeneratedFiles(
scalaClassGenInfo: Seq[ClassGenInfo],
javaClassGenInfo: Option[JavaClassGenInfo],
typescriptClassGenInfo: Option[Seq[ClassGenInfo]])
typescriptClassGenInfo: Option[Seq[ClassGenInfo]],
rustClassGenInfo: Option[Seq[ClassGenInfo]])

case class ElementWithSlices(
el: ElementDefinition,
Expand Down
Loading