Suppose I have the following schema:
$ion_schema_2_0
type::{
name: Foo,
fields: {
a: { fields: { x: int, y: int } },
b: int,
}
}
type::{
name: Bar,
type: list,
element: { fields: { u: float, v: float } }
}
As it is currently implemented, the code generator might generate something like this. (I'm using Kotlin, rather than Java, for conciseness here, but the principle will still apply.)
class Foo(val a: AnonymousType1, val b: Int)
class AnonymousType1(val x: Int, val y: Int)
class Bar(val value: List<AnonymousType2>)
class AnonymousType2(val u: Double, val v: Double)
Now, suppose I add a new field to Foo, like this:
type::{
name: Foo,
fields: {
a: { fields: { x: int, y: int } },
b: int,
c: { fields: { id: string, value: int },
}
}
The code generator has no way of knowing how to keep the generated code stable, and would likely end up generating these classes:
class Foo(val a: AnonymousType1, val b: Int, val c: AnonymousType2)
class AnonymousType1(val x: Int, val y: Int)
class AnonymousType2(val id: String, val value: Int)
class Bar(val value: List<AnonymousType3>)
class AnonymousType3(val u: Double, val v: Double)
In this scenario, AnonymousType2 and Bar have been redefined in a way that will break the code of anyone who is using either one of these classes in any non-trivial way. This cannot be solved by sorting the anonymous types because the only sort attribute that would not result in types being moved/redefined would be the timestamp that the inline type was added to the schema for the first time.
There are a few things that I think we must do in order to fix this problem, but I'm certainly open to hearing other solutions.
- All inline types must be placed inside a namespace corresponding to the parent type. In Java, that would mean e.g.
org.example.AnonymousType1 would instead be org.example.Foo.AnonymousType1. In Rust, it's a little trickier because you could end up with naming clashes where you create a module called foo to hold children of Foo when there's already another module you're generating called foo, but this is solvable.
- Inline types, if they have a
$code_gen_name field, should use that name; otherwise...
- Inline types in a
fields constraint should be named after their respective fields. I.e.AnonymousType1 would be named Foo.A in Java/Kotlin.
- Inline types in an
element constraint should be named Element. I.e. AnonymousType2 would be named Bar.Element in Java/Kotlin.
- Inline types in
type or all_of should have their constraints flattened into the containing type.
- Inline types in
one_of constraint have two possible solutions
- They should either be required to have a code-gen name (so that we can add a variant-specific annotation) OR
- They should be named
Variant0..VariantN, and every one_of constraint must have its own distinct counter (i.e. not a globally incremented counter). E.g. Car.Variant0, Car.Variant1, etc.
- Inline types in
ordered_elements should be named Element0..ElementN, and every ordered_elements constraint must have its own distinct counter.
I might be missing a case somewhere, but I think that mostly covers them all.
Suppose I have the following schema:
As it is currently implemented, the code generator might generate something like this. (I'm using Kotlin, rather than Java, for conciseness here, but the principle will still apply.)
Now, suppose I add a new field to
Foo, like this:The code generator has no way of knowing how to keep the generated code stable, and would likely end up generating these classes:
In this scenario,
AnonymousType2andBarhave been redefined in a way that will break the code of anyone who is using either one of these classes in any non-trivial way. This cannot be solved by sorting the anonymous types because the only sort attribute that would not result in types being moved/redefined would be the timestamp that the inline type was added to the schema for the first time.There are a few things that I think we must do in order to fix this problem, but I'm certainly open to hearing other solutions.
org.example.AnonymousType1would instead beorg.example.Foo.AnonymousType1. In Rust, it's a little trickier because you could end up with naming clashes where you create a module calledfooto hold children ofFoowhen there's already another module you're generating calledfoo, but this is solvable.$code_gen_namefield, should use that name; otherwise...fieldsconstraint should be named after their respective fields. I.e.AnonymousType1would be namedFoo.Ain Java/Kotlin.elementconstraint should be namedElement. I.e.AnonymousType2would be namedBar.Elementin Java/Kotlin.typeorall_ofshould have their constraints flattened into the containing type.one_ofconstraint have two possible solutionsVariant0..VariantN, and everyone_ofconstraint must have its own distinct counter (i.e. not a globally incremented counter). E.g.Car.Variant0,Car.Variant1, etc.ordered_elementsshould be namedElement0..ElementN, and everyordered_elementsconstraint must have its own distinct counter.I might be missing a case somewhere, but I think that mostly covers them all.